summaryrefslogtreecommitdiff
path: root/usr.bin/snmp
diff options
context:
space:
mode:
authorMartijn van Duren <martijn@cvs.openbsd.org>2019-09-18 09:54:37 +0000
committerMartijn van Duren <martijn@cvs.openbsd.org>2019-09-18 09:54:37 +0000
commit6124587d3ce2e2fe998f4c7c05ad3b1bd0d89474 (patch)
treefe9b898310c809866d16a36a3e3731d9a4336dd0 /usr.bin/snmp
parent4ef48e0cd179f9842390d743e3bea521f30642a8 (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.155
-rw-r--r--usr.bin/snmp/snmp.c33
-rw-r--r--usr.bin/snmp/snmp.h8
-rw-r--r--usr.bin/snmp/snmpc.c51
-rw-r--r--usr.bin/snmp/usm.c232
-rw-r--r--usr.bin/snmp/usm.h4
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);