1/* $OpenBSD: radiusd_eap2mschap.c,v 1.3 2024/08/16 09:52:16 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 48int 49main(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 86void 87eap2mschap_init(struct eap2mschap *self) 88{ 89 memset(self, 0, sizeof(struct eap2mschap)); 90 RB_INIT(&self->eapt); 91 TAILQ_INIT(&self->reqq); 92} 93 94void 95eap2mschap_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 107void 108eap2mschap_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 139void 140eap2mschap_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 157void 158eap2mschap_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 187void 188eap2mschap_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 217void 218eap2mschap_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 241void 242eap2mschap_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 251struct access_req * 252access_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 267void 268access_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 277int 278access_request_compar(struct access_req *a, struct access_req *b) 279{ 280 return (memcmp(a->state, b->state, sizeof(a->state))); 281} 282 283RB_GENERATE_STATIC(access_reqt, access_req, tree, access_request_compar); 284 285/*********************************************************************** 286 * EAP related functions 287 * Specfication: RFC 3748 [MS-CHAP] 288 ***********************************************************************/ 289struct access_req * 290eap_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( 431 "q=%u EAP state=%s Received message has wrong in " 432 "size for EAP-MS-CHAPV2: received length %zu " 433 "eap.length=%u", q_id, hex_string(state, statesiz, 434 buf2, sizeof(buf2)), 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( 442 "q=%u EAP state=%s EAP unknown type=%u receieved.", 443 q_id, hex_string(state, statesiz, buf2, sizeof(buf2)), 444 eap->value[0]); 445 goto fail; 446 } 447 448 return (req); 449 fail: 450 radius_delete_packet(pkt); 451 return (NULL); 452} 453 454struct access_req * 455eap_recv_mschap(struct eap2mschap *self, struct access_req *req, 456 RADIUS_PACKET *pkt, struct eap_chap *chap) 457{ 458 size_t eapsiz; 459 char buf[80]; 460 461 EAP2MSCHAP_DBG("%s(%p)", __func__, req); 462 463 eapsiz = ntohs(chap->eap.length); 464 switch (chap->chap.code) { 465 case CHAP_RESPONSE: 466 { 467 struct eap_mschap_response *resp; 468 struct radius_ms_chap2_response rr; 469 size_t namelen; 470 bool reset_username = false; 471 472 if (req->eap_chap_status != EAP_CHAP_CHALLENGE_SENT) 473 goto failmsg; 474 resp = (struct eap_mschap_response *)chap; 475 if (eapsiz < sizeof(struct eap_mschap_response) || 476 htons(resp->chap.length) < 477 sizeof(struct eap_mschap_response) - 478 offsetof(struct eap_mschap_response, chap)) { 479 log_warnx( 480 "q=%u EAP state=%s Received EAP message has wrong " 481 "in size: received length %zu eap.length=%u " 482 "chap.length=%u valuesize=%u", req->q_id, 483 hex_string(req->state, sizeof(req->state), buf, 484 sizeof(buf)), eapsiz, ntohs(resp->eap.length), 485 ntohs(resp->chap.length), resp->chap.value[9]); 486 goto fail; 487 } 488 log_info("q=%u EAP state=%s Received " 489 "CHAP-Response", req->q_id, hex_string(req->state, 490 sizeof(req->state), buf, sizeof(buf))); 491 492 /* Unknown identity in EAP and got the username in CHAP */ 493 namelen = ntohs(resp->chap.length) - 494 (offsetof(struct eap_mschap_response, chap_name[0]) - 495 offsetof(struct eap_mschap_response, chap)); 496 if ((req->username == NULL || req->username[0] == '\0') && 497 namelen > 0) { 498 free(req->username); 499 if ((req->username = strndup(resp->chap_name, namelen)) 500 == NULL) { 501 log_warn("%s: strndup", __func__); 502 goto fail; 503 } 504 log_info("q=%u EAP state=%s username=%s", req->q_id, 505 hex_string(req->state, sizeof(req->state), buf, 506 sizeof(buf)), req->username); 507 reset_username = true; 508 } 509 510 rr.ident = resp->chap.id; 511 rr.flags = resp->flags; 512 memcpy(rr.peerchall, resp->peerchall, sizeof(rr.peerchall)); 513 memcpy(rr.reserved, resp->reserved, sizeof(rr.reserved)); 514 memcpy(rr.ntresponse, resp->ntresponse, sizeof(rr.ntresponse)); 515 516 radius_del_attr_all(pkt, RADIUS_TYPE_EAP_MESSAGE); 517 radius_del_attr_all(pkt, RADIUS_TYPE_STATE); 518 519 if (reset_username) { 520 radius_del_attr_all(pkt, RADIUS_TYPE_USER_NAME); 521 radius_put_string_attr(pkt, RADIUS_TYPE_USER_NAME, 522 req->username); 523 } 524 radius_put_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 525 RADIUS_VTYPE_MS_CHAP_CHALLENGE, req->chall, 526 sizeof(req->chall)); 527 radius_put_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 528 RADIUS_VTYPE_MS_CHAP2_RESPONSE, &rr, sizeof(rr)); 529 req->eap_chap_status = EAP_CHAP_CHALLENGE_SENT; 530 RB_REMOVE(access_reqt, &self->eapt, req); 531 module_accsreq_next(self->base, req->q_id, radius_get_data(pkt), 532 radius_get_length(pkt)); 533 return (req); 534 } 535 case CHAP_SUCCESS: 536 { 537 struct eap eapres; 538 RADIUS_PACKET *radres = NULL; 539 unsigned int i; 540 uint8_t attr[256]; 541 size_t attrlen; 542 543 /* Receiving Success-Reponse */ 544 if (chap->eap.code != EAP_CODE_RESPONSE) { 545 log_info("q=%u EAP state=%s Received " 546 "CHAP-Success but EAP code is wrong %u", req->q_id, 547 hex_string(req->state, sizeof(req->state), buf, 548 sizeof(buf)), chap->eap.code); 549 goto fail; 550 } 551 if (req->eap_chap_status == EAP_CHAP_SUCCESS_REQUEST_SENT) 552 eapres.id = ++req->eap_id; 553 else if (req->eap_chap_status != EAP_CHAP_SUCCESS) 554 goto failmsg; 555 556 req->eap_chap_status = EAP_CHAP_SUCCESS; 557 eapres.code = EAP_CODE_SUCCESS; 558 eapres.length = htons(sizeof(struct eap)); 559 560 if ((radres = radius_new_response_packet( 561 RADIUS_CODE_ACCESS_ACCEPT, pkt)) == NULL) { 562 log_warn("%s: radius_new_response_packet failed", 563 __func__); 564 goto fail; 565 } 566 567 radius_put_raw_attr(radres, RADIUS_TYPE_EAP_MESSAGE, &eapres, 568 sizeof(struct eap)); 569 radius_put_raw_attr(radres, RADIUS_TYPE_STATE, req->state, 570 sizeof(req->state)); 571 /* notice authenticated username */ 572 radius_put_string_attr(radres, RADIUS_TYPE_USER_NAME, 573 req->username); 574 radius_put_message_authenticator(radres, ""); /* dummy */ 575 576 /* restore attributes */ 577 for (i = 0; i < nitems(preserve_attrs); i++) { 578 attrlen = sizeof(attr); 579 if (preserve_attrs[i].vendor == 0) { 580 if (radius_get_raw_attr(req->pkt, 581 preserve_attrs[i].type, &attr, &attrlen) 582 == 0) 583 radius_put_raw_attr(radres, 584 preserve_attrs[i].type, &attr, 585 attrlen); 586 } else { 587 if (radius_get_vs_raw_attr(req->pkt, 588 preserve_attrs[i].vendor, 589 preserve_attrs[i].type, &attr, &attrlen) 590 == 0) 591 radius_put_vs_raw_attr(radres, 592 preserve_attrs[i].vendor, 593 preserve_attrs[i].type, &attr, 594 attrlen); 595 } 596 } 597 598 module_accsreq_answer(self->base, req->q_id, 599 radius_get_data(radres), radius_get_length(radres)); 600 601 radius_delete_packet(pkt); 602 radius_delete_packet(radres); 603 604 return (NULL); 605 } 606 break; 607 } 608 failmsg: 609 log_warnx( 610 "q=%u EAP state=%s Can't handle the received EAP-CHAP message " 611 "(chap.code=%d) in EAP CHAP state=%s", req->q_id, hex_string( 612 req->state, sizeof(req->state), buf, sizeof(buf)), chap->chap.code, 613 eap_chap_status_string(req->eap_chap_status)); 614 fail: 615 radius_delete_packet(pkt); 616 return (NULL); 617} 618 619void 620eap_resp_mschap(struct eap2mschap *self, struct access_req *req, 621 RADIUS_PACKET *pkt) 622{ 623 bool accept = false; 624 int id, code; 625 char resp[256 + 1], buf[80]; 626 size_t respsiz = 0, eapsiz; 627 struct { 628 struct eap_chap chap; 629 char space[256]; 630 } eap; 631 632 code = radius_get_code(pkt); 633 id = radius_get_id(pkt); 634 EAP2MSCHAP_DBG("id=%d code=%d", id, code); 635 switch (code) { 636 case RADIUS_CODE_ACCESS_ACCEPT: 637 case RADIUS_CODE_ACCESS_REJECT: 638 { 639 RADIUS_PACKET *respkt; 640 641 respsiz = sizeof(resp); 642 if (code == RADIUS_CODE_ACCESS_ACCEPT) { 643 accept = true; 644 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 645 RADIUS_VTYPE_MS_CHAP2_SUCCESS, &resp, &respsiz) 646 != 0) { 647 log_warnx("q=%u EAP state=%s no " 648 "MS-CHAP2-Success attribute", req->q_id, 649 hex_string(req->state, sizeof(req->state), 650 buf, sizeof(buf))); 651 goto fail; 652 } 653 } else { 654 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 655 RADIUS_VTYPE_MS_CHAP_ERROR, &resp, &respsiz) 656 != 0) { 657 resp[0] = ++req->chap_id; 658 snprintf(resp + 1, sizeof(resp) - 1, 659 "E=691 R=0 V=3"); 660 respsiz = 1 + strlen(resp + 1); 661 } 662 } 663 664 /* Send EAP-CHAP "Success-Request" or "Failure-Request" */ 665 if ((respkt = radius_new_request_packet(accept 666 ? RADIUS_CODE_ACCESS_CHALLENGE 667 : RADIUS_CODE_ACCESS_REJECT)) == NULL) { 668 log_warn("%s: radius_new_request_packet", __func__); 669 goto fail; 670 } 671 radius_set_id(respkt, id); 672 673 eapsiz = offsetof(struct eap_chap, chap.value[respsiz - 1]); 674 eap.chap.eap.code = EAP_CODE_REQUEST; 675 eap.chap.eap.id = ++req->eap_id; 676 eap.chap.eap.length = htons(eapsiz); 677 eap.chap.eap_type = EAP_TYPE_MSCHAPV2; 678 eap.chap.chap.id = resp[0]; 679 eap.chap.chap.length = htons( 680 offsetof(struct chap, value[respsiz - 1])); 681 memcpy(eap.chap.chap.value, resp + 1, respsiz - 1); 682 if (accept) 683 eap.chap.chap.code = CHAP_SUCCESS; 684 else 685 eap.chap.chap.code = CHAP_FAILURE; 686 687 radius_put_raw_attr(respkt, RADIUS_TYPE_STATE, req->state, 688 sizeof(req->state)); 689 radius_put_raw_attr(respkt, RADIUS_TYPE_EAP_MESSAGE, &eap, 690 eapsiz); 691 692 module_accsreq_answer(req->eap2mschap->base, req->q_id, 693 radius_get_data(respkt), radius_get_length(respkt)); 694 radius_delete_packet(respkt); 695 if (accept) 696 req->eap_chap_status = EAP_CHAP_SUCCESS_REQUEST_SENT; 697 else 698 req->eap_chap_status = EAP_CHAP_FAILURE_REQUEST_SENT; 699 700 RB_INSERT(access_reqt, &req->eap2mschap->eapt, req); 701 eap2mschap_reset_eaptimer(self); 702 req->pkt = pkt; 703 pkt = NULL; 704 break; 705 } 706 default: 707 log_warnx("q=%u Received unknown RADIUS packet code=%d", 708 req->q_id, code); 709 goto fail; 710 } 711 return; 712 fail: 713 if (pkt != NULL) 714 radius_delete_packet(pkt); 715 module_accsreq_aborted(self->base, req->q_id); 716 access_request_free(req); 717 return; 718} 719 720void 721eap_send_reject(struct access_req *req, RADIUS_PACKET *reqp, u_int q_id) 722{ 723 RADIUS_PACKET *resp; 724 struct { 725 uint8_t code; 726 uint8_t id; 727 uint16_t length; 728 } __packed eap; 729 730 resp = radius_new_response_packet(RADIUS_CODE_ACCESS_REJECT, reqp); 731 if (resp == NULL) { 732 log_warn("%s: radius_new_response_packet() failed", __func__); 733 module_accsreq_aborted(req->eap2mschap->base, q_id); 734 return; 735 } 736 memset(&eap, 0, sizeof(eap)); /* just in case */ 737 eap.code = EAP_CODE_REQUEST; 738 eap.id = ++req->eap_id; 739 eap.length = htons(sizeof(eap)); 740 radius_put_raw_attr(resp, RADIUS_TYPE_EAP_MESSAGE, &eap, 741 ntohs(eap.length)); 742 module_accsreq_answer(req->eap2mschap->base, q_id, 743 radius_get_data(resp), radius_get_length(resp)); 744 radius_delete_packet(resp); 745} 746 747const char * 748eap_chap_status_string(enum eap_chap_status status) 749{ 750 switch (status) { 751 case EAP_CHAP_NONE: return "None"; 752 case EAP_CHAP_CHALLENGE_SENT: return "Challenge-Sent"; 753 case EAP_CHAP_SUCCESS_REQUEST_SENT: 754 return "Success-Request-Sent"; 755 case EAP_CHAP_FAILURE_REQUEST_SENT: 756 return "Failure-Request-Sent"; 757 case EAP_CHAP_CHANGE_PASSWORD_SENT: 758 return "Change-Password-Sent"; 759 case EAP_CHAP_SUCCESS: return "Success"; 760 case EAP_CHAP_FAILED: return "Failed"; 761 } 762 return "Error"; 763} 764 765/*********************************************************************** 766 * Miscellaneous functions 767 ***********************************************************************/ 768const char * 769hex_string(const char *bytes, size_t byteslen, char *buf, size_t bufsiz) 770{ 771 const char hexstr[] = "0123456789abcdef"; 772 unsigned i, j; 773 774 for (i = 0, j = 0; i < byteslen && j + 2 < bufsiz; i++, j += 2) { 775 buf[j] = hexstr[(bytes[i] & 0xf0) >> 4]; 776 buf[j + 1] = hexstr[bytes[i] & 0xf]; 777 } 778 779 if (i < byteslen) 780 return (NULL); 781 buf[j] = '\0'; 782 return (buf); 783} 784 785time_t 786monotime(void) 787{ 788 struct timespec ts; 789 790 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 791 fatal("clock_gettime(CLOCK_MONOTONIC,) failed"); 792 793 return (ts.tv_sec); 794} 795