Annotation of embedaddon/libpdel/ppp/ppp_auth_radius.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_auth.h"
45: #include "ppp/ppp_msoft.h"
46:
47: #include <openssl/md5.h>
48:
49: #include <poll.h>
50: #include <radlib.h>
51: #include <radlib_vs.h>
52: #include "ppp/ppp_auth_radius.h"
53:
54: /* Memory type */
55: #define RADIUS_MTYPE "ppp_auth_radius_info"
56:
57: #define HEXVAL(c) (isdigit(c) ? (c) - '0' : tolower(c) - 'a' + 10)
58:
59: /* Macro for logging */
60: #define LOG(sev, fmt, args...) PPP_LOG(log, sev, fmt , ## args)
61:
62: /* Macros for filling in 'struct ppp_auth_radius_info' fields */
63: #define RADINFO_ALLOC_FIELD(rip, field) \
64: do { \
65: void *_mem; \
66: \
67: if ((_mem = MALLOC(RADIUS_MTYPE, \
68: sizeof(*rip->field))) == NULL) { \
69: LOG(LOG_ERR, "%s: %m", "malloc"); \
70: goto fail_errno; \
71: } \
72: if (rip->field != NULL) { \
73: LOG(LOG_WARNING, "duplicate %s field returned" \
74: " by RADIUS server", #field); \
75: FREE(RADIUS_MTYPE, rip->field); \
76: } \
77: rip->field = _mem; \
78: } while (0)
79:
80: #define RADINFO_ALLOC_IP(rip, data, field) \
81: do { \
82: RADINFO_ALLOC_FIELD(rip, field); \
83: *rip->field = rad_cvt_addr(data); \
84: } while (0)
85:
86: #define RADINFO_ALLOC_INT(rip, data, field) \
87: do { \
88: RADINFO_ALLOC_FIELD(rip, field); \
89: *rip->field = rad_cvt_int(data); \
90: } while (0)
91:
92: #define RADINFO_ALLOC_STRING(rip, data, len, field) \
93: do { \
94: if (rip->field != NULL) { \
95: LOG(LOG_WARNING, "duplicate %s field returned" \
96: " by RADIUS server", #field); \
97: FREE(NULL, rip->field); \
98: } \
99: if ((rip->field = rad_cvt_string(data, len)) == NULL) { \
100: errno = ENOMEM; \
101: LOG(LOG_ERR, "%s: %m", "rad_cvt_string"); \
102: goto fail_errno; \
103: } \
104: } while (0)
105:
106: /* Return values from the msoft decoding routines */
107: #define MSOFT_ERROR_SYSTEM -1
108: #define MSOFT_ERROR_LIBRADIUS -2
109: #define MSOFT_ERROR_VALUE -3
110:
111: /* Internal functions */
112: static int ppp_auth_radius_wait(int fd, const struct timeval *tv,
113: int cstate, struct ppp_log *log);
114: static int ppp_auth_radius_vendor_msoft(struct rad_handle *rad,
115: struct ppp_log *log, const struct ppp_auth_cred *cred,
116: struct ppp_auth_resp *resp,
117: struct ppp_auth_radius_info *rip, int attr,
118: const void *data, size_t len);
119: static int ppp_auth_radius_mppe_decode(struct rad_handle *rad,
120: struct ppp_log *log, int salted,
121: struct ppp_auth_resp *resp, const void **datap,
122: size_t *len);
123:
124: /*
125: * Authenticate via RADIUS.
126: */
127: int
128: ppp_auth_radius_check(struct rad_handle *rad, struct ppp_log *log,
129: const struct ppp_auth_cred *cred, struct ppp_auth_resp *resp,
130: struct ppp_auth_radius_info *rip)
131: {
132: struct ppp_auth_radius_info ri;
133: struct timeval tv;
134: const void *data;
135: int rtn = -1;
136: int result;
137: int cstate;
138: size_t len;
139: int attr;
140: int fd;
141:
142: /* Simplify logic by assuming 'rip' is always valid */
143: if (rip == NULL)
144: rip = &ri;
145: memset(rip, 0, sizeof(*rip));
146:
147: /* Initialize response conservatively */
148: memset(resp, 0, sizeof(*resp));
149: strlcpy(resp->errmsg, "Unknown error", sizeof(resp->errmsg));
150:
151: /* Avoid cancellation within libradius */
152: if ((errno = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
153: &cstate)) != 0) {
154: LOG(LOG_ERR, "%s: %m", "pthread_setcancelstate");
155: goto fail_errno;
156: }
157:
158: /* Create request */
159: if (rad_create_request(rad, RAD_ACCESS_REQUEST) == -1)
160: goto fail_radius;
161:
162: /* Add attributes */
163: if (rad_put_int(rad, RAD_SERVICE_TYPE, RAD_FRAMED) == -1
164: || rad_put_int(rad, RAD_FRAMED_PROTOCOL, RAD_PPP) == -1)
165: goto fail_radius;
166: switch (cred->type) {
167: case PPP_AUTH_PAP:
168: if (rad_put_string(rad, RAD_USER_NAME, cred->u.pap.name) == -1)
169: goto fail_radius;
170: if (rad_put_string(rad, RAD_USER_PASSWORD,
171: cred->u.pap.password) == -1)
172: goto fail_radius;
173: break;
174: case PPP_AUTH_CHAP_MSV1:
175: case PPP_AUTH_CHAP_MSV2:
176: if (rad_put_string(rad, RAD_USER_NAME, cred->u.chap.name) == -1)
177: goto fail_radius;
178: if (rad_put_vendor_attr(rad, RAD_VENDOR_MICROSOFT,
179: RAD_MICROSOFT_MS_CHAP_CHALLENGE, cred->u.chap.chal_data,
180: cred->u.chap.chal_len) == -1)
181: goto fail_radius;
182: break;
183: case PPP_AUTH_CHAP_MD5:
184: if (rad_put_string(rad, RAD_USER_NAME, cred->u.chap.name) == -1)
185: goto fail_radius;
186: if (rad_put_attr(rad, RAD_CHAP_CHALLENGE,
187: cred->u.chap.chal_data, cred->u.chap.chal_len) == -1)
188: goto fail_radius;
189: break;
190: default:
191: snprintf(resp->errmsg, sizeof(resp->errmsg),
192: "unknown credential type %u", cred->type);
193: goto done;
194: }
195:
196: /* Add CHAP response attribute */
197: switch (cred->type) {
198: case PPP_AUTH_CHAP_MSV1:
199: {
200: const struct ppp_auth_cred_chap_msv1 *c = &cred->u.chap.u.msv1;
201: u_char ic[50];
202:
203: ic[0] = 0; /* this field is not used */
204: ic[1] = c->use_nt;
205: memcpy(&ic[2], c->lm_hash, 24);
206: memcpy(&ic[26], c->nt_hash, 24);
207: if (rad_put_vendor_attr(rad, RAD_VENDOR_MICROSOFT,
208: RAD_MICROSOFT_MS_CHAP_RESPONSE, ic, sizeof(ic)) == -1)
209: goto fail_radius;
210: break;
211: }
212: case PPP_AUTH_CHAP_MSV2:
213: {
214: const struct ppp_auth_cred_chap_msv2 *c = &cred->u.chap.u.msv2;
215: u_char ic[50];
216:
217: ic[0] = 0; /* this field is not used */
218: ic[1] = c->flags;
219: memcpy(&ic[2], c->peer_chal, sizeof(c->peer_chal));
220: memcpy(&ic[18], c->reserved, sizeof(c->reserved));
221: memcpy(&ic[26], c->nt_response, sizeof(c->nt_response));
222: if (rad_put_vendor_attr(rad, RAD_VENDOR_MICROSOFT,
223: RAD_MICROSOFT_MS_CHAP2_RESPONSE, ic, sizeof(ic)) == -1)
224: goto fail_radius;
225: break;
226: }
227: case PPP_AUTH_CHAP_MD5:
228: {
229: const struct ppp_auth_cred_chap_md5 *c = &cred->u.chap.u.md5;
230: u_char ic[MD5_DIGEST_LENGTH + 1];
231:
232: ic[0] = c->id;
233: memcpy(&ic[1], c->hash, sizeof(c->hash));
234: if (rad_put_attr(rad, RAD_CHAP_PASSWORD, ic, sizeof(ic)) == -1)
235: goto fail_radius;
236: break;
237: }
238: default:
239: break;
240: }
241:
242: /* Send request */
243: result = rad_init_send_request(rad, &fd, &tv);
244: while (1) {
245: int selected;
246:
247: /* Check return value */
248: switch (result) {
249: case RAD_ACCESS_ACCEPT:
250: break;
251: case RAD_ACCESS_REJECT:
252: strlcpy(resp->errmsg,
253: "Authorization failed", sizeof(resp->errmsg));
254: goto done;
255: case RAD_ACCESS_CHALLENGE:
256: strlcpy(resp->errmsg,
257: "RADIUS server returned RAD_ACCESS_CHALLENGE",
258: sizeof(resp->errmsg));
259: goto done;
260: case 0:
261: break;
262: default:
263: snprintf(resp->errmsg, sizeof(resp->errmsg),
264: "unexpected libradius return value %d", result);
265: goto done;
266: case -1:
267: goto fail_radius;
268: }
269:
270: /* If we got our response, continue below */
271: if (result > 0)
272: break;
273:
274: /* Wait for reply or timeout */
275: if ((selected = ppp_auth_radius_wait(fd,
276: &tv, cstate, log)) == -1)
277: goto fail_errno;
278:
279: /* Check in with libradius */
280: result = rad_continue_send_request(rad, selected, &fd, &tv);
281: }
282:
283: /* Extract attributes */
284: while ((attr = rad_get_attr(rad, &data, &len)) != 0) {
285: switch (attr) {
286: case RAD_FRAMED_IP_ADDRESS:
287: RADINFO_ALLOC_IP(rip, data, ip);
288: break;
289: case RAD_FRAMED_IP_NETMASK:
290: RADINFO_ALLOC_IP(rip, data, netmask);
291: break;
292: case RAD_FILTER_ID:
293: RADINFO_ALLOC_STRING(rip, data, len, filter_id);
294: break;
295: case RAD_SESSION_TIMEOUT:
296: RADINFO_ALLOC_INT(rip, data, session_timeout);
297: break;
298: case RAD_FRAMED_MTU:
299: RADINFO_ALLOC_INT(rip, data, mtu);
300: break;
301: case RAD_FRAMED_ROUTING:
302: RADINFO_ALLOC_INT(rip, data, routing);
303: break;
304: case RAD_FRAMED_COMPRESSION:
305: RADINFO_ALLOC_INT(rip, data, vjc);
306: break;
307: case RAD_FRAMED_ROUTE:
308: {
309: void *mem;
310: int num;
311:
312: /* Count number of existing routes */
313: for (num = 0;
314: rip->routes != NULL && rip->routes[num] != NULL;
315: num++);
316:
317: /* Extend the string array */
318: if ((mem = REALLOC(RADIUS_MTYPE, rip->routes,
319: (num + 2) * sizeof(*rip->routes))) == NULL) {
320: LOG(LOG_ERR, "%s: %m", "realloc");
321: goto fail_errno;
322: }
323: rip->routes = mem;
324:
325: /* Add new route */
326: if ((rip->routes[num] = rad_cvt_string(data,
327: len)) == NULL) {
328: errno = ENOMEM;
329: LOG(LOG_ERR, "%s: %m", "rad_cvt_string");
330: goto fail_errno;
331: }
332: rip->routes[num + 1] = NULL;
333: break;
334: }
335: case RAD_REPLY_MESSAGE:
336: RADINFO_ALLOC_STRING(rip, data, len, reply_message);
337: break;
338: case RAD_VENDOR_SPECIFIC:
339: {
340: u_int32_t vendor;
341:
342: attr = rad_get_vendor_attr(&vendor, &data, &len);
343: switch (vendor) {
344: case RAD_VENDOR_MICROSOFT:
345: switch (ppp_auth_radius_vendor_msoft(rad,
346: log, cred, resp, rip, attr, data, len)) {
347: case 0:
348: break;
349: default:
350: case MSOFT_ERROR_SYSTEM:
351: goto fail_errno;
352: case MSOFT_ERROR_LIBRADIUS:
353: goto fail_radius;
354: case MSOFT_ERROR_VALUE:
355: goto fail_value;
356: }
357: break;
358: default:
359: LOG(LOG_DEBUG, "unknown %s attribute"
360: " #%u:#%u returned by server",
361: "vendor", vendor, attr);
362: break;
363: }
364: break;
365: }
366: default:
367: LOG(LOG_DEBUG, "unsupported %s attribute"
368: " #%u returned by server", "RADIUS", attr);
369: break;
370: case -1:
371: goto fail_radius;
372: }
373: }
374:
375: /* Authorization successful */
376: if (result == RAD_ACCESS_ACCEPT)
377: rtn = 0;
378: goto done;
379:
380: fail_radius:
381: /* Fail because of an error from libradius */
382: strlcpy(resp->errmsg, rad_strerror(rad), sizeof(resp->errmsg));
383:
384: fail_value:
385: /* Fail because of a bogus RADIUS value */
386: LOG(LOG_ERR, "RADIUS error: %s", resp->errmsg);
387: goto done;
388:
389: fail_errno:
390: /* Fail because of some system error */
391: strlcpy(resp->errmsg, strerror(errno), sizeof(resp->errmsg));
392:
393: done:
394: /* Restore cancel state and return */
395: (void)pthread_setcancelstate(cstate, &cstate);
396: if (rip == &ri)
397: ppp_auth_radius_info_reset(rip);
398: return (rtn);
399: }
400:
401: /*
402: * Wait for readability on the file descriptor or timeout.
403: * Restore cancellability state of the thread during.
404: */
405: static int
406: ppp_auth_radius_wait(int fd, const struct timeval *tv,
407: int cstate, struct ppp_log *log)
408: {
409: struct pollfd pfd;
410: int rtn;
411:
412: /* Allow cancellation while in poll() */
413: if ((errno = pthread_setcancelstate(cstate, &cstate)) != 0) {
414: LOG(LOG_ERR, "pthread_setcancelstate: %m");
415: return (-1);
416: }
417:
418: /* Poll for data or timeout */
419: memset(&pfd, 0, sizeof(pfd));
420: pfd.fd = fd;
421: pfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
422: switch (poll(&pfd, 1, tv->tv_sec * 1000 + tv->tv_usec / 1000)) {
423: case 0:
424: rtn = 0;
425: break;
426: case 1:
427: rtn = 1;
428: break;
429: case -1:
430: default:
431: LOG(LOG_ERR, "poll: %m");
432: rtn = -1;
433: }
434:
435: /* Block cancellation again */
436: if ((errno = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
437: &cstate)) != 0) {
438: LOG(LOG_ERR, "%s: %m", "pthread_setcancelstate");
439: return (-1);
440: }
441:
442: /* Done */
443: return (rtn);
444: }
445:
446: /*
447: * Decode a Microsoft vendor attribute.
448: */
449: int
450: ppp_auth_radius_vendor_msoft(struct rad_handle *rad, struct ppp_log *log,
451: const struct ppp_auth_cred *cred, struct ppp_auth_resp *resp,
452: struct ppp_auth_radius_info *rip, int attr, const void *data,
453: size_t len)
454: {
455: int i;
456:
457: switch (attr) {
458: case RAD_MICROSOFT_MS_CHAP_ERROR:
459: case RAD_MICROSOFT_MS_CHAP2_SUCCESS:
460: {
461: const char *s;
462:
463: /* Compensate for broken servers that leave out the ID byte */
464: if (len > 0 && (len < 3 || ((const char *)data)[1] != '=')) {
465: data = (const char *)data + 1;
466: len--;
467: }
468:
469: /* Copy string as-is; if error string, we're done */
470: if (attr == RAD_MICROSOFT_MS_CHAP_ERROR) {
471: RADINFO_ALLOC_STRING(rip, data, len, mschap_error);
472: break;
473: }
474: RADINFO_ALLOC_STRING(rip, data, len, mschap2_success);
475:
476: /* Ignore authresp unless MS-CHAPv2 */
477: if (cred->type != PPP_AUTH_CHAP_MSV2)
478: break;
479:
480: /* Parse out server response */
481: if ((s = strstr(rip->mschap2_success, "S=")) == NULL) {
482: bogus_authresp: snprintf(resp->errmsg, sizeof(resp->errmsg),
483: "invalid MS-CHAPv2 response string \"%s\" returned"
484: " from server", rip->mschap2_success);
485: goto fail_value;
486: }
487: s += 2;
488: for (i = 0; i < sizeof(resp->authresp); i++) {
489: if (!isxdigit(s[i * 2]) || !isxdigit(s[i * 2 + 1]))
490: goto bogus_authresp;
491: resp->authresp[i] = (HEXVAL(s[i * 2]) << 4)
492: | HEXVAL(s[i * 2 + 1]);
493: }
494: break;
495: }
496: case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
497: RADINFO_ALLOC_INT(rip, data, mppe_policy);
498: switch (*rip->mppe_policy) {
499: case 1: /* encryption allowed */
500: case 2: /* encryption required */
501: break;
502: default:
503: snprintf(resp->errmsg, sizeof(resp->errmsg),
504: "invalid MS-CHAPv2 encryption policy %d returned"
505: " from server", *rip->mppe_policy);
506: goto fail_value;
507: }
508: break;
509: case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
510: RADINFO_ALLOC_INT(rip, data, mppe_types);
511: break;
512: case RAD_MICROSOFT_MS_MPPE_RECV_KEY:
513: case RAD_MICROSOFT_MS_MPPE_SEND_KEY:
514: {
515: u_char *key;
516: int wlen;
517:
518: /* Ignore unless we did MS-CHAP */
519: if (cred->type != PPP_AUTH_CHAP_MSV1
520: && cred->type != PPP_AUTH_CHAP_MSV2)
521: break;
522:
523: /* Decode key */
524: if ((i = ppp_auth_radius_mppe_decode(rad,
525: log, 1, resp, &data, &len)) != 0)
526: return (i);
527: key = (u_char *)data;
528:
529: /* Sanity check key length */
530: wlen = (cred->type == PPP_AUTH_CHAP_MSV1
531: && attr == RAD_MICROSOFT_MS_MPPE_SEND_KEY) ? 8 : 16;
532: if (len != wlen) {
533: snprintf(resp->errmsg, sizeof(resp->errmsg),
534: "invalid length %d != %d MPPE %s key returned"
535: " from server", (int)len, wlen,
536: attr == RAD_MICROSOFT_MS_MPPE_SEND_KEY ?
537: "send" : "recv");
538: FREE(RADIUS_MTYPE, key);
539: goto fail_value;
540: }
541:
542: /* Copy key into response structure */
543: switch (cred->type) {
544: case PPP_AUTH_CHAP_MSV1:
545: if (attr == RAD_MICROSOFT_MS_MPPE_SEND_KEY)
546: memcpy(resp->mppe.msv1.key_64, data, len);
547: else
548: memcpy(resp->mppe.msv1.key_128, data, len);
549: break;
550: case PPP_AUTH_CHAP_MSV2:
551: memcpy(resp->mppe.msv2.keys[attr
552: == RAD_MICROSOFT_MS_MPPE_RECV_KEY], data, len);
553: break;
554: default:
555: break;
556: }
557: FREE(RADIUS_MTYPE, key);
558: break;
559: }
560: case RAD_MICROSOFT_MS_CHAP_MPPE_KEYS:
561: {
562: u_char *keys;
563:
564: /* Ignore unless we did MS-CHAPv1 */
565: if (cred->type != PPP_AUTH_CHAP_MSV1)
566: break;
567:
568: /* Decode key */
569: if ((i = ppp_auth_radius_mppe_decode(rad,
570: log, 0, resp, &data, &len)) != 0)
571: return (i);
572: keys = (u_char *)data;
573:
574: /* Sanity check key length */
575: if (len != 32) {
576: snprintf(resp->errmsg, sizeof(resp->errmsg),
577: "invalid length %d != %d MPPE %s key returned"
578: " from server", (int)len, 32, "MS-CHAPv1");
579: FREE(RADIUS_MTYPE, keys);
580: goto fail_value;
581: }
582:
583: /* Copy keys into response structure */
584: memcpy(resp->mppe.msv1.key_64, keys, 8);
585: memcpy(resp->mppe.msv1.key_128, keys + 8, 16);
586: ppp_msoft_get_start_key(cred->u.chap.chal_data,
587: resp->mppe.msv1.key_128);
588: FREE(RADIUS_MTYPE, keys);
589: break;
590: }
591:
592: case -1:
593: goto fail_radius;
594:
595: default:
596: LOG(LOG_DEBUG, "unsupported %s attribute"
597: " #%u returned by server", "Microsoft", attr);
598: break;
599: }
600:
601: /* Done */
602: return (0);
603:
604: fail_errno:
605: return (MSOFT_ERROR_SYSTEM);
606:
607: fail_radius:
608: return (MSOFT_ERROR_LIBRADIUS);
609:
610: fail_value:
611: return (MSOFT_ERROR_VALUE);
612: }
613:
614: void
615: ppp_auth_radius_info_reset(struct ppp_auth_radius_info *rip)
616: {
617: FREE(RADIUS_MTYPE, rip->ip);
618: FREE(RADIUS_MTYPE, rip->netmask);
619: FREE(NULL, rip->filter_id);
620: FREE(RADIUS_MTYPE, rip->session_timeout);
621: FREE(RADIUS_MTYPE, rip->mtu);
622: FREE(RADIUS_MTYPE, rip->vjc);
623: FREE(RADIUS_MTYPE, rip->routing);
624: FREE(NULL, rip->reply_message);
625: FREE(NULL, rip->mschap_error);
626: FREE(NULL, rip->mschap2_success);
627: FREE(RADIUS_MTYPE, rip->mppe_policy);
628: FREE(RADIUS_MTYPE, rip->mppe_types);
629: if (rip->routes != NULL) {
630: char **route;
631:
632: for (route = rip->routes; *route != NULL; route++)
633: FREE(NULL, *route);
634: FREE(RADIUS_MTYPE, rip->routes);
635: }
636: memset(rip, 0, sizeof(*rip));
637: }
638:
639: #define MPPE_ENCODE_SALT_LEN 2
640: #define MPPE_ENCODE_AUTH_LEN 16
641: #define MPPE_ENCODE_CHUNK_LEN 16
642:
643: /*
644: * Decode an MPPE keys attribute
645: */
646: static int
647: ppp_auth_radius_mppe_decode(struct rad_handle *rad, struct ppp_log *log,
648: int salted, struct ppp_auth_resp *resp, const void **datap, size_t *len)
649: {
650: u_char key[MPPE_ENCODE_CHUNK_LEN];
651: char reqauth[MPPE_ENCODE_AUTH_LEN];
652: const u_char *edata = *datap;
653: size_t elen = *len;
654: const char *secret;
655: u_char *data;
656: MD5_CTX ctx;
657: int pos;
658:
659: /* Sanity check encoded length */
660: if (elen % MPPE_ENCODE_CHUNK_LEN
661: != (salted ? MPPE_ENCODE_SALT_LEN : 0)) {
662: snprintf(resp->errmsg, sizeof(resp->errmsg),
663: "bogus MPPE key %s length %u from server",
664: "encrypted", (int)elen);
665: return (MSOFT_ERROR_VALUE);
666: }
667:
668: /* Get the Request-Authenticator and server secret */
669: if (rad_request_authenticator(rad, reqauth, sizeof(reqauth)) == -1) {
670: errno = EINVAL;
671: LOG(LOG_ERR, "%s: %m", "rad_request_authenticator");
672: return (MSOFT_ERROR_SYSTEM);
673: }
674: secret = rad_server_secret(rad);
675:
676: /* Initialize decryption key */
677: MD5_Init(&ctx);
678: MD5_Update(&ctx, secret, strlen(secret));
679: MD5_Update(&ctx, reqauth, sizeof(reqauth));
680: if (salted && edata[0] != 0)
681: MD5_Update(&ctx, edata, MPPE_ENCODE_SALT_LEN);
682: MD5_Final(key, &ctx);
683:
684: /* Advance past initial salt */
685: if (salted) {
686: edata += MPPE_ENCODE_SALT_LEN;
687: elen -= MPPE_ENCODE_SALT_LEN;
688: }
689:
690: /* Allocate output buffer */
691: if ((data = MALLOC(RADIUS_MTYPE, elen)) == NULL) {
692: LOG(LOG_ERR, "%s: %m", "malloc");
693: return (MSOFT_ERROR_SYSTEM);
694: }
695:
696: /* Decrypt in blocks of MPPE_ENCODE_CHUNK_LEN */
697: pos = 0;
698: while (1) {
699: int j;
700:
701: /* Decrypt the next block */
702: for (j = 0; j < MPPE_ENCODE_CHUNK_LEN; j++)
703: data[pos++] = edata[j] ^ key[j];
704:
705: /* Advance */
706: edata += MPPE_ENCODE_CHUNK_LEN;
707: elen -= MPPE_ENCODE_CHUNK_LEN;
708: if (elen == 0)
709: break;
710:
711: /* Update key */
712: MD5_Init(&ctx);
713: MD5_Update(&ctx, secret, strlen(secret));
714: MD5_Update(&ctx, edata, MPPE_ENCODE_CHUNK_LEN);
715: MD5_Final(key, &ctx);
716: }
717:
718: /* If not salted, assume no length byte either */
719: if (!salted) {
720: *len = elen;
721: *datap = data;
722: return (0);
723: }
724:
725: /* Extract actual length (first byte) and sanity check it */
726: if (data[0] > elen - 1) {
727: snprintf(resp->errmsg, sizeof(resp->errmsg),
728: "bogus MPPE key %s length %u > %u from server",
729: "decrypted", data[0], (int)elen - 1);
730: FREE(RADIUS_MTYPE, data);
731: return (MSOFT_ERROR_VALUE);
732: }
733: *len = data[0];
734: *datap = data;
735: memmove(data, data + 1, data[0]);
736: return (0);
737: }
738:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>