This diff adds support for authNoPriv.
diff --git a/Makefile b/Makefile
index 9eb684b..102582b 100644
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,8 @@
PROG= snmp
SRCS= mib.c smi.c snmp.c snmpc.c usm.c
-LDADD+= -lutil
-DPADD+= ${LIBUTIL}
+LDADD+= -lcrypto -lutil
+DPADD+= ${LIBCRYPTO} ${LIBUTIL}
MAN= snmp.1
diff --git a/snmp.1 b/snmp.1
index 523fd16..e810560 100644
--- a/snmp.1
+++ b/snmp.1
@@ -23,9 +23,13 @@
.Sh SYNOPSIS
.Nm
.Cm get | getnext
+.Op Fl A Ar authpass
+.Op Fl a Ar digest
.Op Fl c Ar community
.Op Fl E Ar ctxengineid
.Op Fl e Ar secengineid
+.Op Fl k Ar localauth
+.Op Fl l Ar seclevel
.Op Fl n Ar ctxname
.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
@@ -37,9 +41,13 @@
.Ar oid ...
.Nm
.Cm walk
+.Op Fl A Ar authpass
+.Op Fl a Ar digest
.Op Fl c Ar community
.Op Fl E Ar ctxengineid
.Op Fl e Ar secengineid
+.Op Fl k Ar localauth
+.Op Fl l Ar seclevel
.Op Fl n Ar ctxname
.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
@@ -53,9 +61,13 @@
.Op Ar oid
.Nm
.Cm bulkget
+.Op Fl A Ar authpass
+.Op Fl a Ar digest
.Op Fl c Ar community
.Op Fl E Ar ctxengineid
.Op Fl e Ar secengineid
+.Op Fl k Ar localauth
+.Op Fl l Ar seclevel
.Op Fl n Ar ctxname
.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
@@ -68,9 +80,13 @@
.Ar oid ...
.Nm
.Cm bulkwalk
+.Op Fl A Ar authpass
+.Op Fl a Ar digest
.Op Fl c Ar community
.Op Fl E Ar ctxengineid
.Op Fl e Ar secengineid
+.Op Fl k Ar localauth
+.Op Fl l Ar seclevel
.Op Fl n Ar ctxname
.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
@@ -83,9 +99,13 @@
.Op Ar oid
.Nm
.Cm trap
+.Op Fl A Ar authpass
+.Op Fl a Ar digest
.Op Fl c Ar community
.Op Fl E Ar ctxengineid
.Op Fl e Ar secengineid
+.Op Fl k Ar localauth
+.Op Fl l Ar seclevel
.Op Fl n Ar ctxname
.Op Fl r Ar retries
.Op Fl t Ar timeout
@@ -170,6 +190,28 @@ Dump the tree of compiled-in MIB objects.
.Pp
The options are as follows:
.Bl -tag -width Ds
+.It Fl A Ar authpass
+The authentication password for the user.
+This will be transformed to
+.Ar localauth .
+This option is only used by
+.Fl v Cm 3 .
+.It Fl a Ar digest
+Set the digest
+.Pq authentication
+protocol.
+Options are
+.Cm MD5 ,
+.Cm SHA ,
+.Cm SHA-224 ,
+.Cm SHA-256 ,
+.Cm SHA-384
+or
+.Cm SHA-512 .
+This option defaults to
+.Cm MD5 .
+This option is only used by
+.Fl v Cm 3 .
.It Fl C Ar appopt
Set the application specific
.Ar appopt
@@ -260,6 +302,28 @@ 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 localauth
+The localized authentication 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 l Ar seclevel
+The security level.
+Values can be
+.Cm noAuthNoPriv Pq default
+or
+.Cm authNoPriv
+.Po
+requires either
+.Fl A
+or
+.Fl k
+.Pc .
+This option is only used by
+.Fl v Cm 3 .
.It Fl n Ar ctxname
Sets the context name.
Defaults to an empty string.
diff --git a/snmp.c b/snmp.c
index 7165976..57b40e3 100644
--- a/snmp.c
+++ b/snmp.c
@@ -37,6 +37,7 @@ static char *
static struct ber_element *
snmp_unpackage(struct snmp_agent *, char *, size_t);
static void snmp_v3_free(struct snmp_v3 *);
+static void snmp_v3_secparamsoffset(void *, size_t);
struct snmp_v3 *
snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
@@ -356,10 +357,12 @@ static char *
snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
{
struct ber ber;
- struct ber_element *message, *scopedpdu = NULL;
+ struct ber_element *message, *scopedpdu = NULL, *secparams;
ssize_t securitysize, ret;
+ size_t secparamsoffset;
char *securityparams = NULL, *buf, *packet = NULL;
long long msgid;
+ void *cookie = NULL;
bzero(&ber, sizeof(ber));
ber_set_application(&ber, smi_application);
@@ -393,7 +396,7 @@ snmp_package(struct snmp_agent *agent, struct ber_element
*pdu, size_t *len)
}
pdu = NULL;
if ((securityparams = agent->v3->sec->genparams(agent,
- &securitysize)) == NULL) {
+ &securitysize, &cookie)) == NULL) {
ber_free_elements(scopedpdu);
goto fail;
}
@@ -402,6 +405,10 @@ snmp_package(struct snmp_agent *agent, struct ber_element
*pdu, size_t *len)
(size_t) 1, agent->v3->sec->model, securityparams,
securitysize, scopedpdu) == NULL)
goto fail;
+ if (ber_scanf_elements(message, "{SSe", &secparams) == -1)
+ goto fail;
+ ber_set_writecallback(secparams, snmp_v3_secparamsoffset,
+ &secparamsoffset);
break;
}
@@ -413,7 +420,17 @@ snmp_package(struct snmp_agent *agent, struct ber_element
*pdu, size_t *len)
memcpy(packet, buf, ret);
ber_free(&ber);
+ if (agent->version == SNMP_V3 && packet != NULL) {
+ if (agent->v3->sec->finalparams(agent, packet,
+ ret, secparamsoffset, cookie) == -1) {
+ free(packet);
+ packet = NULL;
+ }
+ }
+
fail:
+ if (agent->version == SNMP_V3)
+ agent->v3->sec->freecookie(cookie);
ber_free_elements(message);
free(securityparams);
return packet;
@@ -493,3 +510,11 @@ fail:
ber_free_elements(message);
return NULL;
}
+
+static void
+snmp_v3_secparamsoffset(void *cookie, size_t offset)
+{
+ size_t *spoffset = cookie;
+
+ *spoffset = offset;
+}
diff --git a/snmp.h b/snmp.h
index 1cfd8aa..df20f0d 100644
--- a/snmp.h
+++ b/snmp.h
@@ -113,10 +113,12 @@ struct snmp_agent;
struct snmp_sec {
enum snmp_security_model model;
int (*init)(struct snmp_agent *);
- char *(*genparams)(struct snmp_agent *, size_t *);
+ char *(*genparams)(struct snmp_agent *, size_t *, 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);
void (*free)(void *);
+ void (*freecookie)(void *);
void *data;
};
diff --git a/snmpc.c b/snmpc.c
index 4c09405..edaa50c 100644
--- a/snmpc.c
+++ b/snmpc.c
@@ -23,6 +23,7 @@
#include <sys/un.h>
#include <arpa/inet.h>
+#include <openssl/evp.h>
#include <ber.h>
#include <ctype.h>
@@ -41,7 +42,7 @@
#include "snmp.h"
#include "usm.h"
-#define GETOPT_COMMON "c:E:e:n:O:r:t:u:v:Z:"
+#define GETOPT_COMMON "A:a:c:E:e:k:l:n:O:r:t:u:v:Z:"
int snmpc_get(int, char *[]);
int snmpc_walk(int, char *[]);
@@ -96,8 +97,12 @@ enum smi_output_string output_string = smi_os_default;
int
main(int argc, char *argv[])
{
+ const EVP_MD *md = NULL;
struct snmp_sec *sec;
char *user = NULL;
+ enum usm_key_level authkeylevel;
+ char *authkey = NULL;
+ size_t authkeylen = 0;
int seclevel = SNMP_MSGFLAG_REPORT;
char *ctxname = NULL;
char *ctxengineid = NULL, *secengineid = NULL;
@@ -143,6 +148,28 @@ main(int argc, char *argv[])
while ((ch = getopt(argc, argv, optstr)) != -1) {
switch (ch) {
+ case 'A':
+ authkey = optarg;
+ authkeylen = strlen(authkey);
+ authkeylevel = USM_KEY_PASSWORD;
+ break;
+ case 'a':
+ if (strcasecmp(optarg, "MD5") == 0)
+ md = EVP_md5();
+ else if (strcasecmp(optarg, "SHA") == 0)
+ md = EVP_sha1();
+ else if (strcasecmp(optarg, "SHA-224") == 0)
+ md = EVP_sha224();
+ else if (strcasecmp(optarg, "SHA-256") == 0)
+ md = EVP_sha256();
+ else if (strcasecmp(optarg, "SHA-384") == 0)
+ md = EVP_sha384();
+ else if (strcasecmp(optarg, "SHA-512") == 0)
+ md = EVP_sha512();
+ else
+ errx(1, "Invalid authentication protocol "
+ "specified after -a flag: %s", optarg);
+ break;
case 'c':
community = optarg;
break;
@@ -166,6 +193,25 @@ main(int argc, char *argv[])
err(1, "-3e");
}
break;
+ case 'k':
+ authkey = snmpc_hex2bin(optarg, &authkeylen);
+ if (authkey == NULL) {
+ if (errno == EINVAL)
+ errx(1, "Bad key value after -k flag.");
+ err(1, "-k");
+ }
+ authkeylevel = USM_KEY_LOCALIZED;
+ break;
+ case 'l':
+ if (strcmp(optarg, "noAuthNoPriv") == 0)
+ seclevel = SNMP_MSGFLAG_REPORT;
+ else if (strcmp(optarg, "authNoPriv") == 0)
+ seclevel = SNMP_MSGFLAG_AUTH |
+ SNMP_MSGFLAG_REPORT;
+ else
+ errx(1, "Invalid security level specified "
+ "after -l flag: %s", optarg);
+ break;
case 'n':
ctxname = optarg;
break;
@@ -348,6 +394,15 @@ main(int argc, char *argv[])
errx(1, "No securityName specified");
if ((sec = usm_init(user, strlen(user))) == NULL)
err(1, "usm_init");
+ if (seclevel & SNMP_MSGFLAG_AUTH) {
+ if (md == NULL)
+ md = EVP_md5();
+ if (authkey == NULL)
+ errx(1, "No authKey or authPassword specified");
+ if (usm_setauth(sec, md, authkey, authkeylen,
+ authkeylevel) == -1)
+ err(1, "Can't set authkey");
+ }
if (secengineid != NULL) {
if (usm_setengineid(sec, secengineid,
secengineidlen) == -1)
@@ -1031,7 +1086,8 @@ usage(void)
fprintf(stderr, "usage: snmp %s%s%s\n",
snmp_app->name,
snmp_app->usecommonopt ?
- " [-c community] [-e secengineid] [-E ctxengineid] [-n
ctxname]\n"
+ " [-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"
" " : "",
diff --git a/usm.c b/usm.c
index c51cf7d..df9a53d 100644
--- a/usm.c
+++ b/usm.c
@@ -41,6 +41,9 @@ struct usm_sec {
int engineidset;
char *engineid;
size_t engineidlen;
+ enum usm_key_level authlevel;
+ const EVP_MD *digest;
+ char *authkey;
int bootsset;
uint32_t boots;
int timeset;
@@ -48,11 +51,20 @@ struct usm_sec {
struct timespec timecheck;
};
+struct usm_cookie {
+ size_t digestoffset;
+};
+
static int usm_doinit(struct snmp_agent *);
-static char *usm_genparams(struct snmp_agent *, size_t *);
+static char *usm_genparams(struct snmp_agent *, size_t *, void **);
+static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void
*);
static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
size_t, uint8_t);
+static void usm_digest_pos(void *, size_t);
static void usm_free(void *);
+static char *usm_passwd2mkey(const EVP_MD *, const char *);
+static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
+static size_t usm_digestlen(const EVP_MD *);
struct snmp_sec *
usm_init(const char *user, size_t userlen)
@@ -84,7 +96,9 @@ usm_init(const char *user, size_t userlen)
sec->init = usm_doinit;
sec->genparams = usm_genparams;
sec->parseparams = usm_parseparams;
+ sec->finalparams = usm_finalparams;
sec->free = usm_free;
+ sec->freecookie = free;
sec->data = usm;
return sec;
}
@@ -119,19 +133,29 @@ usm_doinit(struct snmp_agent *agent)
}
static char *
-usm_genparams(struct snmp_agent *agent, size_t *len)
+usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
{
struct ber ber;
- struct ber_element *params;
+ struct ber_element *params, *digestelm;
struct usm_sec *usm = agent->v3->sec->data;
+ char digest[USM_MAX_DIGESTLEN];
char *secparams = NULL, *buf;
ssize_t berlen;
+ struct usm_cookie *usmcookie;
struct timespec now, timediff;
uint32_t boots, time;
+ bzero(digest, sizeof(digest));
+
+ if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
+ return NULL;
+ *cookie = usmcookie;
+
if (usm->timeset) {
- if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
+ free(usmcookie);
return NULL;
+ }
timespecsub(&now, &(usm->timecheck), &timediff);
time = usm->time + timediff.tv_sec;
} else
@@ -139,9 +163,20 @@ usm_genparams(struct snmp_agent *agent, size_t *len)
boots = usm->boots;
if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid,
- usm->engineidlen, boots, time, usm->user, usm->userlen, NULL,
- (size_t) 0, NULL, (size_t) 0)) == NULL)
+ 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) {
+ free(usmcookie);
return NULL;
+ }
+
+ if (ber_scanf_elements(params, "{SSSSe", &digestelm) == -1) {
+ ber_free_element(params);
+ free(usmcookie);
+ return NULL;
+ }
+
+ ber_set_writecallback(digestelm, usm_digest_pos, usmcookie);
bzero(&ber, sizeof(ber));
ber_set_application(&ber, smi_application);
@@ -155,6 +190,29 @@ usm_genparams(struct snmp_agent *agent, size_t *len)
return secparams;
}
+static int
+usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
+ size_t secparamsoffset, void *cookie)
+{
+ struct usm_sec *usm = agent->v3->sec->data;
+ struct usm_cookie *usmcookie = cookie;
+ u_char digest[EVP_MAX_MD_SIZE];
+
+ if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
+ return 0;
+
+ if (usm->authlevel != USM_KEY_LOCALIZED)
+ return -1;
+
+ if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
+ buflen, digest, NULL) == NULL)
+ return -1;
+
+ memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
+ usm_digestlen(usm->digest));
+ return 0;
+}
+
static int
usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
off_t secparamsoffset, char *buf, size_t buflen, uint8_t level)
@@ -162,12 +220,15 @@ usm_parseparams(struct snmp_agent *agent, char *packet,
size_t packetlen,
struct usm_sec *usm = agent->v3->sec->data;
struct ber ber;
struct ber_element *secparams;
- char *engineid, *user;
- size_t engineidlen, userlen;
+ char *engineid, *user, *digest;
+ size_t engineidlen, userlen, digestlen;
struct timespec now, timediff;
+ off_t digestoffset;
+ char exp_digest[EVP_MAX_MD_SIZE];
uint32_t boots, time;
bzero(&ber, sizeof(ber));
+ bzero(exp_digest, sizeof(exp_digest));
ber_set_application(&ber, smi_application);
ber_set_readbuf(&ber, buf, buflen);
@@ -175,8 +236,9 @@ usm_parseparams(struct snmp_agent *agent, char *packet,
size_t packetlen,
return -1;
ber_free(&ber);
- if (ber_scanf_elements(secparams, "{xddxSS}", &engineid, &engineidlen,
- &boots, &time, &user, &userlen) == -1)
+ if (ber_scanf_elements(secparams, "{xddxpxS}", &engineid, &engineidlen,
+ &boots, &time, &user, &userlen, &digestoffset, &digest,
+ &digestlen) == -1)
goto fail;
if (!usm->engineidset) {
@@ -226,6 +288,22 @@ usm_parseparams(struct snmp_agent *agent, char *packet,
size_t packetlen,
memcmp(user, usm->user, userlen) != 0)
goto fail;
+ if (level & SNMP_MSGFLAG_AUTH) {
+ if (digestlen != usm_digestlen(usm->digest))
+ goto fail;
+ }
+ if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
+ bzero(packet + secparamsoffset + digestoffset, digestlen);
+ if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest),
packet,
+ packetlen, exp_digest, NULL) == NULL)
+ goto fail;
+
+ if (memcmp(exp_digest, digest, digestlen) != 0)
+ goto fail;
+ } else
+ if (digestlen != 0)
+ goto fail;
+
ber_free_element(secparams);
return 0;
@@ -234,20 +312,63 @@ fail:
return -1;
}
+static void
+usm_digest_pos(void *data, size_t offset)
+{
+ struct usm_cookie *usmcookie = data;
+
+ usmcookie->digestoffset = offset;
+}
+
static void
usm_free(void *data)
{
struct usm_sec *usm = data;
free(usm->user);
+ free(usm->authkey);
free(usm->engineid);
free(usm);
}
+int
+usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
+ size_t keylen, enum usm_key_level level)
+{
+ struct usm_sec *usm = sec->data;
+ char *lkey;
+
+ /*
+ * 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 this function
+ * first.
+ */
+ if (level == USM_KEY_PASSWORD) {
+ if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
+ return -1;
+ level = USM_KEY_MASTER;
+ keylen = EVP_MD_size(digest);
+ } else {
+ if (keylen != (size_t)EVP_MD_size(digest)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((lkey = malloc(keylen)) == NULL)
+ return -1;
+ memcpy(lkey, key, keylen);
+ usm->authkey = lkey;
+ }
+ usm->digest = digest;
+ usm->authlevel = level;
+ return 0;
+}
+
int
usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
{
struct usm_sec *usm = sec->data;
+ char *mkey;
if (usm->engineid != NULL)
free(usm->engineid);
@@ -257,6 +378,17 @@ usm_setengineid(struct snmp_sec *sec, char *engineid,
size_t engineidlen)
usm->engineidlen = engineidlen;
usm->engineidset = 1;
+ if (usm->authlevel == USM_KEY_MASTER) {
+ mkey = usm->authkey;
+ if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
+ mkey)) == NULL) {
+ usm->authkey = mkey;
+ return -1;
+ }
+ free(mkey);
+ usm->authlevel = USM_KEY_LOCALIZED;
+ }
+
return 0;
}
@@ -274,3 +406,81 @@ usm_setbootstime(struct snmp_sec *sec, uint32_t boots,
uint32_t time)
usm->timeset = 1;
return 0;
}
+
+static char *
+usm_passwd2mkey(const EVP_MD *md, const char *passwd)
+{
+ EVP_MD_CTX ctx;
+ int i, count;
+ const u_char *pw;
+ u_char *c;
+ u_char keybuf[EVP_MAX_MD_SIZE > 64 ? EVP_MAX_MD_SIZE : 64];
+ unsigned dlen;
+ char *key;
+
+ bzero(&ctx, sizeof(ctx));
+ EVP_DigestInit_ex(&ctx, md, NULL);
+ pw = (const u_char *)passwd;
+ for (count = 0; count < 1048576; count += 64) {
+ c = keybuf;
+ for (i = 0; i < 64; i++) {
+ if (*pw == '\0')
+ pw = (const u_char *)passwd;
+ *c++ = *pw++;
+ }
+ EVP_DigestUpdate(&ctx, keybuf, 64);
+ }
+ EVP_DigestFinal_ex(&ctx, keybuf, &dlen);
+ EVP_MD_CTX_cleanup(&ctx);
+
+ if ((key = malloc(dlen)) == NULL)
+ return NULL;
+ memcpy(key, keybuf, dlen);
+ return key;
+}
+
+static char *
+usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
+{
+ EVP_MD_CTX ctx;
+ u_char buf[EVP_MAX_MD_SIZE];
+ u_char *lkey;
+ unsigned lklen;
+
+
+ bzero(&ctx, sizeof(ctx));
+ EVP_DigestInit_ex(&ctx, md, NULL);
+
+ EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
+ EVP_DigestUpdate(&ctx, usm->engineid, usm->engineidlen);
+ EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
+
+ EVP_DigestFinal_ex(&ctx, buf, &lklen);
+ EVP_MD_CTX_cleanup(&ctx);
+
+ if ((lkey = malloc(lklen)) == NULL)
+ return NULL;
+ memcpy(lkey, buf, lklen);
+ return lkey;
+}
+
+static size_t
+usm_digestlen(const EVP_MD *md)
+{
+ switch (EVP_MD_type(md)) {
+ case NID_md5:
+ case NID_sha1:
+ return 12;
+ case NID_sha224:
+ return 16;
+ case NID_sha256:
+ return 24;
+ case NID_sha384:
+ return 32;
+ case NID_sha512:
+ return 48;
+ default:
+ return 0;
+
+ }
+}
diff --git a/usm.h b/usm.h
index 1def439..636cd0e 100644
--- a/usm.h
+++ b/usm.h
@@ -18,6 +18,15 @@
#include "snmp.h"
+enum usm_key_level {
+ USM_KEY_UNSET = 0,
+ USM_KEY_PASSWORD,
+ USM_KEY_MASTER,
+ USM_KEY_LOCALIZED
+};
+
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_setengineid(struct snmp_sec *, char *, size_t);
int usm_setbootstime(struct snmp_sec *, uint32_t, uint32_t);