diff options
author | Martijn van Duren <martijn@cvs.openbsd.org> | 2019-09-18 09:54:37 +0000 |
---|---|---|
committer | Martijn van Duren <martijn@cvs.openbsd.org> | 2019-09-18 09:54:37 +0000 |
commit | 6124587d3ce2e2fe998f4c7c05ad3b1bd0d89474 (patch) | |
tree | fe9b898310c809866d16a36a3e3731d9a4336dd0 /usr.bin/snmp | |
parent | 4ef48e0cd179f9842390d743e3bea521f30642a8 (diff) |
Add support for SNMPv3/USM privacy.
net-snmp's -3K is supported via the -K parameter, -3K is not supported.
Feedback and OK jmatthew@
Diffstat (limited to 'usr.bin/snmp')
-rw-r--r-- | usr.bin/snmp/snmp.1 | 55 | ||||
-rw-r--r-- | usr.bin/snmp/snmp.c | 33 | ||||
-rw-r--r-- | usr.bin/snmp/snmp.h | 8 | ||||
-rw-r--r-- | usr.bin/snmp/snmpc.c | 51 | ||||
-rw-r--r-- | usr.bin/snmp/usm.c | 232 | ||||
-rw-r--r-- | usr.bin/snmp/usm.h | 4 |
6 files changed, 344 insertions, 39 deletions
diff --git a/usr.bin/snmp/snmp.1 b/usr.bin/snmp/snmp.1 index a1ab1a65ee0..5303290816f 100644 --- a/usr.bin/snmp/snmp.1 +++ b/usr.bin/snmp/snmp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: snmp.1,v 1.5 2019/09/18 09:52:47 martijn Exp $ +.\" $OpenBSD: snmp.1,v 1.6 2019/09/18 09:54:36 martijn Exp $ .\" .\" Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> .\" @@ -28,6 +28,7 @@ .Op Fl c Ar community .Op Fl E Ar ctxengineid .Op Fl e Ar secengineid +.Op Fl K Ar localpriv .Op Fl k Ar localauth .Op Fl l Ar seclevel .Op Fl n Ar ctxname @@ -36,6 +37,8 @@ .Op Fl t Ar timeout .Op Fl u Ar user .Op Fl v Ar version +.Op Fl X Ar privpass +.Op Fl x Ar cipher .Op Fl Z Ar boots , Ns Ar time .Ar agent .Ar oid ... @@ -46,6 +49,7 @@ .Op Fl c Ar community .Op Fl E Ar ctxengineid .Op Fl e Ar secengineid +.Op Fl K Ar localpriv .Op Fl k Ar localauth .Op Fl l Ar seclevel .Op Fl n Ar ctxname @@ -54,6 +58,8 @@ .Op Fl t Ar timeout .Op Fl u Ar user .Op Fl v Ar version +.Op Fl X Ar privpass +.Op Fl x Ar cipher .Op Fl Z Ar boots , Ns Ar time .Op Fl C Cm cIipt .Op Fl C Cm E Ar endoid @@ -66,6 +72,7 @@ .Op Fl c Ar community .Op Fl E Ar ctxengineid .Op Fl e Ar secengineid +.Op Fl K Ar localpriv .Op Fl k Ar localauth .Op Fl l Ar seclevel .Op Fl n Ar ctxname @@ -74,6 +81,8 @@ .Op Fl t Ar timeout .Op Fl u Ar user .Op Fl v Ar version +.Op Fl X Ar privpass +.Op Fl x Ar cipher .Op Fl Z Ar boots , Ns Ar time .Op Fl C Cm n Ns Ar nonrep Ns Cm r Ns Ar maxrep .Ar agent @@ -85,6 +94,7 @@ .Op Fl c Ar community .Op Fl E Ar ctxengineid .Op Fl e Ar secengineid +.Op Fl K Ar localpriv .Op Fl k Ar localauth .Op Fl l Ar seclevel .Op Fl n Ar ctxname @@ -93,6 +103,8 @@ .Op Fl t Ar timeout .Op Fl u Ar user .Op Fl v Ar version +.Op Fl X Ar privpass +.Op Fl x Ar cipher .Op Fl Z Ar boots , Ns Ar time .Op Fl C Cm cipn Ns Ar nonrep Ns Cm r Ns Ar maxrep .Ar agent @@ -104,6 +116,7 @@ .Op Fl c Ar community .Op Fl E Ar ctxengineid .Op Fl e Ar secengineid +.Op Fl K Ar localpriv .Op Fl k Ar localauth .Op Fl l Ar seclevel .Op Fl n Ar ctxname @@ -111,6 +124,8 @@ .Op Fl t Ar timeout .Op Fl u Ar user .Op Fl v Ar version +.Op Fl X Ar privpass +.Op Fl x Ar cipher .Op Fl Z Ar boots , Ns Ar time .Ar agent uptime trapoid .Oo Ar varoid type value Oc ... @@ -302,6 +317,14 @@ The snmpv3 context engine id. Most of the time this value can be safely ignored. This option is only used by .Fl v Cm 3 . +.It Fl K Ar localpriv +The localized privacy password for the user in hexadecimal format +.Po +optionally prefixed with a +.Cm 0x +.Pc . +This option is only used by +.Fl v Cm 3 . .It Fl k Ar localauth The localized authentication password for the user in hexadecimal format .Po @@ -313,14 +336,24 @@ This option is only used by .It Fl l Ar seclevel The security level. Values can be -.Cm noAuthNoPriv Pq default -or +.Cm noAuthNoPriv Pq default , .Cm authNoPriv .Po requires either .Fl A or .Fl k +.Pc +or +.Cm authPriv +.Po +requires either +.Fl X +or +.Fl K +in addition to the +.Cm authNoPriv +requirements .Pc . This option is only used by .Fl v Cm 3 . @@ -382,6 +415,22 @@ or .Cm 3 . Currently defaults to .Cm 2c . +.It Fl X Ar privpass +The privacy password for the user. +This will be tansformed to +.Ar localpriv . +This option is only used by +.Fl v Cm 3 . +.It Fl x Ar cipher +Sets the cipher +.Pq privacy +protocol. +Options are +.Cm DES +and +.Cm AES . +This option is only used by +.Fl v Cm 3 . .It Fl Z Ar boots , Ns Ar time Set the engine boots and engine time. Under normal circumstances this value is discovered via snmpv3 discovery and diff --git a/usr.bin/snmp/snmp.c b/usr.bin/snmp/snmp.c index 4d04f2df07b..31ee2efa30a 100644 --- a/usr.bin/snmp/snmp.c +++ b/usr.bin/snmp/snmp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmp.c,v 1.5 2019/09/18 09:52:47 martijn Exp $ */ +/* $OpenBSD: snmp.c,v 1.6 2019/09/18 09:54:36 martijn Exp $ */ /* * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> @@ -359,7 +359,7 @@ static char * snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len) { struct ber ber; - struct ber_element *message, *scopedpdu = NULL, *secparams; + struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu; ssize_t securitysize, ret; size_t secparamsoffset; char *securityparams = NULL, *packet = NULL; @@ -402,6 +402,13 @@ snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len) ber_free_elements(scopedpdu); goto fail; } + if (agent->v3->level & SNMP_MSGFLAG_PRIV) { + if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu, + cookie)) == NULL) + goto fail; + ber_free_elements(scopedpdu); + scopedpdu = encpdu; + } if (ber_printf_elements(message, "d{idxd}xe", agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level), (size_t) 1, agent->v3->sec->model, securityparams, @@ -450,8 +457,9 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) size_t msgflagslen, secparamslen; struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname; off_t secparamsoffset; - char *engineid; - size_t engineidlen; + char *encpdu, *engineid; + size_t encpdulen, engineidlen; + void *cookie = NULL; bzero(&ber, sizeof(ber)); ber_set_application(&ber, smi_application); @@ -485,9 +493,19 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) if (msgflagslen != 1) goto fail; if (agent->v3->sec->parseparams(agent, buf, buflen, - secparamsoffset, secparams, secparamslen, - msgflags[0]) == -1) + secparamsoffset, secparams, secparamslen, msgflags[0], + &cookie) == -1) { + cookie = NULL; goto fail; + } + if (msgflags[0] & SNMP_MSGFLAG_PRIV) { + if (ber_scanf_elements(scopedpdu, "x", &encpdu, + &encpdulen) == -1) + goto fail; + if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu, + encpdulen, cookie)) == NULL) + goto fail; + } if (ber_scanf_elements(scopedpdu, "{xeS{", &engineid, &engineidlen, &ctxname) == -1) goto fail; @@ -505,11 +523,14 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) } ber_free_elements(message); + agent->v3->sec->freecookie(cookie); return pdu; } /* NOTREACHED */ fail: + if (version == SNMP_V3) + agent->v3->sec->freecookie(cookie); ber_free_elements(message); return NULL; } diff --git a/usr.bin/snmp/snmp.h b/usr.bin/snmp/snmp.h index fb777223d73..f22310a816e 100644 --- a/usr.bin/snmp/snmp.h +++ b/usr.bin/snmp/snmp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: snmp.h,v 1.4 2019/09/18 09:52:47 martijn Exp $ */ +/* $OpenBSD: snmp.h,v 1.5 2019/09/18 09:54:36 martijn Exp $ */ /* * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> @@ -114,9 +114,13 @@ struct snmp_sec { enum snmp_security_model model; int (*init)(struct snmp_agent *); char *(*genparams)(struct snmp_agent *, size_t *, void **); + struct ber_element *(*encpdu)(struct snmp_agent *, + struct ber_element *, void *); int (*finalparams)(struct snmp_agent *, char *, size_t, size_t, void *); int (*parseparams)(struct snmp_agent *, char *, size_t, off_t, char *, - size_t, uint8_t); + size_t, uint8_t, void **); + struct ber_element *(*decpdu)(struct snmp_agent *, char *, size_t, + void *); void (*free)(void *); void (*freecookie)(void *); void *data; diff --git a/usr.bin/snmp/snmpc.c b/usr.bin/snmp/snmpc.c index 0f208aaca92..f8462c8d0e0 100644 --- a/usr.bin/snmp/snmpc.c +++ b/usr.bin/snmp/snmpc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpc.c,v 1.10 2019/09/18 09:52:47 martijn Exp $ */ +/* $OpenBSD: snmpc.c,v 1.11 2019/09/18 09:54:36 martijn Exp $ */ /* * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> @@ -42,7 +42,7 @@ #include "snmp.h" #include "usm.h" -#define GETOPT_COMMON "A:a:c:E:e:k:l:n:O:r:t:u:v:Z:" +#define GETOPT_COMMON "A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:" int snmpc_get(int, char *[]); int snmpc_walk(int, char *[]); @@ -98,11 +98,15 @@ int main(int argc, char *argv[]) { const EVP_MD *md = NULL; + const EVP_CIPHER *cipher = NULL; struct snmp_sec *sec; char *user = NULL; enum usm_key_level authkeylevel; char *authkey = NULL; size_t authkeylen = 0; + enum usm_key_level privkeylevel; + char *privkey = NULL; + size_t privkeylen = 0; int seclevel = SNMP_MSGFLAG_REPORT; char *ctxname = NULL; char *ctxengineid = NULL, *secengineid = NULL; @@ -193,6 +197,16 @@ main(int argc, char *argv[]) err(1, "-3e"); } break; + case 'K': + privkey = snmpc_hex2bin(optarg, &privkeylen); + if (privkey == NULL) { + if (errno == EINVAL) + errx(1, "Bad key value after " + "-3K flag."); + errx(1, "-3K"); + } + privkeylevel = USM_KEY_LOCALIZED; + break; case 'k': authkey = snmpc_hex2bin(optarg, &authkeylen); if (authkey == NULL) { @@ -208,6 +222,9 @@ main(int argc, char *argv[]) else if (strcasecmp(optarg, "authNoPriv") == 0) seclevel = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_REPORT; + else if (strcasecmp(optarg, "authPriv") == 0) + seclevel = SNMP_MSGFLAG_AUTH | + SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT; else errx(1, "Invalid security level specified " "after -l flag: %s", optarg); @@ -369,6 +386,21 @@ main(int argc, char *argv[]) } } break; + case 'X': + privkey = optarg; + privkeylen = strlen(privkey); + privkeylevel = USM_KEY_PASSWORD; + break; + case 'x': + if (strcasecmp(optarg, "DES") == 0) + cipher = EVP_des_cbc(); + else if (strcasecmp(optarg, "AES") == 0) + cipher = EVP_aes_128_cfb128(); + else + errx(1, "Invalid privacy protocol " + "specified after -3x flag: %s", + optarg); + break; case 'Z': boots = strtoll(optarg, &strtolp, 10); if (boots < 0 || strtolp == optarg || strtolp[0] != ',') @@ -403,6 +435,15 @@ main(int argc, char *argv[]) authkeylevel) == -1) err(1, "Can't set authkey"); } + if (seclevel & SNMP_MSGFLAG_PRIV) { + if (cipher == NULL) + cipher = EVP_des_cbc(); + if (privkey == NULL) + errx(1, "No privKey or privPassword specified"); + if (usm_setpriv(sec, cipher, privkey, privkeylen, + privkeylevel) == -1) + err(1, "Can't set authkey"); + } if (secengineid != NULL) { if (usm_setengineid(sec, secengineid, secengineidlen) == -1) @@ -1087,9 +1128,9 @@ usage(void) snmp_app->name, snmp_app->usecommonopt ? " [-A authpass] [-a digest] [-c community] [-e secengineid]\n" - " [-E ctxengineid] [-k localauth] [-l seclevel] [-n ctxname]\n" - " [-O afnqvxSQ] [-r retries] [-t timeout] [-u user] [-v version]\n" - " [-Z boots,time]\n" + " [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n" + " [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n" + " [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n" " " : "", snmp_app->usage == NULL ? "" : snmp_app->usage); exit(1); diff --git a/usr.bin/snmp/usm.c b/usr.bin/snmp/usm.c index 1ca43316b43..7da977c9b25 100644 --- a/usr.bin/snmp/usm.c +++ b/usr.bin/snmp/usm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usm.c,v 1.2 2019/09/18 09:52:47 martijn Exp $ */ +/* $OpenBSD: usm.c,v 1.3 2019/09/18 09:54:36 martijn Exp $ */ /* * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> @@ -44,6 +44,9 @@ struct usm_sec { enum usm_key_level authlevel; const EVP_MD *digest; char *authkey; + enum usm_key_level privlevel; + const EVP_CIPHER *cipher; + char *privkey; int bootsset; uint32_t boots; int timeset; @@ -53,13 +56,21 @@ struct usm_sec { struct usm_cookie { size_t digestoffset; + long long salt; + uint32_t boots; + uint32_t time; }; static int usm_doinit(struct snmp_agent *); static char *usm_genparams(struct snmp_agent *, size_t *, void **); static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *); +static struct ber_element *usm_encpdu(struct snmp_agent *agent, + struct ber_element *pdu, void *cookie); +static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *, + char *, size_t, size_t *); static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *, - size_t, uint8_t); + size_t, uint8_t, void **); +struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *); static void usm_digest_pos(void *, size_t); static void usm_free(void *); static char *usm_passwd2mkey(const EVP_MD *, const char *); @@ -95,7 +106,9 @@ usm_init(const char *user, size_t userlen) sec->model = SNMP_SEC_USM; sec->init = usm_doinit; sec->genparams = usm_genparams; + sec->encpdu = usm_encpdu; sec->parseparams = usm_parseparams; + sec->decpdu = usm_decpdu; sec->finalparams = usm_finalparams; sec->free = usm_free; sec->freecookie = free; @@ -139,12 +152,11 @@ usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie) struct ber_element *params, *digestelm; struct usm_sec *usm = agent->v3->sec->data; char digest[USM_MAX_DIGESTLEN]; - size_t digestlen = 0; + size_t digestlen = 0, saltlen = 0; char *secparams = NULL; ssize_t berlen = 0; struct usm_cookie *usmcookie; struct timespec now, timediff; - uint32_t boots, time; bzero(digest, sizeof(digest)); @@ -152,21 +164,27 @@ usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie) return NULL; *cookie = usmcookie; + arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt)); if (usm->timeset) { if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { free(usmcookie); return NULL; } timespecsub(&now, &(usm->timecheck), &timediff); - time = usm->time + timediff.tv_sec; + usmcookie->time = usm->time + timediff.tv_sec; } else - time = 0; - boots = usm->boots; + usmcookie->time = 0; + usmcookie->boots = usm->boots; + if (agent->v3->level & SNMP_MSGFLAG_AUTH) digestlen = usm_digestlen(usm->digest); + if (agent->v3->level & SNMP_MSGFLAG_PRIV) + saltlen = sizeof(usmcookie->salt); + if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid, - usm->engineidlen, boots, time, usm->user, usm->userlen, digest, - digestlen, NULL, (size_t) 0)) == NULL) { + usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user, + usm->userlen, digest, digestlen, &(usmcookie->salt), + saltlen)) == NULL) { free(usmcookie); return NULL; } @@ -190,6 +208,93 @@ usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie) return secparams; } +static struct ber_element * +usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie) +{ + struct usm_sec *usm = agent->v3->sec->data; + struct usm_cookie *usmcookie = cookie; + struct ber ber; + struct ber_element *retpdu; + char *serialpdu, *encpdu; + ssize_t pdulen; + size_t encpdulen; + + bzero(&ber, sizeof(ber)); + ber_set_application(&ber, smi_application); + pdulen = ber_write_elements(&ber, pdu); + if (pdulen == -1) + return NULL; + + ber_get_writebuf(&ber, (void **)&serialpdu); + + encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu, + pdulen, &encpdulen); + ber_free(&ber); + if (encpdu == NULL) + return NULL; + + retpdu = ber_add_nstring(NULL, encpdu, encpdulen); + free(encpdu); + return retpdu; +} + +static char * +usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key, + struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen) +{ + EVP_CIPHER_CTX ctx; + size_t i; + char iv[EVP_MAX_IV_LENGTH]; + char *salt = (char *)&(cookie->salt); + char *outtext; + int len, len2, bs; + uint32_t ivv; + + switch (EVP_CIPHER_type(cipher)) { + case NID_des_cbc: + /* RFC3414, chap 8.1.1.1. */ + for (i = 0; i < 8; i++) + iv[i] = salt[i] ^ key[USM_SALTOFFSET + i]; + break; + case NID_aes_128_cfb128: + /* RFC3826, chap 3.1.2.1. */ + ivv = htobe32(cookie->boots); + memcpy(iv, &ivv, sizeof(ivv)); + ivv = htobe32(cookie->time); + memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv)); + memcpy(iv + 2 * sizeof(ivv), &(cookie->salt), + sizeof(cookie->salt)); + break; + default: + return NULL; + } + + bzero(&ctx, sizeof(ctx)); + if (!EVP_CipherInit(&ctx, cipher, key, iv, do_enc)) + return NULL; + + EVP_CIPHER_CTX_set_padding(&ctx, do_enc); + + bs = EVP_CIPHER_block_size(cipher); + /* Maximum output size */ + *outlen = pdulen + (bs - (pdulen % bs)); + + if ((outtext = malloc(*outlen)) == NULL) + return NULL; + + if (EVP_CipherUpdate(&ctx, outtext, &len, serialpdu, pdulen) && + EVP_CipherFinal_ex(&ctx, outtext + len, &len2)) + *outlen = len + len2; + else { + free(outtext); + outtext = NULL; + } + + EVP_CIPHER_CTX_cleanup(&ctx); + + return outtext; +} + static int usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen, size_t secparamsoffset, void *cookie) @@ -215,17 +320,18 @@ usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen, static int usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, - off_t secparamsoffset, char *buf, size_t buflen, uint8_t level) + off_t secparamsoffset, char *buf, size_t buflen, uint8_t level, + void **cookie) { struct usm_sec *usm = agent->v3->sec->data; struct ber ber; struct ber_element *secparams; - char *engineid, *user, *digest; - size_t engineidlen, userlen, digestlen; + char *engineid, *user, *digest, *salt; + size_t engineidlen, userlen, digestlen, saltlen; struct timespec now, timediff; off_t digestoffset; char exp_digest[EVP_MAX_MD_SIZE]; - uint32_t boots, time; + struct usm_cookie *usmcookie; bzero(&ber, sizeof(ber)); bzero(exp_digest, sizeof(exp_digest)); @@ -236,10 +342,17 @@ usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, return -1; ber_free(&ber); - if (ber_scanf_elements(secparams, "{xddxpxS}", &engineid, &engineidlen, - &boots, &time, &user, &userlen, &digestoffset, &digest, - &digestlen) == -1) + if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL) goto fail; + *cookie = usmcookie; + + if (ber_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen, + &(usmcookie->boots), &(usmcookie->time), &user, &userlen, + &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1) + goto fail; + if (saltlen != sizeof(usmcookie->salt) && saltlen != 0) + goto fail; + memcpy(&(usmcookie->salt), salt, saltlen); if (!usm->engineidset) { if (usm_setengineid(agent->v3->sec, engineid, @@ -253,12 +366,12 @@ usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, } if (!usm->bootsset) { - usm->boots = boots; + usm->boots = usmcookie->boots; usm->bootsset = 1; } else { - if (boots < usm->boots) + if (usmcookie->boots < usm->boots) goto fail; - if (boots > usm->boots) { + if (usmcookie->boots > usm->boots) { usm->bootsset = 0; usm->timeset = 0; usm_doinit(agent); @@ -267,7 +380,7 @@ usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, } if (!usm->timeset) { - usm->time = time; + usm->time = usmcookie->time; if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1) goto fail; usm->timeset = 1; @@ -275,8 +388,10 @@ usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) goto fail; timespecsub(&now, &(usm->timecheck), &timediff); - if (time < usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW || - time > usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) { + if (usmcookie->time < + usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW || + usmcookie->time > + usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) { usm->bootsset = 0; usm->timeset = 0; usm_doinit(agent); @@ -308,10 +423,35 @@ usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, return 0; fail: + free(usmcookie); ber_free_element(secparams); return -1; } +struct ber_element * +usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie) +{ + struct usm_sec *usm = agent->v3->sec->data; + struct usm_cookie *usmcookie = cookie; + struct ber ber; + struct ber_element *scopedpdu; + char *rawpdu; + size_t rawpdulen; + + if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie, + encpdu, encpdulen, &rawpdulen)) == NULL) + return NULL; + + bzero(&ber, sizeof(ber)); + ber_set_application(&ber, smi_application); + ber_set_readbuf(&ber, rawpdu, rawpdulen); + scopedpdu = ber_read_elements(&ber, NULL); + ber_free(&ber); + free(rawpdu); + + return scopedpdu; +} + static void usm_digest_pos(void *data, size_t offset) { @@ -327,6 +467,7 @@ usm_free(void *data) free(usm->user); free(usm->authkey); + free(usm->privkey); free(usm->engineid); free(usm); } @@ -365,6 +506,43 @@ usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key, } int +usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key, + size_t keylen, enum usm_key_level level) +{ + struct usm_sec *usm = sec->data; + char *lkey; + + if (usm->digest == NULL) { + errno = EINVAL; + return -1; + } + + /* + * We could transform a master key to a local key here if we already + * have usm_setengineid called. Sine snmpc.c is the only caller at + * the moment there's no need, since it always calls us first. + */ + if (level == USM_KEY_PASSWORD) { + if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL) + return -1; + level = USM_KEY_MASTER; + keylen = EVP_MD_size(usm->digest); + } else { + if (keylen != (size_t)EVP_MD_size(usm->digest)) { + errno = EINVAL; + return -1; + } + if ((lkey = malloc(keylen)) == NULL) + return -1; + memcpy(lkey, key, keylen); + usm->privkey = lkey; + } + usm->cipher = cipher; + usm->privlevel = level; + return 0; +} + +int usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen) { struct usm_sec *usm = sec->data; @@ -388,6 +566,16 @@ usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen) free(mkey); usm->authlevel = USM_KEY_LOCALIZED; } + if (usm->privlevel == USM_KEY_MASTER) { + mkey = usm->privkey; + if ((usm->privkey = usm_mkey2lkey(usm, usm->digest, + mkey)) == NULL) { + usm->privkey = mkey; + return -1; + } + free(mkey); + usm->privlevel = USM_KEY_LOCALIZED; + } return 0; } diff --git a/usr.bin/snmp/usm.h b/usr.bin/snmp/usm.h index 4506ed4c341..7fe44866d52 100644 --- a/usr.bin/snmp/usm.h +++ b/usr.bin/snmp/usm.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usm.h,v 1.2 2019/09/18 09:52:47 martijn Exp $ */ +/* $OpenBSD: usm.h,v 1.3 2019/09/18 09:54:36 martijn Exp $ */ /* * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> @@ -28,5 +28,7 @@ enum usm_key_level { struct snmp_sec *usm_init(const char *, size_t); int usm_setauth(struct snmp_sec *, const EVP_MD *, const char *, size_t, enum usm_key_level); +int usm_setpriv(struct snmp_sec *, const EVP_CIPHER *, const char *, size_t, + enum usm_key_level); int usm_setengineid(struct snmp_sec *, char *, size_t); int usm_setbootstime(struct snmp_sec *, uint32_t, uint32_t); |