[BACK]Return to usm.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / snmp

Annotation of src/usr.bin/snmp/usm.c, Revision 1.6

1.6     ! tb          1: /*     $OpenBSD: usm.c,v 1.5 2019/10/24 12:39:26 tb Exp $      */
1.1       martijn     2:
                      3: /*
                      4:  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/time.h>
                     20:
                     21: #include <openssl/evp.h>
                     22: #include <openssl/hmac.h>
                     23:
                     24: #include <ber.h>
                     25: #include <errno.h>
                     26: #include <string.h>
                     27: #include <time.h>
                     28:
                     29: #include "smi.h"
                     30: #include "snmp.h"
                     31: #include "usm.h"
                     32:
                     33: #define USM_MAX_DIGESTLEN 48
                     34: #define USM_MAX_TIMEWINDOW 150
                     35: #define USM_SALTOFFSET 8
                     36:
                     37: struct usm_sec {
                     38:        struct snmp_sec snmp;
                     39:        char *user;
                     40:        size_t userlen;
                     41:        int engineidset;
                     42:        char *engineid;
                     43:        size_t engineidlen;
1.2       martijn    44:        enum usm_key_level authlevel;
                     45:        const EVP_MD *digest;
                     46:        char *authkey;
1.3       martijn    47:        enum usm_key_level privlevel;
                     48:        const EVP_CIPHER *cipher;
                     49:        char *privkey;
1.1       martijn    50:        int bootsset;
                     51:        uint32_t boots;
                     52:        int timeset;
                     53:        uint32_t time;
                     54:        struct timespec timecheck;
                     55: };
                     56:
1.2       martijn    57: struct usm_cookie {
                     58:        size_t digestoffset;
1.3       martijn    59:        long long salt;
                     60:        uint32_t boots;
                     61:        uint32_t time;
1.2       martijn    62: };
                     63:
1.1       martijn    64: static int usm_doinit(struct snmp_agent *);
1.2       martijn    65: static char *usm_genparams(struct snmp_agent *, size_t *, void **);
                     66: static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *);
1.3       martijn    67: static struct ber_element *usm_encpdu(struct snmp_agent *agent,
                     68:     struct ber_element *pdu, void *cookie);
                     69: static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *,
                     70:     char *, size_t, size_t *);
1.1       martijn    71: static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
1.3       martijn    72:     size_t, uint8_t, void **);
                     73: struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *);
1.2       martijn    74: static void usm_digest_pos(void *, size_t);
1.1       martijn    75: static void usm_free(void *);
1.2       martijn    76: static char *usm_passwd2mkey(const EVP_MD *, const char *);
                     77: static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
                     78: static size_t usm_digestlen(const EVP_MD *);
1.1       martijn    79:
                     80: struct snmp_sec *
                     81: usm_init(const char *user, size_t userlen)
                     82: {
                     83:        struct snmp_sec *sec;
                     84:        struct usm_sec *usm;
                     85:
                     86:        if (user == NULL || user[0] == '\0') {
                     87:                errno = EINVAL;
                     88:                return NULL;
                     89:        }
                     90:
                     91:        if ((sec = malloc(sizeof(*sec))) == NULL)
                     92:                return NULL;
                     93:
                     94:        if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
                     95:                free(sec);
                     96:                return NULL;
                     97:        }
                     98:        if ((usm->user = malloc(userlen)) == NULL) {
                     99:                free(sec);
                    100:                free(usm);
                    101:                return NULL;
                    102:        }
                    103:        memcpy(usm->user, user, userlen);
                    104:        usm->userlen = userlen;
                    105:
                    106:        sec->model = SNMP_SEC_USM;
                    107:        sec->init = usm_doinit;
                    108:        sec->genparams = usm_genparams;
1.3       martijn   109:        sec->encpdu = usm_encpdu;
1.1       martijn   110:        sec->parseparams = usm_parseparams;
1.3       martijn   111:        sec->decpdu = usm_decpdu;
1.2       martijn   112:        sec->finalparams = usm_finalparams;
1.1       martijn   113:        sec->free = usm_free;
1.2       martijn   114:        sec->freecookie = free;
1.1       martijn   115:        sec->data = usm;
                    116:        return sec;
                    117: }
                    118:
                    119: static int
                    120: usm_doinit(struct snmp_agent *agent)
                    121: {
                    122:        struct ber_element *ber;
                    123:        struct usm_sec *usm = agent->v3->sec->data;
                    124:        int level;
                    125:        size_t userlen;
                    126:
                    127:        if (usm->engineidset && usm->bootsset && usm->timeset)
                    128:                return 0;
                    129:
                    130:        level = agent->v3->level;
                    131:        agent->v3->level = SNMP_MSGFLAG_REPORT;
                    132:        userlen = usm->userlen;
                    133:        usm->userlen = 0;
                    134:
                    135:        if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
                    136:                agent->v3->level = level;
                    137:                usm->userlen = userlen;
                    138:                return -1;
                    139:        }
1.5       tb        140:        ober_free_element(ber);
1.1       martijn   141:
                    142:        agent->v3->level = level;
                    143:        usm->userlen = userlen;
                    144:
1.4       martijn   145:         /*
                    146:          * Ugly hack for HP Laserjet:
                    147:          * This device returns the engineid on probing, but only returns boots
                    148:          * and time after a packet has been sent with full auth/enc.
                    149:          */
                    150:        if (!usm->engineidset || !usm->bootsset || !usm->timeset) {
                    151:                if ((ber = snmp_get(agent, NULL, 0)) == NULL)
                    152:                        return -1;
1.5       tb        153:                ober_free_element(ber);
1.4       martijn   154:        }
1.1       martijn   155:        return 0;
                    156: }
                    157:
                    158: static char *
1.2       martijn   159: usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
1.1       martijn   160: {
                    161:        struct ber ber;
1.2       martijn   162:        struct ber_element *params, *digestelm;
1.1       martijn   163:        struct usm_sec *usm = agent->v3->sec->data;
1.2       martijn   164:        char digest[USM_MAX_DIGESTLEN];
1.3       martijn   165:        size_t digestlen = 0, saltlen = 0;
1.1       martijn   166:        char *secparams = NULL;
                    167:        ssize_t berlen = 0;
1.2       martijn   168:        struct usm_cookie *usmcookie;
1.1       martijn   169:        struct timespec now, timediff;
                    170:
1.2       martijn   171:        bzero(digest, sizeof(digest));
                    172:
                    173:        if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
                    174:                return NULL;
                    175:        *cookie = usmcookie;
                    176:
1.3       martijn   177:        arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt));
1.1       martijn   178:        if (usm->timeset) {
1.2       martijn   179:                if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
                    180:                        free(usmcookie);
1.1       martijn   181:                        return NULL;
1.2       martijn   182:                }
1.1       martijn   183:                timespecsub(&now, &(usm->timecheck), &timediff);
1.3       martijn   184:                usmcookie->time = usm->time + timediff.tv_sec;
1.1       martijn   185:        } else
1.3       martijn   186:                usmcookie->time = 0;
                    187:        usmcookie->boots = usm->boots;
                    188:
1.2       martijn   189:        if (agent->v3->level & SNMP_MSGFLAG_AUTH)
                    190:                digestlen = usm_digestlen(usm->digest);
1.3       martijn   191:        if (agent->v3->level & SNMP_MSGFLAG_PRIV)
                    192:            saltlen = sizeof(usmcookie->salt);
                    193:
1.5       tb        194:        if ((params = ober_printf_elements(NULL, "{xddxxx}", usm->engineid,
1.3       martijn   195:            usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user,
                    196:            usm->userlen, digest, digestlen, &(usmcookie->salt),
                    197:            saltlen)) == NULL) {
1.2       martijn   198:                free(usmcookie);
                    199:                return NULL;
                    200:        }
1.1       martijn   201:
1.5       tb        202:        if (ober_scanf_elements(params, "{SSSSe",  &digestelm) == -1) {
                    203:                ober_free_element(params);
1.2       martijn   204:                free(usmcookie);
1.1       martijn   205:                return NULL;
1.2       martijn   206:        }
                    207:
1.5       tb        208:        ober_set_writecallback(digestelm, usm_digest_pos, usmcookie);
1.1       martijn   209:
                    210:        bzero(&ber, sizeof(ber));
1.5       tb        211:        ober_set_application(&ber, smi_application);
                    212:        if (ober_write_elements(&ber, params) != -1)
1.1       martijn   213:            berlen = ber_copy_writebuf(&ber, (void **)&secparams);
                    214:
                    215:        *len = berlen;
1.5       tb        216:        ober_free_element(params);
                    217:        ober_free(&ber);
1.1       martijn   218:        return secparams;
                    219: }
                    220:
1.3       martijn   221: static struct ber_element *
                    222: usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie)
                    223: {
                    224:        struct usm_sec *usm = agent->v3->sec->data;
                    225:        struct usm_cookie *usmcookie = cookie;
                    226:        struct ber ber;
                    227:        struct ber_element *retpdu;
                    228:        char *serialpdu, *encpdu;
                    229:        ssize_t pdulen;
                    230:        size_t encpdulen;
                    231:
                    232:        bzero(&ber, sizeof(ber));
1.5       tb        233:        ober_set_application(&ber, smi_application);
                    234:        pdulen = ober_write_elements(&ber, pdu);
1.3       martijn   235:        if (pdulen == -1)
                    236:                return NULL;
                    237:
1.5       tb        238:        ober_get_writebuf(&ber, (void **)&serialpdu);
1.3       martijn   239:
                    240:        encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu,
                    241:            pdulen, &encpdulen);
1.5       tb        242:        ober_free(&ber);
1.3       martijn   243:        if (encpdu == NULL)
                    244:                return NULL;
                    245:
1.5       tb        246:        retpdu = ober_add_nstring(NULL, encpdu, encpdulen);
1.3       martijn   247:        free(encpdu);
                    248:        return retpdu;
                    249: }
                    250:
                    251: static char *
                    252: usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key,
                    253:     struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen)
                    254: {
1.6     ! tb        255:        EVP_CIPHER_CTX *ctx;
1.3       martijn   256:        size_t i;
                    257:        char iv[EVP_MAX_IV_LENGTH];
                    258:        char *salt = (char *)&(cookie->salt);
                    259:        char *outtext;
                    260:        int len, len2, bs;
                    261:        uint32_t ivv;
                    262:
                    263:        switch (EVP_CIPHER_type(cipher)) {
                    264:        case NID_des_cbc:
                    265:                /* RFC3414, chap 8.1.1.1. */
                    266:                for (i = 0; i < 8; i++)
                    267:                        iv[i] = salt[i] ^ key[USM_SALTOFFSET + i];
                    268:                break;
                    269:        case NID_aes_128_cfb128:
                    270:                /* RFC3826, chap 3.1.2.1. */
                    271:                ivv = htobe32(cookie->boots);
                    272:                memcpy(iv, &ivv, sizeof(ivv));
                    273:                ivv = htobe32(cookie->time);
                    274:                memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
                    275:                memcpy(iv + 2 * sizeof(ivv), &(cookie->salt),
                    276:                    sizeof(cookie->salt));
                    277:                break;
                    278:        default:
                    279:                return NULL;
                    280:        }
                    281:
1.6     ! tb        282:        if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
1.3       martijn   283:                return NULL;
                    284:
1.6     ! tb        285:        if (!EVP_CipherInit(ctx, cipher, key, iv, do_enc)) {
        !           286:                EVP_CIPHER_CTX_free(ctx);
        !           287:                return NULL;
        !           288:        }
        !           289:
        !           290:        EVP_CIPHER_CTX_set_padding(ctx, do_enc);
1.3       martijn   291:
                    292:        bs = EVP_CIPHER_block_size(cipher);
                    293:        /* Maximum output size */
                    294:        *outlen = pdulen + (bs - (pdulen % bs));
                    295:
1.6     ! tb        296:        if ((outtext = malloc(*outlen)) == NULL) {
        !           297:                EVP_CIPHER_CTX_free(ctx);
1.3       martijn   298:                return NULL;
1.6     ! tb        299:        }
1.3       martijn   300:
1.6     ! tb        301:        if (EVP_CipherUpdate(ctx, outtext, &len, serialpdu, pdulen) &&
        !           302:            EVP_CipherFinal_ex(ctx, outtext + len, &len2))
1.3       martijn   303:                *outlen = len + len2;
                    304:        else {
                    305:                free(outtext);
                    306:                outtext = NULL;
                    307:        }
                    308:
1.6     ! tb        309:        EVP_CIPHER_CTX_free(ctx);
1.3       martijn   310:
                    311:        return outtext;
                    312: }
                    313:
1.1       martijn   314: static int
1.2       martijn   315: usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
                    316:     size_t secparamsoffset, void *cookie)
                    317: {
                    318:        struct usm_sec *usm = agent->v3->sec->data;
                    319:        struct usm_cookie *usmcookie = cookie;
                    320:        u_char digest[EVP_MAX_MD_SIZE];
                    321:
                    322:        if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
                    323:                return 0;
                    324:
                    325:        if (usm->authlevel != USM_KEY_LOCALIZED)
                    326:                return -1;
                    327:
                    328:        if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
                    329:            buflen, digest, NULL) == NULL)
                    330:                return -1;
                    331:
                    332:        memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
                    333:            usm_digestlen(usm->digest));
                    334:        return 0;
                    335: }
                    336:
                    337: static int
1.1       martijn   338: usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
1.3       martijn   339:     off_t secparamsoffset, char *buf, size_t buflen, uint8_t level,
                    340:     void **cookie)
1.1       martijn   341: {
                    342:        struct usm_sec *usm = agent->v3->sec->data;
                    343:        struct ber ber;
                    344:        struct ber_element *secparams;
1.3       martijn   345:        char *engineid, *user, *digest, *salt;
                    346:        size_t engineidlen, userlen, digestlen, saltlen;
1.1       martijn   347:        struct timespec now, timediff;
1.2       martijn   348:        off_t digestoffset;
                    349:        char exp_digest[EVP_MAX_MD_SIZE];
1.3       martijn   350:        struct usm_cookie *usmcookie;
1.1       martijn   351:
                    352:        bzero(&ber, sizeof(ber));
1.2       martijn   353:        bzero(exp_digest, sizeof(exp_digest));
1.1       martijn   354:
1.5       tb        355:        ober_set_application(&ber, smi_application);
                    356:        ober_set_readbuf(&ber, buf, buflen);
                    357:        if ((secparams = ober_read_elements(&ber, NULL)) == NULL)
1.1       martijn   358:                return -1;
1.5       tb        359:        ober_free(&ber);
1.1       martijn   360:
1.3       martijn   361:        if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL)
                    362:                goto fail;
                    363:        *cookie = usmcookie;
                    364:
1.5       tb        365:        if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen,
1.3       martijn   366:            &(usmcookie->boots), &(usmcookie->time), &user, &userlen,
                    367:            &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1)
                    368:                goto fail;
                    369:        if (saltlen != sizeof(usmcookie->salt) && saltlen != 0)
1.1       martijn   370:                goto fail;
1.3       martijn   371:        memcpy(&(usmcookie->salt), salt, saltlen);
1.1       martijn   372:
                    373:        if (!usm->engineidset) {
                    374:                if (usm_setengineid(agent->v3->sec, engineid,
                    375:                    engineidlen) == -1)
                    376:                        goto fail;
                    377:        } else {
                    378:                if (usm->engineidlen != engineidlen)
                    379:                        goto fail;
                    380:                if (memcmp(usm->engineid, engineid, engineidlen) != 0)
                    381:                        goto fail;
                    382:        }
                    383:
                    384:        if (!usm->bootsset) {
1.3       martijn   385:                usm->boots = usmcookie->boots;
1.1       martijn   386:                usm->bootsset = 1;
                    387:        } else {
1.3       martijn   388:                if (usmcookie->boots < usm->boots)
1.1       martijn   389:                        goto fail;
1.3       martijn   390:                if (usmcookie->boots > usm->boots) {
1.1       martijn   391:                        usm->bootsset = 0;
                    392:                        usm->timeset = 0;
                    393:                        usm_doinit(agent);
                    394:                        goto fail;
                    395:                }
                    396:        }
                    397:
                    398:        if (!usm->timeset) {
1.3       martijn   399:                usm->time = usmcookie->time;
1.1       martijn   400:                if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
                    401:                        goto fail;
                    402:                usm->timeset = 1;
                    403:        } else {
                    404:                if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
                    405:                        goto fail;
                    406:                timespecsub(&now, &(usm->timecheck), &timediff);
1.3       martijn   407:                if (usmcookie->time <
                    408:                    usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW ||
                    409:                    usmcookie->time >
                    410:                    usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) {
1.1       martijn   411:                        usm->bootsset = 0;
                    412:                        usm->timeset = 0;
                    413:                        usm_doinit(agent);
                    414:                        goto fail;
                    415:                }
1.4       martijn   416:        }
                    417:        /*
                    418:         * Don't assume these are set if both are zero.
                    419:         * Ugly hack for HP Laserjet
                    420:         */
                    421:        if (usm->boots == 0 && usm->time == 0) {
                    422:                usm->bootsset = 0;
                    423:                usm->timeset = 0;
1.1       martijn   424:        }
                    425:
                    426:        if (userlen != usm->userlen ||
                    427:            memcmp(user, usm->user, userlen) != 0)
                    428:                goto fail;
                    429:
1.2       martijn   430:        if (level & SNMP_MSGFLAG_AUTH) {
                    431:                if (digestlen != usm_digestlen(usm->digest))
                    432:                        goto fail;
                    433:        }
                    434:        if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
                    435:                bzero(packet + secparamsoffset + digestoffset, digestlen);
                    436:                if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet,
                    437:                    packetlen, exp_digest, NULL) == NULL)
                    438:                        goto fail;
                    439:
                    440:                if (memcmp(exp_digest, digest, digestlen) != 0)
                    441:                        goto fail;
                    442:        } else
                    443:                if (digestlen != 0)
                    444:                        goto fail;
                    445:
1.5       tb        446:        ober_free_element(secparams);
1.1       martijn   447:        return 0;
                    448:
                    449: fail:
1.3       martijn   450:        free(usmcookie);
1.5       tb        451:        ober_free_element(secparams);
1.1       martijn   452:        return -1;
                    453: }
                    454:
1.3       martijn   455: struct ber_element *
                    456: usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie)
                    457: {
                    458:        struct usm_sec *usm = agent->v3->sec->data;
                    459:        struct usm_cookie *usmcookie = cookie;
                    460:        struct ber ber;
                    461:        struct ber_element *scopedpdu;
                    462:        char *rawpdu;
                    463:        size_t rawpdulen;
                    464:
                    465:        if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie,
                    466:            encpdu, encpdulen, &rawpdulen)) == NULL)
                    467:                return NULL;
                    468:
                    469:        bzero(&ber, sizeof(ber));
1.5       tb        470:        ober_set_application(&ber, smi_application);
                    471:        ober_set_readbuf(&ber, rawpdu, rawpdulen);
                    472:        scopedpdu = ober_read_elements(&ber, NULL);
                    473:        ober_free(&ber);
1.3       martijn   474:        free(rawpdu);
                    475:
                    476:        return scopedpdu;
                    477: }
                    478:
1.1       martijn   479: static void
1.2       martijn   480: usm_digest_pos(void *data, size_t offset)
                    481: {
                    482:        struct usm_cookie *usmcookie = data;
                    483:
                    484:        usmcookie->digestoffset = offset;
                    485: }
                    486:
                    487: static void
1.1       martijn   488: usm_free(void *data)
                    489: {
                    490:        struct usm_sec *usm = data;
                    491:
                    492:        free(usm->user);
1.2       martijn   493:        free(usm->authkey);
1.3       martijn   494:        free(usm->privkey);
1.1       martijn   495:        free(usm->engineid);
                    496:        free(usm);
                    497: }
                    498:
                    499: int
1.2       martijn   500: usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
                    501:     size_t keylen, enum usm_key_level level)
                    502: {
                    503:        struct usm_sec *usm = sec->data;
                    504:        char *lkey;
                    505:
                    506:        /*
                    507:         * We could transform a master key to a local key here if we already
                    508:         * have usm_setengineid called. Sine snmpc.c is the only caller at
                    509:         * the moment there's no need, since it always calls this function
                    510:         * first.
                    511:         */
                    512:        if (level == USM_KEY_PASSWORD) {
                    513:                if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
                    514:                        return -1;
                    515:                level = USM_KEY_MASTER;
                    516:                keylen = EVP_MD_size(digest);
                    517:        } else {
                    518:                if (keylen != (size_t)EVP_MD_size(digest)) {
                    519:                        errno = EINVAL;
                    520:                        return -1;
                    521:                }
                    522:                if ((lkey = malloc(keylen)) == NULL)
                    523:                        return -1;
                    524:                memcpy(lkey, key, keylen);
                    525:                usm->authkey = lkey;
                    526:        }
                    527:        usm->digest = digest;
                    528:        usm->authlevel = level;
                    529:        return 0;
                    530: }
                    531:
                    532: int
1.3       martijn   533: usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key,
                    534:     size_t keylen, enum usm_key_level level)
                    535: {
                    536:        struct usm_sec *usm = sec->data;
                    537:        char *lkey;
                    538:
                    539:        if (usm->digest == NULL) {
                    540:                errno = EINVAL;
                    541:                return -1;
                    542:        }
                    543:
                    544:        /*
                    545:         * We could transform a master key to a local key here if we already
                    546:         * have usm_setengineid called. Sine snmpc.c is the only caller at
                    547:         * the moment there's no need, since it always calls us first.
                    548:         */
                    549:        if (level == USM_KEY_PASSWORD) {
                    550:                if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL)
                    551:                        return -1;
                    552:                level = USM_KEY_MASTER;
                    553:                keylen = EVP_MD_size(usm->digest);
                    554:        } else {
                    555:                if (keylen != (size_t)EVP_MD_size(usm->digest)) {
                    556:                        errno = EINVAL;
                    557:                        return -1;
                    558:                }
                    559:                if ((lkey = malloc(keylen)) == NULL)
                    560:                        return -1;
                    561:                memcpy(lkey, key, keylen);
                    562:                usm->privkey = lkey;
                    563:        }
                    564:        usm->cipher = cipher;
                    565:        usm->privlevel = level;
                    566:        return 0;
                    567: }
                    568:
                    569: int
1.1       martijn   570: usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
                    571: {
                    572:        struct usm_sec *usm = sec->data;
1.2       martijn   573:        char *mkey;
1.1       martijn   574:
                    575:        if (usm->engineid != NULL)
                    576:                free(usm->engineid);
                    577:        if ((usm->engineid = malloc(engineidlen)) == NULL)
                    578:                return -1;
                    579:        memcpy(usm->engineid, engineid, engineidlen);
                    580:        usm->engineidlen = engineidlen;
                    581:        usm->engineidset = 1;
                    582:
1.2       martijn   583:        if (usm->authlevel == USM_KEY_MASTER) {
                    584:                mkey = usm->authkey;
                    585:                if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
                    586:                    mkey)) == NULL) {
                    587:                        usm->authkey = mkey;
                    588:                        return -1;
                    589:                }
                    590:                free(mkey);
                    591:                usm->authlevel = USM_KEY_LOCALIZED;
1.3       martijn   592:        }
                    593:        if (usm->privlevel == USM_KEY_MASTER) {
                    594:                mkey = usm->privkey;
                    595:                if ((usm->privkey = usm_mkey2lkey(usm, usm->digest,
                    596:                    mkey)) == NULL) {
                    597:                        usm->privkey = mkey;
                    598:                        return -1;
                    599:                }
                    600:                free(mkey);
                    601:                usm->privlevel = USM_KEY_LOCALIZED;
1.2       martijn   602:        }
                    603:
1.1       martijn   604:        return 0;
                    605: }
                    606:
                    607: int
                    608: usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
                    609: {
                    610:        struct usm_sec *usm = sec->data;
                    611:
                    612:        if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
                    613:                return -1;
                    614:
                    615:        usm->boots = boots;
                    616:        usm->bootsset = 1;
                    617:        usm->time = time;
                    618:        usm->timeset = 1;
                    619:        return 0;
1.2       martijn   620: }
                    621:
                    622: static char *
                    623: usm_passwd2mkey(const EVP_MD *md, const char *passwd)
                    624: {
1.6     ! tb        625:        EVP_MD_CTX *ctx;
1.2       martijn   626:        int i, count;
                    627:        const u_char *pw;
                    628:        u_char *c;
                    629:        u_char keybuf[EVP_MAX_MD_SIZE];
                    630:        unsigned dlen;
                    631:        char *key;
                    632:
1.6     ! tb        633:        if ((ctx = EVP_MD_CTX_new()) == NULL)
        !           634:                return NULL;
        !           635:        EVP_DigestInit_ex(ctx, md, NULL);
1.2       martijn   636:        pw = (const u_char *)passwd;
                    637:        for (count = 0; count < 1048576; count += 64) {
                    638:                c = keybuf;
                    639:                for (i = 0; i < 64; i++) {
                    640:                        if (*pw == '\0')
                    641:                                pw = (const u_char *)passwd;
                    642:                        *c++ = *pw++;
                    643:                }
1.6     ! tb        644:                EVP_DigestUpdate(ctx, keybuf, 64);
1.2       martijn   645:        }
1.6     ! tb        646:        EVP_DigestFinal_ex(ctx, keybuf, &dlen);
        !           647:        EVP_MD_CTX_free(ctx);
1.2       martijn   648:
                    649:        if ((key = malloc(dlen)) == NULL)
                    650:                return NULL;
                    651:        memcpy(key, keybuf, dlen);
                    652:        return key;
                    653: }
                    654:
                    655: static char *
                    656: usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
                    657: {
1.6     ! tb        658:        EVP_MD_CTX *ctx;
1.2       martijn   659:        u_char buf[EVP_MAX_MD_SIZE];
                    660:        u_char *lkey;
                    661:        unsigned lklen;
                    662:
1.6     ! tb        663:        if ((ctx = EVP_MD_CTX_new()) == NULL)
        !           664:                return NULL;
        !           665:        EVP_DigestInit_ex(ctx, md, NULL);
1.2       martijn   666:
1.6     ! tb        667:        EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md));
        !           668:        EVP_DigestUpdate(ctx, usm->engineid, usm->engineidlen);
        !           669:        EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md));
1.2       martijn   670:
1.6     ! tb        671:        EVP_DigestFinal_ex(ctx, buf, &lklen);
        !           672:        EVP_MD_CTX_free(ctx);
1.2       martijn   673:
                    674:        if ((lkey = malloc(lklen)) == NULL)
                    675:                return NULL;
                    676:        memcpy(lkey, buf, lklen);
                    677:        return lkey;
                    678: }
                    679:
                    680: static size_t
                    681: usm_digestlen(const EVP_MD *md)
                    682: {
                    683:        switch (EVP_MD_type(md)) {
                    684:        case NID_md5:
                    685:        case NID_sha1:
                    686:                return 12;
                    687:        case NID_sha224:
                    688:                return 16;
                    689:        case NID_sha256:
                    690:                return 24;
                    691:        case NID_sha384:
                    692:                return 32;
                    693:        case NID_sha512:
                    694:                return 48;
                    695:        default:
                    696:                return 0;
                    697:        }
1.1       martijn   698: }