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