1 /* $OpenBSD: radiusctl.c,v 1.7 2019/04/01 09:51:56 yasuoka Exp $ */ 2 /* 3 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <netinet/in.h> 20 21 #include <arpa/inet.h> 22 #include <err.h> 23 #include <md5.h> 24 #include <netdb.h> 25 #include <stdbool.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include <radius.h> 32 33 #include "parser.h" 34 #include "chap_ms.h" 35 36 37 static void radius_test (struct parse_result *); 38 static void radius_dump (FILE *, RADIUS_PACKET *, bool, 39 const char *); 40 static const char *radius_code_str (int code); 41 static const char *hexstr(const u_char *, int, char *, int); 42 43 static void 44 usage(void) 45 { 46 extern char *__progname; 47 48 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 49 } 50 51 int 52 main(int argc, char *argv[]) 53 { 54 int ch; 55 struct parse_result *result; 56 57 while ((ch = getopt(argc, argv, "")) != -1) 58 switch (ch) { 59 default: 60 usage(); 61 return (EXIT_FAILURE); 62 } 63 argc -= optind; 64 argv += optind; 65 66 if ((result = parse(argc, argv)) == NULL) 67 return (EXIT_FAILURE); 68 69 switch (result->action) { 70 case NONE: 71 break; 72 case TEST: 73 if (pledge("stdio dns inet", NULL) == -1) 74 err(EXIT_FAILURE, "pledge"); 75 radius_test(result); 76 break; 77 } 78 79 return (EXIT_SUCCESS); 80 } 81 82 static void 83 radius_test(struct parse_result *res) 84 { 85 struct addrinfo hints, *ai; 86 int sock, retval; 87 struct sockaddr_storage sockaddr; 88 socklen_t sockaddrlen; 89 RADIUS_PACKET *reqpkt, *respkt; 90 struct sockaddr_in *sin4; 91 struct sockaddr_in6 *sin6; 92 uint32_t u32val; 93 uint8_t id; 94 95 reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST); 96 if (reqpkt == NULL) 97 err(1, "radius_new_request_packet"); 98 id = arc4random(); 99 radius_set_id(reqpkt, id); 100 101 memset(&hints, 0, sizeof(hints)); 102 hints.ai_family = PF_UNSPEC; 103 hints.ai_socktype = SOCK_DGRAM; 104 105 retval = getaddrinfo(res->hostname, "radius", &hints, &ai); 106 if (retval) 107 errx(1, "%s %s", res->hostname, gai_strerror(retval)); 108 109 if (res->port != 0) 110 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 111 htons(res->port); 112 113 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 114 if (sock == -1) 115 err(1, "socket"); 116 117 /* Prepare NAS-IP{,V6}-ADDRESS attribute */ 118 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) 119 err(1, "connect"); 120 sockaddrlen = sizeof(sockaddr); 121 if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1) 122 err(1, "getsockname"); 123 sin4 = (struct sockaddr_in *)&sockaddr; 124 sin6 = (struct sockaddr_in6 *)&sockaddr; 125 switch (sockaddr.ss_family) { 126 case AF_INET: 127 radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS, 128 sin4->sin_addr); 129 break; 130 case AF_INET6: 131 radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, 132 sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)); 133 break; 134 } 135 136 /* User-Name and User-Password */ 137 radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME, 138 res->username); 139 140 switch (res->auth_method) { 141 case PAP: 142 if (res->password != NULL) 143 radius_put_user_password_attr(reqpkt, res->password, 144 res->secret); 145 break; 146 case CHAP: 147 { 148 u_char chal[16]; 149 u_char resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */ 150 MD5_CTX md5ctx; 151 152 arc4random_buf(resp, 1); /* CHAP Id is random */ 153 MD5Init(&md5ctx); 154 MD5Update(&md5ctx, resp, 1); 155 if (res->password != NULL) 156 MD5Update(&md5ctx, res->password, 157 strlen(res->password)); 158 MD5Update(&md5ctx, chal, sizeof(chal)); 159 MD5Final(resp + 1, &md5ctx); 160 radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE, 161 chal, sizeof(chal)); 162 radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD, 163 resp, sizeof(resp)); 164 } 165 break; 166 case MSCHAPV2: 167 { 168 u_char pass[256], chal[16]; 169 u_int i, lpass; 170 struct _resp { 171 u_int8_t ident; 172 u_int8_t flags; 173 char peer_challenge[16]; 174 char reserved[8]; 175 char response[24]; 176 } __packed resp; 177 178 if (res->password == NULL) { 179 lpass = 0; 180 } else { 181 lpass = strlen(res->password); 182 if (lpass * 2 >= sizeof(pass)) 183 err(1, "password too long"); 184 for (i = 0; i < lpass; i++) { 185 pass[i * 2] = res->password[i]; 186 pass[i * 2 + 1] = 0; 187 } 188 } 189 190 memset(&resp, 0, sizeof(resp)); 191 resp.ident = arc4random(); 192 arc4random_buf(chal, sizeof(chal)); 193 arc4random_buf(resp.peer_challenge, 194 sizeof(resp.peer_challenge)); 195 196 mschap_nt_response(chal, resp.peer_challenge, 197 (char *)res->username, strlen(res->username), pass, 198 lpass * 2, resp.response); 199 200 radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 201 RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal)); 202 radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 203 RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp)); 204 explicit_bzero(pass, sizeof(pass)); 205 } 206 break; 207 208 } 209 u32val = htonl(res->nas_port); 210 radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4); 211 212 radius_put_message_authenticator(reqpkt, res->secret); 213 214 /* Send! */ 215 fprintf(stderr, "Sending:\n"); 216 radius_dump(stdout, reqpkt, false, res->secret); 217 if (send(sock, radius_get_data(reqpkt), radius_get_length(reqpkt), 0) 218 == -1) 219 warn("send"); 220 if ((respkt = radius_recv(sock, 0)) == NULL) 221 warn("recv"); 222 else { 223 radius_set_request_packet(respkt, reqpkt); 224 fprintf(stderr, "\nReceived:\n"); 225 radius_dump(stdout, respkt, true, res->secret); 226 } 227 228 /* Release the resources */ 229 radius_delete_packet(reqpkt); 230 if (respkt) 231 radius_delete_packet(respkt); 232 close(sock); 233 freeaddrinfo(ai); 234 235 explicit_bzero((char *)res->secret, strlen(res->secret)); 236 if (res->password) 237 explicit_bzero((char *)res->password, strlen(res->password)); 238 239 return; 240 } 241 242 static void 243 radius_dump(FILE *out, RADIUS_PACKET *pkt, bool resp, const char *secret) 244 { 245 size_t len; 246 char buf[256], buf1[256]; 247 uint32_t u32val; 248 struct in_addr ipv4; 249 250 fprintf(out, 251 " Id = %d\n" 252 " Code = %s(%d)\n", 253 (int)radius_get_id(pkt), radius_code_str((int)radius_get_code(pkt)), 254 (int)radius_get_code(pkt)); 255 if (resp && secret) { 256 fprintf(out, " Authenticator = %s\n", 257 (radius_check_response_authenticator(pkt, secret) == 0) 258 ? "Verified" : "NG"); 259 fprintf(out, " Message-Authenticator = %s\n", 260 (!radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 261 ? "(Not present)" 262 : (radius_check_message_authenticator(pkt, secret) == 0) 263 ? "Verified" : "NG"); 264 } 265 266 if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, buf, 267 sizeof(buf)) == 0) 268 fprintf(out, " User-Name = \"%s\"\n", buf); 269 270 if (secret && 271 radius_get_user_password_attr(pkt, buf, sizeof(buf), secret) == 0) 272 fprintf(out, " User-Password = \"%s\"\n", buf); 273 274 memset(buf, 0, sizeof(buf)); 275 len = sizeof(buf); 276 if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_PASSWORD, buf, &len) 277 == 0) 278 fprintf(out, " CHAP-Password = %s\n", 279 (hexstr(buf, len, buf1, sizeof(buf1))) 280 ? buf1 : "(too long)"); 281 282 memset(buf, 0, sizeof(buf)); 283 len = sizeof(buf); 284 if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_CHALLENGE, buf, &len) 285 == 0) 286 fprintf(out, " CHAP-Challenge = %s\n", 287 (hexstr(buf, len, buf1, sizeof(buf1))) 288 ? buf1 : "(too long)"); 289 290 memset(buf, 0, sizeof(buf)); 291 len = sizeof(buf); 292 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 293 RADIUS_VTYPE_MS_CHAP_CHALLENGE, buf, &len) == 0) 294 fprintf(out, " MS-CHAP-Challenge = %s\n", 295 (hexstr(buf, len, buf1, sizeof(buf1))) 296 ? buf1 : "(too long)"); 297 298 memset(buf, 0, sizeof(buf)); 299 len = sizeof(buf); 300 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 301 RADIUS_VTYPE_MS_CHAP2_RESPONSE, buf, &len) == 0) 302 fprintf(out, " MS-CHAP2-Response = %s\n", 303 (hexstr(buf, len, buf1, sizeof(buf1))) 304 ? buf1 : "(too long)"); 305 306 memset(buf, 0, sizeof(buf)); 307 len = sizeof(buf) - 1; 308 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 309 RADIUS_VTYPE_MS_CHAP2_SUCCESS, buf, &len) == 0) { 310 fprintf(out, " MS-CHAP-Success = Id=%u \"%s\"\n", 311 (u_int)(u_char)buf[0], buf + 1); 312 } 313 314 memset(buf, 0, sizeof(buf)); 315 len = sizeof(buf) - 1; 316 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 317 RADIUS_VTYPE_MS_CHAP_ERROR, buf, &len) == 0) { 318 fprintf(out, " MS-CHAP-Error = Id=%u \"%s\"\n", 319 (u_int)(u_char)buf[0], buf + 1); 320 } 321 322 memset(buf, 0, sizeof(buf)); 323 len = sizeof(buf); 324 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 325 RADIUS_VTYPE_MPPE_SEND_KEY, buf, &len) == 0) 326 fprintf(out, " MS-MPPE-Send-Key = %s\n", 327 (hexstr(buf, len, buf1, sizeof(buf1))) 328 ? buf1 : "(too long)"); 329 330 memset(buf, 0, sizeof(buf)); 331 len = sizeof(buf); 332 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 333 RADIUS_VTYPE_MPPE_RECV_KEY, buf, &len) == 0) 334 fprintf(out, " MS-MPPE-Recv-Key = %s\n", 335 (hexstr(buf, len, buf1, sizeof(buf1))) 336 ? buf1 : "(too long)"); 337 338 memset(buf, 0, sizeof(buf)); 339 len = sizeof(buf); 340 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 341 RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY, buf, &len) == 0) 342 fprintf(out, " MS-MPPE-Encryption-Policy = 0x%08x\n", 343 ntohl(*(u_long *)buf)); 344 345 346 memset(buf, 0, sizeof(buf)); 347 len = sizeof(buf); 348 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 349 RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES, buf, &len) == 0) 350 fprintf(out, " MS-MPPE-Encryption-Types = 0x%08x\n", 351 ntohl(*(u_long *)buf)); 352 353 if (radius_get_string_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, buf, 354 sizeof(buf)) == 0) 355 fprintf(out, " Reply-Message = \"%s\"\n", buf); 356 357 memset(buf, 0, sizeof(buf)); 358 len = sizeof(buf); 359 if (radius_get_uint32_attr(pkt, RADIUS_TYPE_NAS_PORT, &u32val) == 0) 360 fprintf(out, " NAS-Port = %lu\n", 361 (u_long)u32val); 362 363 memset(buf, 0, sizeof(buf)); 364 len = sizeof(buf); 365 if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, &ipv4) == 0) 366 fprintf(out, " NAS-IP-Address = %s\n", 367 inet_ntoa(ipv4)); 368 369 memset(buf, 0, sizeof(buf)); 370 len = sizeof(buf); 371 if (radius_get_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, buf, &len) 372 == 0) 373 fprintf(out, " NAS-IPv6-Address = %s\n", 374 inet_ntop(AF_INET6, buf, buf1, len)); 375 376 } 377 378 static const char * 379 radius_code_str(int code) 380 { 381 int i; 382 static struct _codestr { 383 int code; 384 const char *str; 385 } codestr[] = { 386 { RADIUS_CODE_ACCESS_REQUEST, "Access-Request" }, 387 { RADIUS_CODE_ACCESS_ACCEPT, "Access-Accept" }, 388 { RADIUS_CODE_ACCESS_REJECT, "Access-Reject" }, 389 { RADIUS_CODE_ACCOUNTING_REQUEST, "Accounting-Request" }, 390 { RADIUS_CODE_ACCOUNTING_RESPONSE, "Accounting-Response" }, 391 { RADIUS_CODE_ACCESS_CHALLENGE, "Access-Challenge" }, 392 { RADIUS_CODE_STATUS_SERVER, "Status-Server" }, 393 { RADIUS_CODE_STATUS_CLIENT, "Status-Client" }, 394 { -1, NULL } 395 }; 396 397 for (i = 0; codestr[i].code != -1; i++) { 398 if (codestr[i].code == code) 399 return (codestr[i].str); 400 } 401 402 return ("Unknown"); 403 } 404 405 static const char * 406 hexstr(const u_char *data, int len, char *str, int strsiz) 407 { 408 int i, off = 0; 409 static const char hex[] = "0123456789abcdef"; 410 411 for (i = 0; i < len; i++) { 412 if (strsiz - off < 3) 413 return (NULL); 414 str[off++] = hex[(data[i] & 0xf0) >> 4]; 415 str[off++] = hex[(data[i] & 0x0f)]; 416 str[off++] = ' '; 417 } 418 if (strsiz - off < 1) 419 return (NULL); 420 421 str[off++] = '\0'; 422 423 return (str); 424 } 425