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);
>