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>