Annotation of embedaddon/libpdel/ppp/ppp_auth_radius.c, revision 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>