1 /* $OpenBSD: radiusd_eap2mschap.c,v 1.4 2024/09/15 05:31:23 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2024 Internet Initiative Japan Inc. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <sys/types.h> 19 #include <sys/cdefs.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/tree.h> 23 #include <arpa/inet.h> 24 25 #include <assert.h> 26 #include <err.h> 27 #include <event.h> 28 #include <radius.h> 29 #include <stdbool.h> 30 #include <stddef.h> 31 #include <stdint.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <syslog.h> 36 #include <time.h> 37 #include <unistd.h> 38 39 #include "radiusd.h" 40 #include "radiusd_module.h" 41 #include "radius_subr.h" 42 #include "log.h" 43 44 #define EAP_TIMEOUT 60 45 46 #include "eap2mschap_local.h" 47 48 int 49 main(int argc, char *argv[]) 50 { 51 struct module_handlers handlers = { 52 .start = eap2mschap_start, 53 .config_set = eap2mschap_config_set, 54 .stop = eap2mschap_stop, 55 .access_request = eap2mschap_access_request, 56 .next_response = eap2mschap_next_response 57 }; 58 struct eap2mschap eap2mschap; 59 60 eap2mschap_init(&eap2mschap); 61 if ((eap2mschap.base = module_create(STDIN_FILENO, &eap2mschap, 62 &handlers)) == NULL) 63 err(1, "module_create"); 64 65 module_drop_privilege(eap2mschap.base, 0); 66 setproctitle("[main]"); 67 68 module_load(eap2mschap.base); 69 event_init(); 70 log_init(0); 71 72 if (pledge("stdio", NULL) == -1) 73 err(1, "pledge"); 74 75 module_start(eap2mschap.base); 76 event_loop(0); 77 78 module_destroy(eap2mschap.base); 79 80 event_loop(0); 81 event_base_free(NULL); 82 83 exit(EXIT_SUCCESS); 84 } 85 86 void 87 eap2mschap_init(struct eap2mschap *self) 88 { 89 memset(self, 0, sizeof(struct eap2mschap)); 90 RB_INIT(&self->eapt); 91 TAILQ_INIT(&self->reqq); 92 } 93 94 void 95 eap2mschap_start(void *ctx) 96 { 97 struct eap2mschap *self = ctx; 98 99 if (self->chap_name[0] == '\0') 100 strlcpy(self->chap_name, "radiusd", sizeof(self->chap_name)); 101 102 module_send_message(self->base, IMSG_OK, NULL); 103 104 evtimer_set(&self->ev_eapt, eap2mschap_on_eapt, self); 105 } 106 107 void 108 eap2mschap_config_set(void *ctx, const char *name, int argc, 109 char * const * argv) 110 { 111 struct eap2mschap *self = ctx; 112 const char *errmsg = "none"; 113 114 if (strcmp(name, "chap-name") == 0) { 115 SYNTAX_ASSERT(argc == 1, 116 "specify 1 argument for `chap-name'"); 117 if (strlcpy(self->chap_name, argv[0], sizeof(self->chap_name)) 118 >= sizeof(self->chap_name)) { 119 module_send_message(self->base, IMSG_NG, 120 "chap-name is too long"); 121 return; 122 } 123 } else if (strcmp(name, "_debug") == 0) 124 log_init(1); 125 else if (strncmp(name, "_", 1) == 0) 126 /* ignore all internal messages */; 127 else { 128 module_send_message(self->base, IMSG_NG, 129 "Unknown config parameter `%s'", name); 130 return; 131 } 132 133 module_send_message(self->base, IMSG_OK, NULL); 134 return; 135 syntax_error: 136 module_send_message(self->base, IMSG_NG, "%s", errmsg); 137 } 138 139 void 140 eap2mschap_stop(void *ctx) 141 { 142 struct eap2mschap *self = ctx; 143 struct access_req *req, *reqt; 144 145 evtimer_del(&self->ev_eapt); 146 147 RB_FOREACH_SAFE(req, access_reqt, &self->eapt, reqt) { 148 RB_REMOVE(access_reqt, &self->eapt, req); 149 access_request_free(req); 150 } 151 TAILQ_FOREACH_SAFE(req, &self->reqq, next, reqt) { 152 TAILQ_REMOVE(&self->reqq, req, next); 153 access_request_free(req); 154 } 155 } 156 157 void 158 eap2mschap_access_request(void *ctx, u_int q_id, const u_char *reqpkt, 159 size_t reqpktlen) 160 { 161 struct eap2mschap *self = ctx; 162 struct access_req *req = NULL; 163 RADIUS_PACKET *pkt; 164 165 if ((pkt = radius_convert_packet(reqpkt, reqpktlen)) == NULL) { 166 log_warn("%s: radius_convert_packet() failed", __func__); 167 goto on_fail; 168 } 169 170 if (radius_has_attr(pkt, RADIUS_TYPE_EAP_MESSAGE)) { 171 if ((req = eap_recv(self, q_id, pkt)) == NULL) 172 return; 173 TAILQ_INSERT_TAIL(&self->reqq, req, next); 174 radius_delete_packet(pkt); 175 return; 176 } 177 if (pkt != NULL) 178 radius_delete_packet(pkt); 179 module_accsreq_next(self->base, q_id, reqpkt, reqpktlen); 180 return; 181 on_fail: 182 if (pkt != NULL) 183 radius_delete_packet(pkt); 184 module_accsreq_aborted(self->base, q_id); 185 } 186 187 void 188 eap2mschap_next_response(void *ctx, u_int q_id, const u_char *respkt, 189 size_t respktlen) 190 { 191 struct eap2mschap *self = ctx; 192 struct access_req *req = NULL; 193 RADIUS_PACKET *pkt = NULL; 194 195 TAILQ_FOREACH(req, &self->reqq, next) { 196 if (req->q_id == q_id) 197 break; 198 } 199 if (req == NULL) { 200 module_accsreq_answer(self->base, q_id, respkt, respktlen); 201 return; 202 } 203 TAILQ_REMOVE(&self->reqq, req, next); 204 if ((pkt = radius_convert_packet(respkt, respktlen)) == NULL) { 205 log_warn("%s: q=%u radius_convert_packet() failed", __func__, 206 q_id); 207 goto on_fail; 208 } 209 eap_resp_mschap(self, req, pkt); 210 return; 211 on_fail: 212 if (pkt != NULL) 213 radius_delete_packet(pkt); 214 module_accsreq_aborted(self->base, q_id); 215 } 216 217 void 218 eap2mschap_on_eapt(int fd, short ev, void *ctx) 219 { 220 struct eap2mschap *self = ctx; 221 time_t currtime; 222 struct access_req *req, *reqt; 223 224 currtime = monotime(); 225 RB_FOREACH_SAFE(req, access_reqt, &self->eapt, reqt) { 226 if (currtime - req->eap_time > EAP_TIMEOUT) { 227 RB_REMOVE(access_reqt, &self->eapt, req); 228 access_request_free(req); 229 } 230 } 231 TAILQ_FOREACH_SAFE(req, &self->reqq, next, reqt) { 232 if (currtime - req->eap_time > EAP_TIMEOUT) { 233 TAILQ_REMOVE(&self->reqq, req, next); 234 access_request_free(req); 235 } 236 } 237 238 eap2mschap_reset_eaptimer(self); 239 } 240 241 void 242 eap2mschap_reset_eaptimer(struct eap2mschap *self) 243 { 244 struct timeval tv = { 4, 0 }; 245 246 if ((!RB_EMPTY(&self->eapt) || !TAILQ_EMPTY(&self->reqq)) && 247 evtimer_pending(&self->ev_eapt, NULL) == 0) 248 evtimer_add(&self->ev_eapt, &tv); 249 } 250 251 struct access_req * 252 access_request_new(struct eap2mschap *self, u_int q_id) 253 { 254 struct access_req *req = NULL; 255 256 if ((req = calloc(1, sizeof(struct access_req))) == NULL) { 257 log_warn("%s: Out of memory", __func__); 258 return (NULL); 259 } 260 req->eap2mschap = self; 261 req->q_id = q_id; 262 263 EAP2MSCHAP_DBG("%s(%p)", __func__, req); 264 return (req); 265 } 266 267 void 268 access_request_free(struct access_req *req) 269 { 270 EAP2MSCHAP_DBG("%s(%p)", __func__, req); 271 free(req->username); 272 if (req->pkt != NULL) 273 radius_delete_packet(req->pkt); 274 free(req); 275 } 276 277 int 278 access_request_compar(struct access_req *a, struct access_req *b) 279 { 280 return (memcmp(a->state, b->state, sizeof(a->state))); 281 } 282 283 RB_GENERATE_STATIC(access_reqt, access_req, tree, access_request_compar); 284 285 /*********************************************************************** 286 * EAP related functions 287 * Specfication: RFC 3748 [MS-CHAP] 288 ***********************************************************************/ 289 struct access_req * 290 eap_recv(struct eap2mschap *self, u_int q_id, RADIUS_PACKET *pkt) 291 { 292 char buf[512], buf2[80]; 293 size_t msgsiz = 0; 294 struct eap *eap; 295 int namesiz; 296 struct access_req *req = NULL; 297 char state[16]; 298 size_t statesiz; 299 struct access_req key; 300 301 /* 302 * Check the message authenticator. OK if it exists since the check 303 * is done by radiusd(8). 304 */ 305 if (!radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) { 306 log_warnx("q=%u Received EAP message but has no message " 307 "authenticator", q_id); 308 goto fail; 309 } 310 311 if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, NULL, 312 &msgsiz) != 0) { 313 log_warnx("q=%u Received EAP message is too big %zu", q_id, 314 msgsiz); 315 goto fail; 316 } 317 msgsiz = sizeof(buf); 318 if (radius_get_raw_attr_cat(pkt, RADIUS_TYPE_EAP_MESSAGE, buf, 319 &msgsiz) != 0) { 320 log_warnx("%s: radius_get_raw_attr_cat() failed", __func__); 321 goto fail; 322 } 323 324 eap = (struct eap *)buf; 325 if (msgsiz < offsetof(struct eap, value[1]) || 326 ntohs(eap->length) > msgsiz) { 327 log_warnx("q=%u Received EAP message has wrong in size: " 328 "received length %zu eap.length=%u", q_id, msgsiz, 329 ntohs(eap->length)); 330 goto fail; 331 } 332 333 EAP2MSCHAP_DBG("q=%u Received EAP code=%d type=%d", q_id, 334 (int)eap->code, (int)eap->value[0]); 335 336 if (eap->code != EAP_CODE_RESPONSE) { 337 log_warnx("q=%u Received EAP message has unexpected code %u", 338 q_id, (unsigned)eap->code); 339 goto fail; 340 } 341 342 if (eap->value[0] == EAP_TYPE_IDENTITY) { 343 /* 344 * Handle EAP-Indentity 345 */ 346 struct eap_mschap_challenge *chall; 347 RADIUS_PACKET *radres = NULL; 348 349 if ((req = access_request_new(self, q_id)) == NULL) 350 goto fail; 351 req->eap_time = monotime(); 352 arc4random_buf(req->state, sizeof(req->state)); 353 arc4random_buf(req->chall, sizeof(req->chall)); 354 355 namesiz = ntohs(eap->length) - offsetof(struct eap, value[1]); 356 log_info("q=%u EAP state=%s EAP-Identity %.*s ", 357 q_id, hex_string(req->state, sizeof(req->state), 358 buf2, sizeof(buf2)), namesiz, eap->value + 1); 359 namesiz = strlen(self->chap_name); 360 361 /* 362 * Start MS-CHAP-V2 363 */ 364 msgsiz = offsetof(struct eap_mschap_challenge, 365 chap_name[namesiz]); 366 chall = (struct eap_mschap_challenge *)buf; 367 chall->eap.code = EAP_CODE_REQUEST; 368 chall->eap.id = ++req->eap_id; 369 chall->eap.length = htons(msgsiz); 370 chall->eap_type = EAP_TYPE_MSCHAPV2; 371 chall->chap.code = CHAP_CHALLENGE; 372 chall->chap.id = ++req->chap_id; 373 chall->chap.length = htons(msgsiz - 374 offsetof(struct eap_mschap_challenge, chap)); 375 chall->challsiz = sizeof(chall->chall); 376 memcpy(chall->chall, req->chall, sizeof(chall->chall)); 377 memcpy(chall->chap_name, self->chap_name, namesiz); 378 379 if ((radres = radius_new_response_packet( 380 RADIUS_CODE_ACCESS_CHALLENGE, pkt)) == NULL) { 381 log_warn("%s: radius_new_response_packet() failed", 382 __func__); 383 goto fail; 384 } 385 radius_put_raw_attr(radres, RADIUS_TYPE_EAP_MESSAGE, buf, 386 msgsiz); 387 radius_put_raw_attr(radres, RADIUS_TYPE_STATE, req->state, 388 sizeof(req->state)); 389 radius_put_uint32_attr(radres, RADIUS_TYPE_SESSION_TIMEOUT, 390 EAP_TIMEOUT); 391 radius_put_message_authenticator(radres, ""); /* dummy */ 392 393 req->eap_chap_status = EAP_CHAP_CHALLENGE_SENT; 394 module_accsreq_answer(self->base, req->q_id, 395 radius_get_data(radres), radius_get_length(radres)); 396 397 radius_delete_packet(pkt); 398 radius_delete_packet(radres); 399 RB_INSERT(access_reqt, &self->eapt, req); 400 eap2mschap_reset_eaptimer(self); 401 402 return (NULL); 403 } 404 /* Other than EAP-Identity */ 405 statesiz = sizeof(state); 406 if (radius_get_raw_attr(pkt, RADIUS_TYPE_STATE, state, &statesiz) != 0) 407 { 408 log_info("q=%u received EAP message (type=%d) doesn't have a " 409 "proper state attribute", q_id, eap->value[0]); 410 goto fail; 411 } 412 413 memcpy(key.state, state, statesiz); 414 if ((req = RB_FIND(access_reqt, &self->eapt, &key)) == NULL) { 415 log_info("q=%u received EAP message (type=%d) no context for " 416 "the state=%s", q_id, eap->value[0], hex_string(state, 417 statesiz, buf2, sizeof(buf2))); 418 goto fail; 419 } 420 req->eap_time = monotime(); 421 req->q_id = q_id; 422 switch (eap->value[0]) { 423 case EAP_TYPE_NAK: 424 log_info("q=%u EAP state=%s NAK received", q_id, 425 hex_string(state, statesiz, buf2, sizeof(buf2))); 426 eap_send_reject(req, pkt, q_id); 427 goto fail; 428 case EAP_TYPE_MSCHAPV2: 429 if (msgsiz < offsetof(struct eap, value[1])) { 430 log_warnx("q=%u EAP state=%s Received message has " 431 "wrong in size for EAP-MS-CHAPV2: received length " 432 "%zu eap.length=%u", q_id, 433 hex_string(state, statesiz, buf2, sizeof(buf2)), 434 msgsiz, ntohs(eap->length)); 435 goto fail; 436 } 437 req = eap_recv_mschap(self, req, pkt, (struct eap_chap *)eap); 438 439 break; 440 default: 441 log_warnx("q=%u EAP state=%s EAP unknown type=%u receieved.", 442 q_id, hex_string(state, statesiz, buf2, sizeof(buf2)), 443 eap->value[0]); 444 goto fail; 445 } 446 447 return (req); 448 fail: 449 radius_delete_packet(pkt); 450 return (NULL); 451 } 452 453 struct access_req * 454 eap_recv_mschap(struct eap2mschap *self, struct access_req *req, 455 RADIUS_PACKET *pkt, struct eap_chap *chap) 456 { 457 size_t eapsiz; 458 char buf[80]; 459 460 EAP2MSCHAP_DBG("%s(%p)", __func__, req); 461 462 eapsiz = ntohs(chap->eap.length); 463 switch (chap->chap.code) { 464 case CHAP_RESPONSE: 465 { 466 struct eap_mschap_response *resp; 467 struct radius_ms_chap2_response rr; 468 size_t namelen; 469 bool reset_username = false; 470 471 if (req->eap_chap_status != EAP_CHAP_CHALLENGE_SENT) 472 goto failmsg; 473 resp = (struct eap_mschap_response *)chap; 474 if (eapsiz < sizeof(struct eap_mschap_response) || 475 htons(resp->chap.length) < 476 sizeof(struct eap_mschap_response) - 477 offsetof(struct eap_mschap_response, chap)) { 478 log_warnx("q=%u EAP state=%s Received EAP message has " 479 "wrong in size: received length %zu eap.length=%u " 480 "chap.length=%u valuesize=%u", req->q_id, 481 hex_string(req->state, sizeof(req->state), buf, 482 sizeof(buf)), eapsiz, ntohs(resp->eap.length), 483 ntohs(resp->chap.length), resp->chap.value[9]); 484 goto fail; 485 } 486 log_info("q=%u EAP state=%s Received " 487 "CHAP-Response", req->q_id, hex_string(req->state, 488 sizeof(req->state), buf, sizeof(buf))); 489 490 /* Unknown identity in EAP and got the username in CHAP */ 491 namelen = ntohs(resp->chap.length) - 492 (offsetof(struct eap_mschap_response, chap_name[0]) - 493 offsetof(struct eap_mschap_response, chap)); 494 if ((req->username == NULL || req->username[0] == '\0') && 495 namelen > 0) { 496 free(req->username); 497 if ((req->username = strndup(resp->chap_name, namelen)) 498 == NULL) { 499 log_warn("%s: strndup", __func__); 500 goto fail; 501 } 502 log_info("q=%u EAP state=%s username=%s", req->q_id, 503 hex_string(req->state, sizeof(req->state), buf, 504 sizeof(buf)), req->username); 505 reset_username = true; 506 } 507 508 rr.ident = resp->chap.id; 509 rr.flags = resp->flags; 510 memcpy(rr.peerchall, resp->peerchall, sizeof(rr.peerchall)); 511 memcpy(rr.reserved, resp->reserved, sizeof(rr.reserved)); 512 memcpy(rr.ntresponse, resp->ntresponse, sizeof(rr.ntresponse)); 513 514 radius_del_attr_all(pkt, RADIUS_TYPE_EAP_MESSAGE); 515 radius_del_attr_all(pkt, RADIUS_TYPE_STATE); 516 517 if (reset_username) { 518 radius_del_attr_all(pkt, RADIUS_TYPE_USER_NAME); 519 radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, 520 req->username); 521 } 522 radius_put_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 523 RADIUS_VTYPE_MS_CHAP_CHALLENGE, req->chall, 524 sizeof(req->chall)); 525 radius_put_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 526 RADIUS_VTYPE_MS_CHAP2_RESPONSE, &rr, sizeof(rr)); 527 req->eap_chap_status = EAP_CHAP_CHALLENGE_SENT; 528 RB_REMOVE(access_reqt, &self->eapt, req); 529 module_accsreq_next(self->base, req->q_id, radius_get_data(pkt), 530 radius_get_length(pkt)); 531 return (req); 532 } 533 case CHAP_SUCCESS: 534 { 535 struct eap eapres; 536 RADIUS_PACKET *radres = NULL; 537 unsigned int i; 538 uint8_t attr[256]; 539 size_t attrlen; 540 541 /* Receiving Success-Reponse */ 542 if (chap->eap.code != EAP_CODE_RESPONSE) { 543 log_info("q=%u EAP state=%s Received " 544 "CHAP-Success but EAP code is wrong %u", req->q_id, 545 hex_string(req->state, sizeof(req->state), buf, 546 sizeof(buf)), chap->eap.code); 547 goto fail; 548 } 549 if (req->eap_chap_status == EAP_CHAP_SUCCESS_REQUEST_SENT) 550 eapres.id = ++req->eap_id; 551 else if (req->eap_chap_status != EAP_CHAP_SUCCESS) 552 goto failmsg; 553 554 req->eap_chap_status = EAP_CHAP_SUCCESS; 555 eapres.code = EAP_CODE_SUCCESS; 556 eapres.length = htons(sizeof(struct eap)); 557 558 if ((radres = radius_new_response_packet( 559 RADIUS_CODE_ACCESS_ACCEPT, pkt)) == NULL) { 560 log_warn("%s: radius_new_response_packet failed", 561 __func__); 562 goto fail; 563 } 564 565 radius_put_raw_attr(radres, RADIUS_TYPE_EAP_MESSAGE, &eapres, 566 sizeof(struct eap)); 567 radius_put_raw_attr(radres, RADIUS_TYPE_STATE, req->state, 568 sizeof(req->state)); 569 /* notice authenticated username */ 570 radius_put_string_attr(radres, RADIUS_TYPE_USER_NAME, 571 req->username); 572 radius_put_message_authenticator(radres, ""); /* dummy */ 573 574 /* restore attributes */ 575 for (i = 0; i < nitems(preserve_attrs); i++) { 576 attrlen = sizeof(attr); 577 if (preserve_attrs[i].vendor == 0) { 578 if (radius_get_raw_attr(req->pkt, 579 preserve_attrs[i].type, &attr, &attrlen) 580 == 0) 581 radius_put_raw_attr(radres, 582 preserve_attrs[i].type, &attr, 583 attrlen); 584 } else { 585 if (radius_get_vs_raw_attr(req->pkt, 586 preserve_attrs[i].vendor, 587 preserve_attrs[i].type, &attr, &attrlen) 588 == 0) 589 radius_put_vs_raw_attr(radres, 590 preserve_attrs[i].vendor, 591 preserve_attrs[i].type, &attr, 592 attrlen); 593 } 594 } 595 596 module_accsreq_answer(self->base, req->q_id, 597 radius_get_data(radres), radius_get_length(radres)); 598 599 radius_delete_packet(pkt); 600 radius_delete_packet(radres); 601 602 return (NULL); 603 } 604 break; 605 } 606 failmsg: 607 log_warnx( 608 "q=%u EAP state=%s Can't handle the received EAP-CHAP message " 609 "(chap.code=%d) in EAP CHAP state=%s", req->q_id, hex_string( 610 req->state, sizeof(req->state), buf, sizeof(buf)), chap->chap.code, 611 eap_chap_status_string(req->eap_chap_status)); 612 fail: 613 radius_delete_packet(pkt); 614 return (NULL); 615 } 616 617 void 618 eap_resp_mschap(struct eap2mschap *self, struct access_req *req, 619 RADIUS_PACKET *pkt) 620 { 621 bool accept = false; 622 int id, code; 623 char resp[256 + 1], buf[80]; 624 size_t respsiz = 0, eapsiz; 625 struct { 626 struct eap_chap chap; 627 char space[256]; 628 } eap; 629 630 code = radius_get_code(pkt); 631 id = radius_get_id(pkt); 632 EAP2MSCHAP_DBG("id=%d code=%d", id, code); 633 switch (code) { 634 case RADIUS_CODE_ACCESS_ACCEPT: 635 case RADIUS_CODE_ACCESS_REJECT: 636 { 637 RADIUS_PACKET *respkt; 638 639 respsiz = sizeof(resp); 640 if (code == RADIUS_CODE_ACCESS_ACCEPT) { 641 accept = true; 642 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 643 RADIUS_VTYPE_MS_CHAP2_SUCCESS, &resp, &respsiz) 644 != 0) { 645 log_warnx("q=%u EAP state=%s no " 646 "MS-CHAP2-Success attribute", req->q_id, 647 hex_string(req->state, sizeof(req->state), 648 buf, sizeof(buf))); 649 goto fail; 650 } 651 } else { 652 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 653 RADIUS_VTYPE_MS_CHAP_ERROR, &resp, &respsiz) 654 != 0) { 655 resp[0] = ++req->chap_id; 656 snprintf(resp + 1, sizeof(resp) - 1, 657 "E=691 R=0 V=3"); 658 respsiz = 1 + strlen(resp + 1); 659 } 660 } 661 662 /* Send EAP-CHAP "Success-Request" or "Failure-Request" */ 663 if ((respkt = radius_new_request_packet(accept 664 ? RADIUS_CODE_ACCESS_CHALLENGE 665 : RADIUS_CODE_ACCESS_REJECT)) == NULL) { 666 log_warn("%s: radius_new_request_packet", __func__); 667 goto fail; 668 } 669 radius_set_id(respkt, id); 670 671 eapsiz = offsetof(struct eap_chap, chap.value[respsiz - 1]); 672 eap.chap.eap.code = EAP_CODE_REQUEST; 673 eap.chap.eap.id = ++req->eap_id; 674 eap.chap.eap.length = htons(eapsiz); 675 eap.chap.eap_type = EAP_TYPE_MSCHAPV2; 676 eap.chap.chap.id = resp[0]; 677 eap.chap.chap.length = htons( 678 offsetof(struct chap, value[respsiz - 1])); 679 memcpy(eap.chap.chap.value, resp + 1, respsiz - 1); 680 if (accept) 681 eap.chap.chap.code = CHAP_SUCCESS; 682 else 683 eap.chap.chap.code = CHAP_FAILURE; 684 685 radius_put_raw_attr(respkt, RADIUS_TYPE_STATE, req->state, 686 sizeof(req->state)); 687 radius_put_raw_attr(respkt, RADIUS_TYPE_EAP_MESSAGE, &eap, 688 eapsiz); 689 690 module_accsreq_answer(req->eap2mschap->base, req->q_id, 691 radius_get_data(respkt), radius_get_length(respkt)); 692 radius_delete_packet(respkt); 693 if (accept) 694 req->eap_chap_status = EAP_CHAP_SUCCESS_REQUEST_SENT; 695 else 696 req->eap_chap_status = EAP_CHAP_FAILURE_REQUEST_SENT; 697 698 RB_INSERT(access_reqt, &req->eap2mschap->eapt, req); 699 eap2mschap_reset_eaptimer(self); 700 req->pkt = pkt; 701 pkt = NULL; 702 break; 703 } 704 default: 705 log_warnx("q=%u Received unknown RADIUS packet code=%d", 706 req->q_id, code); 707 goto fail; 708 } 709 return; 710 fail: 711 if (pkt != NULL) 712 radius_delete_packet(pkt); 713 module_accsreq_aborted(self->base, req->q_id); 714 access_request_free(req); 715 return; 716 } 717 718 void 719 eap_send_reject(struct access_req *req, RADIUS_PACKET *reqp, u_int q_id) 720 { 721 RADIUS_PACKET *resp; 722 struct { 723 uint8_t code; 724 uint8_t id; 725 uint16_t length; 726 } __packed eap; 727 728 resp = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT, reqp); 729 if (resp == NULL) { 730 log_warn("%s: radius_new_response_packet() failed", __func__); 731 module_accsreq_aborted(req->eap2mschap->base, q_id); 732 return; 733 } 734 memset(&eap, 0, sizeof(eap)); /* just in case */ 735 eap.code = EAP_CODE_REQUEST; 736 eap.id = ++req->eap_id; 737 eap.length = htons(sizeof(eap)); 738 radius_put_raw_attr(resp, RADIUS_TYPE_EAP_MESSAGE, &eap, 739 ntohs(eap.length)); 740 module_accsreq_answer(req->eap2mschap->base, q_id, 741 radius_get_data(resp), radius_get_length(resp)); 742 radius_delete_packet(resp); 743 } 744 745 const char * 746 eap_chap_status_string(enum eap_chap_status status) 747 { 748 switch (status) { 749 case EAP_CHAP_NONE: return "None"; 750 case EAP_CHAP_CHALLENGE_SENT: return "Challenge-Sent"; 751 case EAP_CHAP_SUCCESS_REQUEST_SENT: 752 return "Success-Request-Sent"; 753 case EAP_CHAP_FAILURE_REQUEST_SENT: 754 return "Failure-Request-Sent"; 755 case EAP_CHAP_CHANGE_PASSWORD_SENT: 756 return "Change-Password-Sent"; 757 case EAP_CHAP_SUCCESS: return "Success"; 758 case EAP_CHAP_FAILED: return "Failed"; 759 } 760 return "Error"; 761 } 762 763 /*********************************************************************** 764 * Miscellaneous functions 765 ***********************************************************************/ 766 const char * 767 hex_string(const char *bytes, size_t byteslen, char *buf, size_t bufsiz) 768 { 769 const char hexstr[] = "0123456789abcdef"; 770 unsigned i, j; 771 772 for (i = 0, j = 0; i < byteslen && j + 2 < bufsiz; i++, j += 2) { 773 buf[j] = hexstr[(bytes[i] & 0xf0) >> 4]; 774 buf[j + 1] = hexstr[bytes[i] & 0xf]; 775 } 776 777 if (i < byteslen) 778 return (NULL); 779 buf[j] = '\0'; 780 return (buf); 781 } 782 783 time_t 784 monotime(void) 785 { 786 struct timespec ts; 787 788 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 789 fatal("clock_gettime(CLOCK_MONOTONIC,) failed"); 790 791 return (ts.tv_sec); 792 } 793