summaryrefslogtreecommitdiffstats
path: root/usr.bin/snmp
diff options
context:
space:
mode:
authormartijn <martijn@openbsd.org>2019-09-18 09:48:14 +0000
committermartijn <martijn@openbsd.org>2019-09-18 09:48:14 +0000
commitb89ba26fac84ada547bc29af5c17533ab27b2597 (patch)
tree9d2c8f4294f0423952e6b9d446fe60e881015a84 /usr.bin/snmp
parentMove snmp packaging and unpackaging to their own function. (diff)
downloadwireguard-openbsd-b89ba26fac84ada547bc29af5c17533ab27b2597.tar.xz
wireguard-openbsd-b89ba26fac84ada547bc29af5c17533ab27b2597.zip
Initial SNMPv3/USM support.
This commit only implements noAuthNoPriv. Feedback and OK jmatthew@
Diffstat (limited to 'usr.bin/snmp')
-rw-r--r--usr.bin/snmp/Makefile4
-rw-r--r--usr.bin/snmp/snmp.175
-rw-r--r--usr.bin/snmp/snmp.c156
-rw-r--r--usr.bin/snmp/snmp.h34
-rw-r--r--usr.bin/snmp/snmpc.c173
-rw-r--r--usr.bin/snmp/usm.c275
-rw-r--r--usr.bin/snmp/usm.h23
7 files changed, 699 insertions, 41 deletions
diff --git a/usr.bin/snmp/Makefile b/usr.bin/snmp/Makefile
index 62bb5565a30..7fef458a60d 100644
--- a/usr.bin/snmp/Makefile
+++ b/usr.bin/snmp/Makefile
@@ -1,7 +1,7 @@
-# $OpenBSD: Makefile,v 1.1 2019/08/09 06:17:59 martijn Exp $
+# $OpenBSD: Makefile,v 1.2 2019/09/18 09:48:14 martijn Exp $
PROG= snmp
-SRCS= mib.c smi.c snmp.c snmpc.c
+SRCS= mib.c smi.c snmp.c snmpc.c usm.c
LDADD+= -lutil
DPADD+= ${LIBUTIL}
diff --git a/usr.bin/snmp/snmp.1 b/usr.bin/snmp/snmp.1
index e158ba00048..95eb26b7a45 100644
--- a/usr.bin/snmp/snmp.1
+++ b/usr.bin/snmp/snmp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: snmp.1,v 1.3 2019/08/14 14:40:23 deraadt Exp $
+.\" $OpenBSD: snmp.1,v 1.4 2019/09/18 09:48:14 martijn Exp $
.\"
.\" Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: August 14 2019 $
+.Dd $Mdocdate: September 18 2019 $
.Dt SNMP 1
.Os
.Sh NAME
@@ -24,19 +24,29 @@
.Nm
.Cm get | getnext
.Op Fl c Ar community
+.Op Fl E Ar ctxengineid
+.Op Fl e Ar secengineid
+.Op Fl n Ar ctxname
+.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
.Op Fl t Ar timeout
+.Op Fl u Ar user
.Op Fl v Ar version
-.Op Fl O Cm afnQqSvx
+.Op Fl Z Ar boots , Ns Ar time
.Ar agent
.Ar oid ...
.Nm
.Cm walk
.Op Fl c Ar community
+.Op Fl E Ar ctxengineid
+.Op Fl e Ar secengineid
+.Op Fl n Ar ctxname
+.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
.Op Fl t Ar timeout
+.Op Fl u Ar user
.Op Fl v Ar version
-.Op Fl O Cm afnQqSvx
+.Op Fl Z Ar boots , Ns Ar time
.Op Fl C Cm cIipt
.Op Fl C Cm E Ar endoid
.Ar agent
@@ -44,29 +54,44 @@
.Nm
.Cm bulkget
.Op Fl c Ar community
+.Op Fl E Ar ctxengineid
+.Op Fl e Ar secengineid
+.Op Fl n Ar ctxname
+.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
.Op Fl t Ar timeout
+.Op Fl u Ar user
.Op Fl v Ar version
-.Op Fl O Cm afnQqSvx
+.Op Fl Z Ar boots , Ns Ar time
.Op Fl C Cm n Ns Ar nonrep Ns Cm r Ns Ar maxrep
.Ar agent
.Ar oid ...
.Nm
.Cm bulkwalk
.Op Fl c Ar community
+.Op Fl E Ar ctxengineid
+.Op Fl e Ar secengineid
+.Op Fl n Ar ctxname
+.Op Fl O Cm afnQqSvx
.Op Fl r Ar retries
.Op Fl t Ar timeout
+.Op Fl u Ar user
.Op Fl v Ar version
-.Op Fl O Cm afnQqSvx
+.Op Fl Z Ar boots , Ns Ar time
.Op Fl C Cm cipn Ns Ar nonrep Ns Cm r Ns Ar maxrep
.Ar agent
.Op Ar oid
.Nm
.Cm trap
.Op Fl c Ar community
+.Op Fl E Ar ctxengineid
+.Op Fl e Ar secengineid
+.Op Fl n Ar ctxname
.Op Fl r Ar retries
.Op Fl t Ar timeout
+.Op Fl u Ar user
.Op Fl v Ar version
+.Op Fl Z Ar boots , Ns Ar time
.Ar agent uptime trapoid
.Oo Ar varoid type value Oc ...
.Nm
@@ -220,6 +245,26 @@ Set the
string.
Defaults to
.Cm public .
+This option is only used by
+.Fl v Cm 1
+and
+.Fl v Cm 2c .
+.It Fl e Ar secengineid
+The USM security engine id.
+Under normal circumstances this value is discovered via snmpv3 discovery and
+does not need to be specified.
+This option is only used by
+.Fl v Cm 3 .
+.It Fl E Ar ctxengineid
+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 n Ar ctxname
+Sets the context name.
+Defaults to an empty string.
+This option is only used by
+.Fl v Cm 3 .
.It Fl O Ar output
Set the
.Ar output
@@ -256,15 +301,29 @@ Set the
.Ar timeout
to wait for a reply, in seconds.
Defaults to 1.
+.It Fl u Ar user
+Sets the username.
+If
+.Fl v Cm 3
+is used this option is required.
+This option is only used by
+.Fl v Cm 3 .
.It Fl v Ar version
Set the snmp protocol
.Ar version
to either
-.Cm 1
+.Cm 1 ,
+.Cm 2c
or
-.Cm 2c .
+.Cm 3 .
Currently defaults to
.Cm 2c .
+.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
+does not need to be specified.
+This option is only used by
+.Fl v Cm 3 .
.El
.Pp
The syntax for the
diff --git a/usr.bin/snmp/snmp.c b/usr.bin/snmp/snmp.c
index af2e66d82f7..1ccac891b5e 100644
--- a/usr.bin/snmp/snmp.c
+++ b/usr.bin/snmp/snmp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmp.c,v 1.3 2019/09/18 09:44:38 martijn Exp $ */
+/* $OpenBSD: snmp.c,v 1.4 2019/09/18 09:48:14 martijn Exp $ */
/*
* Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
@@ -30,12 +30,55 @@
#include "smi.h"
#include "snmp.h"
+#define UDP_MAXPACKET 65535
+
static struct ber_element *
snmp_resolve(struct snmp_agent *, struct ber_element *, int);
static char *
snmp_package(struct snmp_agent *, struct ber_element *, size_t *);
static struct ber_element *
snmp_unpackage(struct snmp_agent *, char *, size_t);
+static void snmp_v3_free(struct snmp_v3 *);
+
+struct snmp_v3 *
+snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
+ struct snmp_sec *sec)
+{
+ struct snmp_v3 *v3;
+
+ if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
+ sec == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((v3 = calloc(1, sizeof(*v3))) == NULL)
+ return NULL;
+
+ v3->level = level | SNMP_MSGFLAG_REPORT;
+ v3->ctxnamelen = ctxnamelen;
+ if (ctxnamelen != 0) {
+ if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
+ free(v3);
+ return NULL;
+ }
+ memcpy(v3->ctxname, ctxname, ctxnamelen);
+ }
+ v3->sec = sec;
+ return v3;
+}
+
+int
+snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
+{
+ if (v3->engineidset)
+ free(v3->engineid);
+ if ((v3->engineid = malloc(engineidlen)) == NULL)
+ return -1;
+ memcpy(v3->engineid, engineid, engineidlen);
+ v3->engineidlen = engineidlen;
+ v3->engineidset = 1;
+ return 0;
+}
struct snmp_agent *
snmp_connect_v12(int fd, enum snmp_version version, const char *community)
@@ -54,21 +97,54 @@ snmp_connect_v12(int fd, enum snmp_version version, const char *community)
goto fail;
agent->timeout = 1;
agent->retries = 5;
+ agent->v3 = NULL;
return agent;
fail:
- free(agent->community);
free(agent);
return NULL;
}
+struct snmp_agent *
+snmp_connect_v3(int fd, struct snmp_v3 *v3)
+{
+ struct snmp_agent *agent;
+
+ if ((agent = malloc(sizeof(*agent))) == NULL)
+ return NULL;
+ agent->fd = fd;
+ agent->version = SNMP_V3;
+ agent->v3 = v3;
+ agent->timeout = 1;
+ agent->retries = 5;
+ agent->community = NULL;
+
+ if (v3->sec->init(agent) == -1) {
+ snmp_free_agent(agent);
+ return NULL;
+ }
+ return agent;
+}
+
void
snmp_free_agent(struct snmp_agent *agent)
{
free(agent->community);
+ if (agent->v3 != NULL)
+ snmp_v3_free(agent->v3);
free(agent);
}
+static void
+snmp_v3_free(struct snmp_v3 *v3)
+{
+ v3->sec->free(v3->sec->data);
+ free(v3->sec);
+ free(v3->ctxname);
+ free(v3->engineid);
+ free(v3);
+}
+
struct ber_element *
snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
{
@@ -253,7 +329,7 @@ snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
tries--;
continue;
}
- if (rreqid != reqid) {
+ if (rreqid != reqid && rreqid != 0) {
errno = EPROTO;
direction = POLLOUT;
tries--;
@@ -265,7 +341,7 @@ snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
errno = EPROTO;
direction = POLLOUT;
tries--;
- break;
+ continue;
}
}
@@ -282,9 +358,10 @@ static char *
snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
{
struct ber ber;
- struct ber_element *message;
- ssize_t ret;
- char *packet = NULL;
+ struct ber_element *message, *scopedpdu = NULL;
+ ssize_t securitysize, ret;
+ char *securityparams = NULL, *packet = NULL;
+ long long msgid;
bzero(&ber, sizeof(ber));
ber_set_application(&ber, smi_application);
@@ -304,6 +381,29 @@ snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
}
break;
case SNMP_V3:
+ msgid = arc4random_uniform(2147483647);
+ if ((scopedpdu = ber_add_sequence(NULL)) == NULL) {
+ ber_free_elements(pdu);
+ goto fail;
+ }
+ if (ber_printf_elements(scopedpdu, "xxe",
+ agent->v3->engineid, agent->v3->engineidlen,
+ agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
+ ber_free_elements(pdu);
+ ber_free_elements(scopedpdu);
+ goto fail;
+ }
+ pdu = NULL;
+ if ((securityparams = agent->v3->sec->genparams(agent,
+ &securitysize)) == NULL) {
+ ber_free_elements(scopedpdu);
+ goto fail;
+ }
+ if (ber_printf_elements(message, "d{idxd}xe",
+ agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
+ (size_t) 1, agent->v3->sec->model, securityparams,
+ securitysize, scopedpdu) == NULL)
+ goto fail;
break;
}
@@ -316,6 +416,7 @@ snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
fail:
ber_free_elements(message);
+ free(securityparams);
return packet;
}
@@ -326,7 +427,14 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
enum snmp_version version;
char *community;
struct ber_element *pdu;
- struct ber_element *message = NULL, *payload;
+ long long msgid, model;
+ int msgsz;
+ char *msgflags, *secparams;
+ size_t msgflagslen, secparamslen;
+ struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
+ off_t secparamsoffset;
+ char *engineid;
+ size_t engineidlen;
bzero(&ber, sizeof(ber));
ber_set_application(&ber, smi_application);
@@ -342,8 +450,7 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
if (version != agent->version)
goto fail;
- switch (version)
- {
+ switch (version) {
case SNMP_V1:
case SNMP_V2C:
if (ber_scanf_elements(payload, "se", &community, &pdu) == -1)
@@ -354,7 +461,34 @@ snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
ber_free_elements(message);
return pdu;
case SNMP_V3:
- break;
+ if (ber_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
+ &msgflags, &msgflagslen, &model, &secparamsoffset,
+ &secparams, &secparamslen, &scopedpdu) == -1)
+ goto fail;
+ if (msgflagslen != 1)
+ goto fail;
+ if (agent->v3->sec->parseparams(agent, buf, buflen,
+ secparamsoffset, secparams, secparamslen,
+ msgflags[0]) == -1)
+ goto fail;
+ if (ber_scanf_elements(scopedpdu, "{xeS{", &engineid,
+ &engineidlen, &ctxname) == -1)
+ goto fail;
+ if (!agent->v3->engineidset) {
+ if (snmp_v3_setengineid(agent->v3, engineid,
+ engineidlen) == -1)
+ goto fail;
+ }
+ pdu = ber_unlink_elements(ctxname);
+ /* Accept reports, so we can continue if possible */
+ if (pdu->be_type != SNMP_C_REPORT) {
+ if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
+ (agent->v3->level & SNMP_MSGFLAG_SECMASK))
+ goto fail;
+ }
+
+ ber_free_elements(message);
+ return pdu;
}
/* NOTREACHED */
diff --git a/usr.bin/snmp/snmp.h b/usr.bin/snmp/snmp.h
index 81034c95096..4895f4cd3cb 100644
--- a/usr.bin/snmp/snmp.h
+++ b/usr.bin/snmp/snmp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmp.h,v 1.2 2019/09/18 09:44:38 martijn Exp $ */
+/* $OpenBSD: snmp.h,v 1.3 2019/09/18 09:48:14 martijn Exp $ */
/*
* Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
@@ -108,12 +108,37 @@ enum snmp_security_model {
SNMP_SEC_TSM = 4
};
+struct snmp_agent;
+
+struct snmp_sec {
+ enum snmp_security_model model;
+ int (*init)(struct snmp_agent *);
+ char *(*genparams)(struct snmp_agent *, size_t *);
+ int (*parseparams)(struct snmp_agent *, char *, size_t, off_t, char *,
+ size_t, uint8_t);
+ void (*free)(void *);
+ void *data;
+};
+
+struct snmp_v3 {
+ uint8_t level;
+ char *ctxname;
+ size_t ctxnamelen;
+ int engineidset;
+ char *engineid;
+ size_t engineidlen;
+ struct snmp_sec *sec;
+};
+
struct snmp_agent {
int fd;
- enum snmp_version version;
- char *community;
int timeout;
int retries;
+ enum snmp_version version;
+/* SNMP_V1 & SNMP_V2C */
+ char *community;
+/* SNMP_V3 */
+ struct snmp_v3 *v3;
};
#define SNMP_MSGFLAG_AUTH 0x01
@@ -123,7 +148,10 @@ struct snmp_agent {
#define SNMP_MAX_TIMEWINDOW 150 /* RFC3414 */
+struct snmp_v3 *snmp_v3_init(int, const char *, size_t, struct snmp_sec *);
+int snmp_v3_setengineid(struct snmp_v3 *, char *, size_t);
struct snmp_agent *snmp_connect_v12(int, enum snmp_version, const char *);
+struct snmp_agent *snmp_connect_v3(int, struct snmp_v3 *);
void snmp_free_agent(struct snmp_agent *);
struct ber_element *
snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len);
diff --git a/usr.bin/snmp/snmpc.c b/usr.bin/snmp/snmpc.c
index d49c272d6bc..4150d9d11b1 100644
--- a/usr.bin/snmp/snmpc.c
+++ b/usr.bin/snmp/snmpc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmpc.c,v 1.8 2019/09/18 09:44:38 martijn Exp $ */
+/* $OpenBSD: snmpc.c,v 1.9 2019/09/18 09:48:14 martijn Exp $ */
/*
* Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
@@ -25,6 +25,7 @@
#include <arpa/inet.h>
#include <ber.h>
+#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
@@ -38,8 +39,9 @@
#include "smi.h"
#include "snmp.h"
+#include "usm.h"
-#define GETOPT_COMMON "c:r:t:v:O:"
+#define GETOPT_COMMON "c:E:e:n:O:r:t:u:v:Z:"
int snmpc_get(int, char *[]);
int snmpc_walk(int, char *[]);
@@ -49,6 +51,7 @@ struct snmp_agent *snmpc_connect(char *, char *);
int snmpc_parseagent(char *, char *);
int snmpc_print(struct ber_element *);
__dead void snmpc_printerror(enum snmp_error, char *);
+char *snmpc_hex2bin(char *, size_t *);
void usage(void);
struct snmp_app {
@@ -71,10 +74,11 @@ struct snmp_app snmp_apps[] = {
struct snmp_app *snmp_app = NULL;
char *community = "public";
+struct snmp_v3 *v3;
char *mib = "mib_2";
int retries = 5;
int timeout = 1;
-int version = SNMP_V2C;
+enum snmp_version version = SNMP_V2C;
int print_equals = 1;
int print_varbind_only = 0;
int print_summary = 0;
@@ -92,6 +96,14 @@ enum smi_output_string output_string = smi_os_default;
int
main(int argc, char *argv[])
{
+ struct snmp_sec *sec;
+ char *user = NULL;
+ int seclevel = SNMP_MSGFLAG_REPORT;
+ char *ctxname = NULL;
+ char *ctxengineid = NULL, *secengineid = NULL;
+ size_t ctxengineidlen, secengineidlen;
+ int zflag = 0;
+ long long boots, time;
char optstr[BUFSIZ];
const char *errstr;
char *strtolp;
@@ -134,6 +146,29 @@ main(int argc, char *argv[])
case 'c':
community = optarg;
break;
+ case 'E':
+ ctxengineid = snmpc_hex2bin(optarg,
+ &ctxengineidlen);
+ if (ctxengineid == NULL) {
+ if (errno == EINVAL)
+ errx(1, "Bad engine ID value "
+ "after -3E flag.");
+ err(1, "-3E");
+ }
+ break;
+ case 'e':
+ secengineid = snmpc_hex2bin(optarg,
+ &secengineidlen);
+ if (secengineid == NULL) {
+ if (errno == EINVAL)
+ errx(1, "Bad engine ID value "
+ "after -3e flag.");
+ err(1, "-3e");
+ }
+ break;
+ case 'n':
+ ctxname = optarg;
+ break;
case 'r':
if ((retries = strtonum(optarg, 0, INT_MAX,
&errstr)) == 0) {
@@ -148,11 +183,16 @@ main(int argc, char *argv[])
errx(1, "-t: %s argument", errstr);
}
break;
+ case 'u':
+ user = optarg;
+ break;
case 'v':
if (strcmp(optarg, "1") == 0)
version = SNMP_V1;
else if (strcmp(optarg, "2c") == 0)
version = SNMP_V2C;
+ else if (strcmp(optarg, "3") == 0)
+ version = SNMP_V3;
else
errc(1, EINVAL, "-v");
break;
@@ -283,6 +323,18 @@ main(int argc, char *argv[])
}
}
break;
+ case 'Z':
+ boots = strtoll(optarg, &strtolp, 10);
+ if (boots < 0 || strtolp == optarg || strtolp[0] != ',')
+ usage();
+ strtolp++;
+ while (strtolp[0] == ' ' && strtolp[0] == '\t')
+ strtolp++;
+ time = strtoll(strtolp, &strtolp, 10);
+ if (boots < 0 || strtolp == optarg)
+ usage();
+ zflag = 1;
+ break;
default:
usage();
}
@@ -290,6 +342,32 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (version == SNMP_V3) {
+ /* Setup USM */
+ if (user == NULL || user[0] == '\0')
+ errx(1, "No securityName specified");
+ if ((sec = usm_init(user, strlen(user))) == NULL)
+ err(1, "usm_init");
+ if (secengineid != NULL) {
+ if (usm_setengineid(sec, secengineid,
+ secengineidlen) == -1)
+ err(1, "Can't set secengineid");
+ }
+ if (zflag)
+ if (usm_setbootstime(sec, boots, time) == -1)
+ err(1, "Can't set boots/time");
+ v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
+ strlen(ctxname), sec);
+ if (v3 == NULL)
+ err(1, "snmp_v3_init");
+ if (ctxengineid != NULL) {
+ if (snmp_v3_setengineid(v3, ctxengineid,
+ ctxengineidlen) == -1)
+ err(1, "Can't set ctxengineid");
+ }
+ }
+
+
return snmp_app->exec(argc, argv);
}
@@ -301,6 +379,8 @@ snmpc_get(int argc, char *argv[])
struct snmp_agent *agent;
int errorstatus, errorindex;
int i;
+ int class;
+ unsigned type;
if (argc < 2)
usage();
@@ -338,12 +418,14 @@ snmpc_get(int argc, char *argv[])
err(1, "get");
}
- (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus, &errorindex,
- &varbind);
+ (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
+ &errorindex, &varbind);
if (errorstatus != 0)
snmpc_printerror((enum snmp_error) errorstatus,
argv[errorindex - 1]);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ printf("Received report:\n");
for (; varbind != NULL; varbind = varbind->be_next) {
if (!snmpc_print(varbind))
err(1, "Can't print response");
@@ -364,6 +446,8 @@ snmpc_walk(int argc, char *argv[])
char oidstr[SNMP_MAX_OID_STRLEN];
int n = 0, prev_cmp;
int errorstatus, errorindex;
+ int class;
+ unsigned type;
if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
errx(1, "Cannot send V2 PDU on V1 session");
@@ -388,15 +472,19 @@ snmpc_walk(int argc, char *argv[])
if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
err(1, "%s", snmp_app->name);
- (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
- &errorindex, &varbind);
+ (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
+ &errorstatus, &errorindex, &varbind);
if (errorstatus != 0)
snmpc_printerror((enum snmp_error) errorstatus,
argv[errorindex - 1]);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ printf("Received report:\n");
if (!snmpc_print(varbind))
err(1, "Can't print response");
ber_free_element(pdu);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ return 1;
n++;
}
while (1) {
@@ -410,14 +498,16 @@ snmpc_walk(int argc, char *argv[])
err(1, "walk");
}
- (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
- &errorindex, &varbind);
+ (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
+ &errorstatus, &errorindex, &varbind);
if (errorstatus != 0) {
smi_oid2string(&noid, oidstr, sizeof(oidstr),
oid_lookup);
snmpc_printerror((enum snmp_error) errorstatus, oidstr);
}
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ printf("Received report:\n");
for (; varbind != NULL; varbind = varbind->be_next) {
(void) ber_scanf_elements(varbind, "{oe}", &noid,
&value);
@@ -438,6 +528,8 @@ snmpc_walk(int argc, char *argv[])
n++;
}
ber_free_elements(pdu);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ return 1;
if (varbind != NULL)
break;
}
@@ -445,15 +537,19 @@ snmpc_walk(int argc, char *argv[])
if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
err(1, "%s", snmp_app->name);
- (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
- &errorindex, &varbind);
+ (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
+ &errorstatus, &errorindex, &varbind);
if (errorstatus != 0)
snmpc_printerror((enum snmp_error) errorstatus,
argv[errorindex - 1]);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ printf("Received report:\n");
if (!snmpc_print(varbind))
err(1, "Can't print response");
ber_free_element(pdu);
+ if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
+ return 1;
n++;
}
if (print_time)
@@ -697,6 +793,8 @@ snmpc_connect(char *host, char *port)
case SNMP_V2C:
return snmp_connect_v12(snmpc_parseagent(host, port), version,
community);
+ case SNMP_V3:
+ return snmp_connect_v3(snmpc_parseagent(host, port), v3);
}
return NULL;
}
@@ -883,18 +981,60 @@ snmpc_parseagent(char *agent, char *defaultport)
return s;
}
+char *
+snmpc_hex2bin(char *hexstr, size_t *binlen)
+{
+ char *decstr;
+
+ if (hexstr[0] == '0' && hexstr[1] == 'x')
+ hexstr += 2;
+ while (hexstr[0] == ' ' || hexstr[0] == '\t')
+ hexstr++;
+
+ if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
+ return NULL;
+
+ for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
+ hexstr[0] = toupper(hexstr[0]);
+ hexstr[1] = toupper(hexstr[1]);
+ if (hexstr[0] >= '0' && hexstr[0] <= '9')
+ decstr[*binlen] = (hexstr[0] - '0') << 4;
+ else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
+ decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
+ else
+ goto fail;
+ if (hexstr[1] >= '0' && hexstr[1] <= '9')
+ decstr[*binlen] |= (hexstr[1] - '0');
+ else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
+ decstr[*binlen] |= (hexstr[1] - 'A') + 10;
+ else
+ goto fail;
+
+ hexstr += 2;
+ while (hexstr[0] == ' ' || hexstr[0] == '\t')
+ hexstr++;
+ }
+
+ return decstr;
+fail:
+ errno = EINVAL;
+ free(decstr);
+ return NULL;
+}
+
__dead void
usage(void)
{
size_t i;
if (snmp_app != NULL) {
- fprintf(stderr, "usage: snmp %s%s%s%s\n",
+ fprintf(stderr, "usage: snmp %s%s%s\n",
snmp_app->name,
snmp_app->usecommonopt ?
- " [-c community] [-r retries] [-t timeout] [-v version]\n"
- " [-O afnqvxSQ]" : "",
- snmp_app->usage == NULL ? "" : " ",
+ " [-c community] [-e secengineid] [-E ctxengineid] [-n ctxname]\n"
+ " [-O afnqvxSQ] [-r retries] [-t timeout] [-u user] [-v version]\n"
+ " [-Z boots,time]\n"
+ " " : "",
snmp_app->usage == NULL ? "" : snmp_app->usage);
exit(1);
}
@@ -906,8 +1046,7 @@ usage(void)
fprintf(stderr, "snmp %s%s %s\n",
snmp_apps[i].name,
snmp_apps[i].usecommonopt ?
- " [-c community] [-r retries] [-t timeout] [-v version]\n"
- " [-O afnqvxSQ]" : "",
+ " [common options]" : "",
snmp_apps[i].usage ? snmp_apps[i].usage : "");
}
exit(1);
diff --git a/usr.bin/snmp/usm.c b/usr.bin/snmp/usm.c
new file mode 100644
index 00000000000..6fa960797b5
--- /dev/null
+++ b/usr.bin/snmp/usm.c
@@ -0,0 +1,275 @@
+/* $OpenBSD: usm.c,v 1.1 2019/09/18 09:48:14 martijn Exp $ */
+
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/time.h>
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+#include <ber.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include "smi.h"
+#include "snmp.h"
+#include "usm.h"
+
+#define USM_MAX_DIGESTLEN 48
+#define USM_MAX_TIMEWINDOW 150
+#define USM_SALTOFFSET 8
+
+struct usm_sec {
+ struct snmp_sec snmp;
+ char *user;
+ size_t userlen;
+ int engineidset;
+ char *engineid;
+ size_t engineidlen;
+ int bootsset;
+ uint32_t boots;
+ int timeset;
+ uint32_t time;
+ struct timespec timecheck;
+};
+
+static int usm_doinit(struct snmp_agent *);
+static char *usm_genparams(struct snmp_agent *, size_t *);
+static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
+ size_t, uint8_t);
+static void usm_free(void *);
+
+struct snmp_sec *
+usm_init(const char *user, size_t userlen)
+{
+ struct snmp_sec *sec;
+ struct usm_sec *usm;
+
+ if (user == NULL || user[0] == '\0') {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((sec = malloc(sizeof(*sec))) == NULL)
+ return NULL;
+
+ if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
+ free(sec);
+ return NULL;
+ }
+ if ((usm->user = malloc(userlen)) == NULL) {
+ free(sec);
+ free(usm);
+ return NULL;
+ }
+ memcpy(usm->user, user, userlen);
+ usm->userlen = userlen;
+
+ sec->model = SNMP_SEC_USM;
+ sec->init = usm_doinit;
+ sec->genparams = usm_genparams;
+ sec->parseparams = usm_parseparams;
+ sec->free = usm_free;
+ sec->data = usm;
+ return sec;
+}
+
+static int
+usm_doinit(struct snmp_agent *agent)
+{
+ struct ber_element *ber;
+ struct usm_sec *usm = agent->v3->sec->data;
+ int level;
+ size_t userlen;
+
+ if (usm->engineidset && usm->bootsset && usm->timeset)
+ return 0;
+
+ level = agent->v3->level;
+ agent->v3->level = SNMP_MSGFLAG_REPORT;
+ userlen = usm->userlen;
+ usm->userlen = 0;
+
+ if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
+ agent->v3->level = level;
+ usm->userlen = userlen;
+ return -1;
+ }
+ ber_free_element(ber);
+
+ agent->v3->level = level;
+ usm->userlen = userlen;
+
+ return 0;
+}
+
+static char *
+usm_genparams(struct snmp_agent *agent, size_t *len)
+{
+ struct ber ber;
+ struct ber_element *params;
+ struct usm_sec *usm = agent->v3->sec->data;
+ char *secparams = NULL;
+ ssize_t berlen = 0;
+ struct timespec now, timediff;
+ uint32_t boots, time;
+
+ if (usm->timeset) {
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+ return NULL;
+ timespecsub(&now, &(usm->timecheck), &timediff);
+ time = usm->time + timediff.tv_sec;
+ } else
+ time = 0;
+ 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)
+ return NULL;
+
+ bzero(&ber, sizeof(ber));
+ ber_set_application(&ber, smi_application);
+ if (ber_write_elements(&ber, params) != -1)
+ berlen = ber_copy_writebuf(&ber, (void **)&secparams);
+
+ *len = berlen;
+ ber_free_element(params);
+ ber_free(&ber);
+ return secparams;
+}
+
+static int
+usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
+ off_t secparamsoffset, char *buf, size_t buflen, uint8_t level)
+{
+ struct usm_sec *usm = agent->v3->sec->data;
+ struct ber ber;
+ struct ber_element *secparams;
+ char *engineid, *user;
+ size_t engineidlen, userlen;
+ struct timespec now, timediff;
+ uint32_t boots, time;
+
+ bzero(&ber, sizeof(ber));
+
+ ber_set_application(&ber, smi_application);
+ ber_set_readbuf(&ber, buf, buflen);
+ if ((secparams = ber_read_elements(&ber, NULL)) == NULL)
+ return -1;
+ ber_free(&ber);
+
+ if (ber_scanf_elements(secparams, "{xddxSS}", &engineid, &engineidlen,
+ &boots, &time, &user, &userlen) == -1)
+ goto fail;
+
+ if (!usm->engineidset) {
+ if (usm_setengineid(agent->v3->sec, engineid,
+ engineidlen) == -1)
+ goto fail;
+ } else {
+ if (usm->engineidlen != engineidlen)
+ goto fail;
+ if (memcmp(usm->engineid, engineid, engineidlen) != 0)
+ goto fail;
+ }
+
+ if (!usm->bootsset) {
+ usm->boots = boots;
+ usm->bootsset = 1;
+ } else {
+ if (boots < usm->boots)
+ goto fail;
+ if (boots > usm->boots) {
+ usm->bootsset = 0;
+ usm->timeset = 0;
+ usm_doinit(agent);
+ goto fail;
+ }
+ }
+
+ if (!usm->timeset) {
+ usm->time = time;
+ if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
+ goto fail;
+ usm->timeset = 1;
+ } else {
+ 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) {
+ usm->bootsset = 0;
+ usm->timeset = 0;
+ usm_doinit(agent);
+ goto fail;
+ }
+ }
+
+ if (userlen != usm->userlen ||
+ memcmp(user, usm->user, userlen) != 0)
+ goto fail;
+
+ ber_free_element(secparams);
+ return 0;
+
+fail:
+ ber_free_element(secparams);
+ return -1;
+}
+
+static void
+usm_free(void *data)
+{
+ struct usm_sec *usm = data;
+
+ free(usm->user);
+ free(usm->engineid);
+ free(usm);
+}
+
+int
+usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
+{
+ struct usm_sec *usm = sec->data;
+
+ if (usm->engineid != NULL)
+ free(usm->engineid);
+ if ((usm->engineid = malloc(engineidlen)) == NULL)
+ return -1;
+ memcpy(usm->engineid, engineid, engineidlen);
+ usm->engineidlen = engineidlen;
+ usm->engineidset = 1;
+
+ return 0;
+}
+
+int
+usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
+{
+ struct usm_sec *usm = sec->data;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
+ return -1;
+
+ usm->boots = boots;
+ usm->bootsset = 1;
+ usm->time = time;
+ usm->timeset = 1;
+ return 0;
+}
diff --git a/usr.bin/snmp/usm.h b/usr.bin/snmp/usm.h
new file mode 100644
index 00000000000..aa84290992a
--- /dev/null
+++ b/usr.bin/snmp/usm.h
@@ -0,0 +1,23 @@
+/* $OpenBSD: usm.h,v 1.1 2019/09/18 09:48:14 martijn Exp $ */
+
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "snmp.h"
+
+struct snmp_sec *usm_init(const char *, size_t);
+int usm_setengineid(struct snmp_sec *, char *, size_t);
+int usm_setbootstime(struct snmp_sec *, uint32_t, uint32_t);