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
main(int argc,char * argv[])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
eap2mschap_init(struct eap2mschap * self)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
eap2mschap_start(void * ctx)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
eap2mschap_config_set(void * ctx,const char * name,int argc,char * const * argv)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
eap2mschap_stop(void * ctx)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
eap2mschap_access_request(void * ctx,u_int q_id,const u_char * reqpkt,size_t reqpktlen)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
eap2mschap_next_response(void * ctx,u_int q_id,const u_char * respkt,size_t respktlen)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
eap2mschap_on_eapt(int fd,short ev,void * ctx)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
eap2mschap_reset_eaptimer(struct eap2mschap * self)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 *
access_request_new(struct eap2mschap * self,u_int q_id)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
access_request_free(struct access_req * req)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
access_request_compar(struct access_req * a,struct access_req * b)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 *
eap_recv(struct eap2mschap * self,u_int q_id,RADIUS_PACKET * pkt)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 *
eap_recv_mschap(struct eap2mschap * self,struct access_req * req,RADIUS_PACKET * pkt,struct eap_chap * chap)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
eap_resp_mschap(struct eap2mschap * self,struct access_req * req,RADIUS_PACKET * pkt)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
eap_send_reject(struct access_req * req,RADIUS_PACKET * reqp,u_int q_id)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 *
eap_chap_status_string(enum eap_chap_status status)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 *
hex_string(const char * bytes,size_t byteslen,char * buf,size_t bufsiz)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
monotime(void)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