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>