This diff needs the following change on top to preserve authNoPriv: --- usm.c.old Thu Sep 12 21:41:14 2019 +++ usm.c Thu Sep 12 21:41:39 2019 @@ -185,7 +185,7 @@ usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user, usm->userlen, digest, agent->v3->level & SNMP_MSGFLAG_AUTH ? usm_digestlen(usm->digest) : (size_t) 0, &(usmcookie->salt), - agent->v3->level & SNMP_MSGFLAG_AUTH ? sizeof(usmcookie->salt) : + agent->v3->level & SNMP_MSGFLAG_PRIV ? sizeof(usmcookie->salt) : (size_t) 0)) == NULL) { free(usmcookie); return NULL;
On 9/2/19 9:13 AM, Martijn van Duren wrote: > This diff adds support for authPriv. > > diff --git a/snmp.1 b/snmp.1 > index e810560..fe283a5 100644 > --- a/snmp.1 > +++ b/snmp.1 > @@ -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/snmp.c b/snmp.c > index 57b40e3..ba4dc3c 100644 > --- a/snmp.c > +++ b/snmp.c > @@ -357,7 +357,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, *buf, *packet = NULL; > @@ -400,6 +400,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, 1472, &(agent->v3->level), > (size_t) 1, agent->v3->sec->model, securityparams, > @@ -449,8 +456,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); > @@ -482,9 +490,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; > @@ -502,11 +520,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/snmp.h b/snmp.h > index df20f0d..ce62119 100644 > --- a/snmp.h > +++ b/snmp.h > @@ -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/snmpc.c b/snmpc.c > index edaa50c..658ca82 100644 > --- a/snmpc.c > +++ b/snmpc.c > @@ -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 (strcmp(optarg, "authNoPriv") == 0) > seclevel = SNMP_MSGFLAG_AUTH | > SNMP_MSGFLAG_REPORT; > + else if (strcmp(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/usm.c b/usm.c > index df9a53d..ba2020c 100644 > --- a/usm.c > +++ b/usm.c > @@ -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; > @@ -143,7 +156,6 @@ usm_genparams(struct snmp_agent *agent, size_t *len, void > **cookie) > ssize_t berlen; > struct usm_cookie *usmcookie; > struct timespec now, timediff; > - uint32_t boots, time; > > bzero(digest, sizeof(digest)); > > @@ -151,21 +163,24 @@ 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 ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid, > - usm->engineidlen, boots, time, usm->user, usm->userlen, digest, > - agent->v3->level & SNMP_MSGFLAG_AUTH ? usm_digestlen(usm->digest) : > - (size_t) 0, NULL, (size_t) 0)) == NULL) { > + usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user, > + usm->userlen, digest, agent->v3->level & SNMP_MSGFLAG_AUTH ? > + usm_digestlen(usm->digest) : (size_t) 0, &(usmcookie->salt), > + agent->v3->level & SNMP_MSGFLAG_AUTH ? sizeof(usmcookie->salt) : > + (size_t) 0)) == NULL) { > free(usmcookie); > return NULL; > } > @@ -190,6 +205,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; > + 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); > + > + *outlen = EVP_CIPHER_block_size(cipher); > + /* Maximum output size */ > + *outlen = pdulen + (*outlen - (pdulen % *outlen)); > + > + 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 +317,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 +339,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 +363,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 +377,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 +385,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 +420,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 +464,7 @@ usm_free(void *data) > > free(usm->user); > free(usm->authkey); > + free(usm->privkey); > free(usm->engineid); > free(usm); > } > @@ -364,6 +502,43 @@ usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, > const char *key, > return 0; > } > > +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) > { > @@ -388,6 +563,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/usm.h b/usm.h > index 636cd0e..b1aea8b 100644 > --- a/usm.h > +++ b/usm.h > @@ -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); >