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

Reply via email to