Annotation of embedaddon/libpdel/ppp/ppp_auth_chap.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (c) 2001-2002 Packet Design, LLC.
4: * All rights reserved.
5: *
6: * Subject to the following obligations and disclaimer of warranty,
7: * use and redistribution of this software, in source or object code
8: * forms, with or without modifications are expressly permitted by
9: * Packet Design; provided, however, that:
10: *
11: * (i) Any and all reproductions of the source or object code
12: * must include the copyright notice above and the following
13: * disclaimer of warranties; and
14: * (ii) No rights are granted, in any manner or form, to use
15: * Packet Design trademarks, including the mark "PACKET DESIGN"
16: * on advertising, endorsements, or otherwise except as such
17: * appears in the above copyright notice or in the software.
18: *
19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
36: * THE POSSIBILITY OF SUCH DAMAGE.
37: *
38: * Author: Archie Cobbs <archie@freebsd.org>
39: */
40:
41: #include "ppp/ppp_defs.h"
42: #include "ppp/ppp_log.h"
43: #include "ppp/ppp_fsm_option.h"
44: #include "ppp/ppp_fsm.h"
45: #include "ppp/ppp_auth.h"
46: #include "ppp/ppp_link.h"
47: #include "ppp/ppp_util.h"
48: #include "ppp/ppp_auth_chap.h"
49:
50: #define CHAP_MTYPE "ppp_authtype.chap"
51:
52: #define CHAP_RETRY 3
53: #define CHAP_MAXTRY 5
54:
55: #define CHAP_CHALLENGE 1
56: #define CHAP_RESPONSE 2
57: #define CHAP_ACK 3
58: #define CHAP_NAK 4
59:
60: #define CHAP_MSG_ACK "Authorization successful"
61: #define CHAP_MSG_NAK "Authorization failed"
62: #define CHAP_MSG_BUFSIZE 256
63:
64: #define MSCHAPV1_MSG_ACK CHAP_MSG_ACK
65: #define MSCHAPV1_MSG_NAK "E=691 R=0"
66:
67: #define MSCHAPV2_MSG_ACK CHAP_MSG_ACK
68: #define MSCHAPV2_MSG_NAK CHAP_MSG_NAK
69:
70: /* CHAP info structure */
71: struct ppp_auth_chap {
72: struct ppp_link *link;
73: struct ppp_log *log;
74: struct ppp_auth_config aconf;
75: const struct ppp_auth_type *auth;
76: struct ppp_auth_cred cred;
77: struct ppp_auth_resp resp;
78: const struct ppp_auth_chap_type *type;
79: struct pevent_ctx *ev_ctx;
80: struct pevent *timer;
81: pthread_mutex_t *mutex;
82: int dir;
83: int retry;
84: u_char id;
85: };
86:
87: /* Internal functions */
88: static void ppp_auth_chap_send_challenge(struct ppp_auth_chap *chap);
89: static void ppp_auth_chap_send_response(struct ppp_auth_chap *chap);
90: static void ppp_auth_chap_send_result(struct ppp_auth_chap *chap,
91: u_char id, int ack);
92: static int ppp_chap_unpack(const u_char *data, size_t len,
93: char *name, u_char *value, int *vlenp);
94: static void ppp_chap_send_value(struct ppp_auth_chap *chap, u_char code,
95: const u_char *value, size_t vlen, const char *name);
96:
97: static ppp_link_auth_finish_t ppp_auth_chap_acquire_finish;
98: static ppp_link_auth_finish_t ppp_auth_chap_check_finish;
99:
100: static pevent_handler_t ppp_auth_chap_timeout;
101:
102: /* Internal variables */
103: static const char *chap_codes[] = {
104: "zero",
105: "challenge",
106: "response",
107: "ack",
108: "nak"
109: };
110:
111: /* Macro for logging */
112: #define LOG(sev, fmt, args...) PPP_LOG(chap->log, sev, fmt , ## args)
113:
114: /*
115: * Start CHAP
116: */
117: void *
118: ppp_auth_chap_start(struct pevent_ctx *ev_ctx, struct ppp_link *link,
119: pthread_mutex_t *mutex, int dir, u_int16_t *protop, struct ppp_log *log)
120: {
121: struct ppp_auth_chap *chap;
122:
123: /* Create info structure */
124: if ((chap = MALLOC(CHAP_MTYPE, sizeof(*chap))) == NULL)
125: return (NULL);
126: memset(chap, 0, sizeof(*chap));
127: chap->ev_ctx = ev_ctx;
128: chap->mutex = mutex;
129: chap->link = link;
130: chap->log = log;
131: chap->dir = dir;
132: chap->retry = CHAP_MAXTRY;
133:
134: /* Get link auth config and auth type */
135: chap->aconf = *ppp_link_auth_get_config(link);
136: chap->auth = ppp_link_get_auth(link, dir);
137: switch (chap->auth->index) {
138: case PPP_AUTH_CHAP_MD5:
139: chap->type = &ppp_auth_chap_md5;
140: break;
141: case PPP_AUTH_CHAP_MSV1:
142: chap->type = &ppp_auth_chap_msv1;
143: break;
144: case PPP_AUTH_CHAP_MSV2:
145: chap->type = &ppp_auth_chap_msv2;
146: break;
147: default:
148: errno = EPROTONOSUPPORT;
149: FREE(CHAP_MTYPE, chap);
150: return (NULL);
151: }
152: chap->cred.type = chap->auth->index;
153:
154: /* Return protocol */
155: *protop = PPP_PROTO_CHAP;
156:
157: /* If sending auth, wait for peer's challenge */
158: if (dir == PPP_PEER)
159: return (chap);
160:
161: /* If receiving auth, send first challenge */
162: ppp_auth_chap_send_challenge(chap);
163:
164: /* Done */
165: return (chap);
166: }
167:
168: /*
169: * Cancel CHAP
170: */
171: void
172: ppp_auth_chap_cancel(void *arg)
173: {
174: struct ppp_auth_chap *chap = arg;
175:
176: pevent_unregister(&chap->timer);
177: ppp_log_close(&chap->log);
178: FREE(CHAP_MTYPE, chap);
179: }
180:
181: /*
182: * Handle timeout event.
183: */
184: static void
185: ppp_auth_chap_timeout(void *arg)
186: {
187: struct ppp_auth_chap *chap = arg;
188:
189: /* Logging */
190: LOG(LOG_DEBUG, "%s timeout", chap->dir == PPP_SELF ?
191: chap_codes[CHAP_CHALLENGE] : chap_codes[CHAP_RESPONSE]);
192:
193: /* Cancel timeout event */
194: pevent_unregister(&chap->timer);
195:
196: /* Send another challenge or response? */
197: if (chap->retry <= 0) {
198: ppp_link_auth_complete(chap->link, chap->dir, NULL, NULL);
199: return;
200: }
201:
202: /* Send challenge or response again */
203: if (chap->dir == PPP_SELF)
204: ppp_auth_chap_send_challenge(chap);
205: else
206: ppp_auth_chap_send_response(chap);
207: }
208:
209: /*
210: * Handle CHAP input
211: */
212: void
213: ppp_auth_chap_input(void *arg, int dir, void *data, size_t len)
214: {
215: struct ppp_auth_chap *chap = arg;
216: struct ppp_fsm_pkt *const pkt = data;
217: u_char value[PPP_MAX_AUTHVALUE];
218: char name[PPP_MAX_AUTHNAME];
219: int vlen;
220:
221: if (len < sizeof(*pkt))
222: return;
223: memcpy(pkt, data, sizeof(*pkt));
224: pkt->length = ntohs(pkt->length);
225: if (pkt->length > len)
226: return;
227: if (pkt->length < len)
228: len = pkt->length;
229: len -= sizeof(*pkt);
230: switch (pkt->code) {
231: case CHAP_CHALLENGE:
232: {
233: struct ppp_auth_cred_chap *const cred = &chap->cred.u.chap;
234:
235: /* Check direction */
236: if (dir != PPP_PEER)
237: break;
238:
239: /* Logging */
240: LOG(LOG_DEBUG, "rec'd %s #%u", chap_codes[pkt->code], pkt->id);
241:
242: /* Parse out packet contents */
243: if (ppp_chap_unpack(pkt->data, len, name, value, &vlen) == -1) {
244: LOG(LOG_NOTICE, "rec'd malformed %s",
245: chap_codes[pkt->code]);
246: break;
247: }
248:
249: #ifdef notyet
250: /* Don't respond to our own outstanding challenge */
251: /*
252: * Don't respond to a challenge that looks like it came from
253: * us and has the wrong origination value embedded in it. This
254: * avoids a security hole associated with using the same CHAP
255: * password to authenticate in both directions on a link.
256: */
257: #endif
258:
259: /* Check challenge length (fixed for MS-CHAP types) */
260: if (chap->type->cfixed && vlen != chap->type->clen) {
261: LOG(LOG_NOTICE, "wrong %s length %u != %u"
262: " for %s", chap_codes[pkt->code],
263: vlen, chap->type->clen, chap->auth->name);
264: break;
265: }
266:
267: /* Ignore if already handling a previous challenge */
268: if (ppp_link_auth_in_progress(chap->link, chap->dir)) {
269: LOG(LOG_DEBUG, "ignoring packet, action pending");
270: break;
271: }
272:
273: /* Partially fill in credentials based on challenge info */
274: memset(cred, 0, sizeof(*cred));
275: strlcpy(cred->name, name, sizeof(cred->name)); /* XXX */
276: cred->chal_len = vlen;
277: memcpy(cred->chal_data, value, cred->chal_len);
278: if (chap->type->set_id != NULL)
279: (*chap->type->set_id)(cred, pkt->id);
280: if (chap->auth->index == PPP_AUTH_CHAP_MSV2) {
281: if (ppp_util_random(cred->u.msv2.peer_chal,
282: sizeof(cred->u.msv2.peer_chal)) == -1) {
283: LOG(LOG_NOTICE, "%s: %m", "ppp_util_random");
284: break;
285: }
286: }
287:
288: /* Acquire credentials */
289: if (ppp_link_authorize(chap->link, chap->dir,
290: &chap->cred, ppp_auth_chap_acquire_finish) == -1) {
291: ppp_link_auth_complete(chap->link,
292: chap->dir, NULL, NULL);
293: break;
294: }
295:
296: /* Save peer's id for my response */
297: chap->id = pkt->id;
298:
299: /* Now wait for credentials acquisition to finish */
300: break;
301: }
302: case CHAP_RESPONSE:
303: {
304: struct ppp_auth_cred_chap *const cred = &chap->cred.u.chap;
305: int pvlen;
306:
307: /* Check direction */
308: if (dir != PPP_SELF)
309: break;
310:
311: /* Logging */
312: LOG(LOG_DEBUG, "rec'd %s #%u", chap_codes[pkt->code], pkt->id);
313:
314: /* Stop timer */
315: pevent_unregister(&chap->timer);
316:
317: /* Ignore if already checking a previous response */
318: if (ppp_link_auth_in_progress(chap->link, chap->dir)) {
319: LOG(LOG_DEBUG, "ignoring packet, action pending");
320: break;
321: }
322:
323: /* Fill out peer credentials using response packet */
324: if (ppp_chap_unpack(pkt->data, len, cred->name,
325: (u_char *)&cred->u + chap->type->roff, &pvlen) == -1) {
326: LOG(LOG_NOTICE, "rec'd malformed %s",
327: chap_codes[pkt->code]);
328: break;
329: }
330: if (chap->type->set_id != NULL)
331: (*chap->type->set_id)(cred, pkt->id);
332:
333: /* Check credentials */
334: if (ppp_link_authorize(chap->link, chap->dir,
335: &chap->cred, ppp_auth_chap_check_finish) == -1) {
336: ppp_auth_chap_send_result(chap, pkt->id, 0);
337: ppp_link_auth_complete(chap->link,
338: chap->dir, NULL, NULL);
339: break;
340: }
341:
342: /* Save peer's id for my response */
343: chap->id = pkt->id;
344:
345: /* Now wait for check to finish */
346: break;
347: }
348: case CHAP_ACK:
349: case CHAP_NAK:
350: {
351: int valid = (pkt->code == CHAP_ACK);
352:
353: /* Check direction */
354: if (dir != PPP_PEER)
355: break;
356:
357: /* Logging */
358: LOG(LOG_DEBUG, "rec'd %s #%u", chap_codes[pkt->code], pkt->id);
359:
360: /* Stop timer */
361: pevent_unregister(&chap->timer);
362:
363: /* Do final stuff */
364: if ((*chap->type->final)(&chap->cred.u.chap, chap->log,
365: valid, pkt->data, len, chap->resp.authresp) == -1) {
366: LOG(LOG_NOTICE, "invalid CHAP %s",
367: chap_codes[pkt->code]);
368: valid = 0;
369: }
370:
371: /* Finish up */
372: if (valid) {
373: ppp_link_auth_complete(chap->link,
374: chap->dir, &chap->cred, &chap->resp.mppe);
375: } else {
376: ppp_link_auth_complete(chap->link,
377: chap->dir, NULL, NULL);
378: }
379: break;
380: }
381: default:
382: break;
383: }
384: }
385:
386: /*
387: * Continue after a successful credentials acquisition.
388: */
389: static void
390: ppp_auth_chap_acquire_finish(void *arg,
391: const struct ppp_auth_cred *creds, const struct ppp_auth_resp *resp)
392: {
393: struct ppp_auth_chap *const chap = arg;
394: struct ppp_auth_cred_chap *const cred = &chap->cred.u.chap;
395:
396: /* Copy credentials */
397: chap->cred = *creds;
398:
399: /* Sanitize credentials */
400: cred->name[sizeof(cred->name) - 1] = '\0';
401: cred->chal_len = MAX(cred->chal_len, sizeof(cred->chal_data));
402:
403: /* Send response */
404: ppp_auth_chap_send_response(chap);
405: }
406:
407: /*
408: * Continue after a successful credentials check.
409: */
410: static void
411: ppp_auth_chap_check_finish(void *arg,
412: const struct ppp_auth_cred *creds, const struct ppp_auth_resp *resp)
413: {
414: struct ppp_auth_chap *const chap = arg;
415: struct ppp_auth_cred_chap *const cred = &chap->cred.u.chap;
416: int valid = (*resp->errmsg == '\0');
417:
418: /* Copy response */
419: chap->resp = *resp;
420:
421: /* Report validity */
422: if (valid) {
423: LOG(LOG_INFO, "rec'd %s credentials for \"%s\"",
424: "valid", cred->name);
425: } else {
426: LOG(LOG_NOTICE, "rec'd %s credentials for \"%s\": %s",
427: "invalid", cred->name, resp->errmsg);
428: }
429:
430: /* Send result */
431: ppp_auth_chap_send_result(chap, chap->id, valid);
432:
433: /* Finish up */
434: ppp_link_auth_complete(chap->link,
435: chap->dir, valid ? &chap->cred : NULL, &chap->resp.mppe);
436: }
437:
438: /*
439: * Send a CHAP challenge
440: */
441: static void
442: ppp_auth_chap_send_challenge(struct ppp_auth_chap *chap)
443: {
444: struct ppp_auth_cred_chap *cred = &chap->cred.u.chap;
445:
446: /* Create a challenge (first time only) */
447: if (chap->retry == CHAP_MAXTRY) {
448:
449: /* Generate random challenge bytes */
450: if (ppp_util_random(cred->chal_data, chap->type->clen) == -1) {
451: LOG(LOG_ERR, "%s: %m", "ppp_util_random");
452: return;
453: }
454: cred->chal_len = chap->type->clen;
455:
456: /* Set id field (if appropriate) */
457: if (chap->type->set_id != NULL)
458: (*chap->type->set_id)(&chap->cred.u.chap, ++chap->id);
459: }
460:
461: /* Send packet */
462: ppp_chap_send_value(chap, CHAP_CHALLENGE,
463: cred->chal_data, cred->chal_len, cred->name);
464: }
465:
466: /*
467: * Send a CHAP response.
468: */
469: static void
470: ppp_auth_chap_send_response(struct ppp_auth_chap *chap)
471: {
472: struct ppp_auth_cred_chap *const cred = &chap->cred.u.chap;
473: u_char value[PPP_MAX_AUTHVALUE];
474:
475: /* Send response */
476: memcpy((u_char *)&cred->u + chap->type->roff,
477: value, chap->type->rlen);
478: ppp_chap_send_value(chap, CHAP_RESPONSE,
479: value, chap->type->rlen, cred->name);
480: }
481:
482: /*
483: * Send a challenge or response packet.
484: */
485: static void
486: ppp_chap_send_value(struct ppp_auth_chap *chap, u_char code,
487: const u_char *value, size_t vlen, const char *name)
488: {
489: union {
490: u_char buf[sizeof(struct ppp_fsm_pkt) + 1
491: + PPP_MAX_AUTHVALUE + PPP_MAX_AUTHNAME];
492: struct ppp_fsm_pkt pkt;
493: } u;
494: struct ppp_fsm_pkt *const pkt = &u.pkt;
495:
496: /* Cancel previous timeout event (if any) and start another */
497: pevent_unregister(&chap->timer);
498: if (pevent_register(chap->ev_ctx, &chap->timer, 0, chap->mutex,
499: ppp_auth_chap_timeout, chap, PEVENT_TIME, CHAP_RETRY * 1000) == -1)
500: LOG(LOG_ERR, "%s: %m", "pevent_register");
501:
502: /* Construct packet */
503: pkt->id = chap->id;
504: pkt->code = code;
505: pkt->length = htons(sizeof(*pkt) + 1 + vlen + strlen(name));
506: pkt->data[0] = vlen;
507: memcpy(pkt->data + 1, value, vlen);
508: memcpy(pkt->data + 1 + vlen, name, strlen(name));
509:
510: /* Logging */
511: LOG(LOG_DEBUG, "xmit %s #%u", chap_codes[code], chap->id);
512:
513: /* Send packet */
514: ppp_link_write(chap->link, PPP_PROTO_CHAP, pkt, ntohs(pkt->length));
515:
516: /* Decrement retry counter */
517: chap->retry--;
518: }
519:
520: /*
521: * Send a CHAP result
522: */
523: static void
524: ppp_auth_chap_send_result(struct ppp_auth_chap *chap, u_char id, int ack)
525: {
526: union {
527: u_char buf[sizeof(struct ppp_fsm_pkt) + CHAP_MSG_BUFSIZE];
528: struct ppp_fsm_pkt pkt;
529: } u;
530: struct ppp_fsm_pkt *const pkt = &u.pkt;
531: struct ppp_auth_cred_chap *cred = &chap->cred.u.chap;
532: int i;
533:
534: /* Construct packet */
535: pkt->id = id;
536: pkt->code = ack ? CHAP_ACK : CHAP_NAK;
537:
538: /* Add response string */
539: switch (chap->auth->index) {
540: case PPP_AUTH_CHAP_MSV1:
541: strlcpy(pkt->data, ack ? MSCHAPV1_MSG_ACK
542: : MSCHAPV1_MSG_NAK, CHAP_MSG_BUFSIZE);
543: break;
544: case PPP_AUTH_CHAP_MSV2:
545: if (ack) {
546: char hex[(PPP_MSOFTV2_AUTHRESP_LEN * 2) + 1];
547:
548: for (i = 0; i < PPP_MSOFTV2_AUTHRESP_LEN; i++) {
549: sprintf(hex + (i * 2),
550: "%02X", chap->resp.authresp[i]);
551: }
552: snprintf(pkt->data, CHAP_MSG_BUFSIZE, "S=%s", hex);
553: } else {
554: char cbuf[(2 * PPP_MSOFTV2_CHAL_LEN) + 1];
555:
556: for (i = 0; i < PPP_MSOFTV2_CHAL_LEN; i++) {
557: sprintf(cbuf + (2 * i),
558: "%02X", cred->u.msv2.peer_chal[i]);
559: }
560: snprintf(pkt->data, CHAP_MSG_BUFSIZE,
561: "E=691 R=0 C=%s V=3 M=%s", cbuf, MSCHAPV2_MSG_NAK);
562: }
563: break;
564: default:
565: strlcpy(pkt->data, ack ? CHAP_MSG_ACK
566: : CHAP_MSG_NAK, CHAP_MSG_BUFSIZE);
567: break;
568: }
569: pkt->length = htons(sizeof(*pkt) + strlen(pkt->data));
570:
571: /* Logging */
572: LOG(LOG_DEBUG, "xmit %s #%u", chap_codes[pkt->code], chap->id);
573:
574: /* Send packet */
575: ppp_link_write(chap->link, PPP_PROTO_CHAP, pkt, ntohs(pkt->length));
576: }
577:
578: /*
579: * Decode a CHAP challenge or response packet.
580: */
581: static int
582: ppp_chap_unpack(const u_char *data, size_t len,
583: char *name, u_char *value, int *vlenp)
584: {
585: int nlen;
586: int vlen;
587:
588: /* Check well-formedness */
589: if (len < 1
590: || (vlen = data[0]) < 1
591: || vlen > PPP_MAX_AUTHVALUE
592: || (nlen = len - vlen - 1) < 0
593: || nlen > PPP_MAX_AUTHNAME - 1)
594: return (-1);
595:
596: /* Get stuff */
597: memcpy(name, data + 1 + vlen, nlen);
598: name[nlen] = '\0';
599: memcpy(value, data + 1, vlen);
600: *vlenp = vlen;
601: return (0);
602: }
603:
604:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>