From ab8588121d0fbbaf7bf7cae580daac33eb2737db Mon Sep 17 00:00:00 2001 From: martijn Date: Thu, 2 Sep 2021 05:41:02 +0000 Subject: [PATCH] Allow snmpd(8) to send SNMPv3 traps. OK jmatthew@ --- usr.sbin/snmpd/parse.y | 145 +++++++++++++++++++++++++++++------- usr.sbin/snmpd/snmpd.conf.5 | 41 ++++++++-- usr.sbin/snmpd/snmpd.h | 19 +++-- usr.sbin/snmpd/snmpe.c | 5 +- usr.sbin/snmpd/trap.c | 102 ++++++++++++------------- 5 files changed, 223 insertions(+), 89 deletions(-) diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y index b9f4016fabe..cfd8d4d1343 100644 --- a/usr.sbin/snmpd/parse.y +++ b/usr.sbin/snmpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.67 2021/08/10 16:14:00 martijn Exp $ */ +/* $OpenBSD: parse.y,v 1.68 2021/09/02 05:41:02 martijn Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -134,11 +134,11 @@ typedef struct { %token HANDLE DEFAULT SRCADDR TCP UDP PFADDRFILTER PORT %token STRING %token NUMBER -%type hostcmn +%type usmuser community optcommunity %type listenproto listenflag listenflags %type srcaddr port %type optwrite yesno seclevel -%type objtype cmd +%type objtype cmd hostauth hostauthv3 usmauthopts usmauthopt %type oid hostoid trapoid %type auth %type enc @@ -243,13 +243,13 @@ main : LISTEN ON listen_udptcp free($3); } | TRAP RECEIVER host - | TRAP HANDLE hostcmn trapoid cmd { - struct trapcmd *cmd = $5.data; + | TRAP HANDLE trapoid cmd { + struct trapcmd *cmd = $4.data; - cmd->cmd_oid = $4; + cmd->cmd_oid = $3; if (trapcmd_add(cmd) != 0) { - free($4); + free($3); free(cmd); yyerror("duplicate oid"); YYERROR; @@ -268,8 +268,8 @@ main : LISTEN ON listen_udptcp | PFADDRFILTER yesno { conf->sc_pfaddrfilter = $2; } - | SECLEVEL seclevel { - conf->sc_min_seclevel = $2; + | seclevel { + conf->sc_min_seclevel = $1; } | USER STRING { const char *errstr; @@ -701,15 +701,93 @@ hostoid : /* empty */ { $$ = NULL; } | OBJECTID oid { $$ = $2; } ; -hostcmn : /* empty */ { $$ = NULL; } - | COMMUNITY STRING { $$ = $2; } +usmuser : USER STRING { + if (strlen($2) > SNMPD_MAXUSERNAMELEN) { + yyerror("User name too long: %s", $2); + free($2); + YYERROR; + } + $$ = $2; + } + ; + +community : COMMUNITY STRING { + if (strlen($2) > SNMPD_MAXCOMMUNITYLEN) { + yyerror("Community too long: %s", $2); + free($2); + YYERROR; + } + $$ = $2; + } + ; + +optcommunity : /* empty */ { $$ = NULL; } + | community { $$ = $1; } + ; + +usmauthopt : usmuser { + $$.data = $1; + $$.value = -1; + } + | seclevel { + $$.data = 0; + $$.value = $1; + } + ; + +usmauthopts : /* empty */ { + $$.data = NULL; + $$.value = -1; + } + | usmauthopts usmauthopt { + if ($2.data != NULL) { + if ($$.data != NULL) { + yyerror("user redefined"); + free($2.data); + YYERROR; + } + $$.data = $2.data; + } else { + if ($$.value != -1) { + yyerror("seclevel redefined"); + YYERROR; + } + $$.value = $2.value; + } + } + ; + +hostauthv3 : usmauthopts { + if ($1.data == NULL) { + yyerror("user missing"); + YYERROR; + } + $$.data = $1.data; + $$.value = $1.value; + } + ; + +hostauth : hostauthv3 { + $$.type = SNMP_V3; + $$.data = $1.data; + $$.value = $1.value; + } + | SNMPV2 optcommunity { + $$.type = SNMP_V2; + $$.data = $2; + } + | SNMPV3 hostauthv3 { + $$.type = SNMP_V3; + $$.data = $2.data; + $$.value = $2.value; + } ; srcaddr : /* empty */ { $$ = NULL; } | SRCADDR STRING { $$ = $2; } ; -hostdef : STRING hostoid hostcmn srcaddr { +hostdef : STRING hostoid hostauth srcaddr { struct sockaddr_storage ss; struct trap_address *tr; @@ -722,27 +800,35 @@ hostdef : STRING hostoid hostcmn srcaddr { yyerror("invalid host: %s", $1); free($1); free($2); - free($3); + free($3.data); free($4); free(tr); YYERROR; } free($1); - memcpy(&(tr->ss), &ss, sizeof(ss)); + memcpy(&(tr->ta_ss), &ss, sizeof(ss)); if ($4 != NULL) { if (host($1, "0", SOCK_DGRAM, &ss, 1) <= 0) { yyerror("invalid host: %s", $1); free($2); - free($3); + free($3.data); free($4); free(tr); YYERROR; } free($4); - memcpy(&(tr->ss_local), &ss, sizeof(ss)); + memcpy(&(tr->ta_sslocal), &ss, sizeof(ss)); + } + tr->ta_oid = $2; + tr->ta_version = $3.type; + if ($3.type == ADDRESS_FLAG_SNMPV2) { + (void)strlcpy(tr->ta_community, $3.data, + sizeof(tr->ta_community)); + free($3.data); + } else { + tr->ta_usmusername = $3.data; + tr->ta_seclevel = $3.value; } - tr->sa_oid = $2; - tr->sa_community = $3; TAILQ_INSERT_TAIL(&(conf->sc_trapreceivers), tr, entry); } ; @@ -759,9 +845,9 @@ comma : /* empty */ | ',' ; -seclevel : NONE { $$ = 0; } - | AUTH { $$ = SNMP_MSGFLAG_AUTH; } - | ENC { $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; } +seclevel : SECLEVEL NONE { $$ = 0; } + | SECLEVEL AUTH { $$ = SNMP_MSGFLAG_AUTH; } + | SECLEVEL ENC { $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; } ; userspecs : /* empty */ @@ -1393,10 +1479,19 @@ parse_config(const char *filename, u_int flags) return (NULL); } - if (conf->sc_trcommunity[0] == '\0') { - TAILQ_FOREACH(tr, &conf->sc_trapreceivers, entry) { - if (tr->sa_community == NULL) { - log_warnx("trap receiver: missing community"); + TAILQ_FOREACH(tr, &conf->sc_trapreceivers, entry) { + if (tr->ta_version == SNMP_V2 && + tr->ta_community[0] == '\0' && + conf->sc_trcommunity[0] == '\0') { + log_warnx("trap receiver: missing community"); + free(conf); + return (NULL); + } + if (tr->ta_version == SNMP_V3) { + tr->ta_usmuser = usm_finduser(tr->ta_usmusername); + if (tr->ta_usmuser == NULL) { + log_warnx("trap receiver: user not defined: %s", + tr->ta_usmusername); free(conf); return (NULL); } diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5 index bf2fd6935b6..2e4de727c51 100644 --- a/usr.sbin/snmpd/snmpd.conf.5 +++ b/usr.sbin/snmpd/snmpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: snmpd.conf.5,v 1.57 2021/08/11 18:35:02 sthen Exp $ +.\" $OpenBSD: snmpd.conf.5,v 1.58 2021/09/02 05:41:02 martijn Exp $ .\" .\" Copyright (c) 2007, 2008, 2012 Reyk Floeter .\" @@ -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 11 2021 $ +.Dd $Mdocdate: September 2 2021 $ .Dt SNMPD.CONF 5 .Os .Sh NAME @@ -308,21 +308,50 @@ statement with a flag set. Traps over SNMPv3 are currently unsupported. .It Xo -.Ic trap receiver Ar string +.Ic trap receiver Ar address .Op Ic oid Ar oid-string +.Ic snmpv2c .Op Ic community Ar string .Op Ic source-address Ar address .Xc -Specify the address or FQDN of a remote trap receiver for outgoing traps +Specify the +.Ar address +or FQDN of a remote trap receiver for outgoing traps sent by .Xr snmpd 8 . This option may be specified multiple times. -The daemon will send outgoing traps using the revised SNMPv2 format and the -configured trap community. +The daemon will send outgoing traps in +.Ic snmpv2c +format. The default community is specified by the global .Ic trap community option. The IPv4 or IPv6 source address of the traps can be enforced using +.It Xo +.Ic trap receiver Ar address +.Op Ic oid Ar oid-string +.Op Ic snmpv3 +.Ic user Ar name Oo Ic seclevel Ar level Oc +.Op Ic source-address Ar address +.Xc +Specify the +.Ar address +or FQDN of a remote trap receiver for outgoing traps +sent by +.Xr snmpd 8 . +This option may be specified multiple times. +The daemon will send outgoing traps in +.Ic snmpv3 +format. +.Ic user +must point to an existing global +.Ic user . +If +.Ic seclevel +is not defined it defaults to the global +.Ic seclevel +option. +The IPv4 or IPv6 source address of the traps can be enforced using .Ic source-address . .El .Sh USER CONFIGURATION diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h index d5f3b75d688..778f5eca6b3 100644 --- a/usr.sbin/snmpd/snmpd.h +++ b/usr.sbin/snmpd/snmpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpd.h,v 1.99 2021/08/09 18:14:53 martijn Exp $ */ +/* $OpenBSD: snmpd.h,v 1.100 2021/09/02 05:41:02 martijn Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -512,10 +512,18 @@ TAILQ_HEAD(addresslist, address); (ADDRESS_FLAG_SNMPV1 | ADDRESS_FLAG_SNMPV2 | ADDRESS_FLAG_SNMPV3) struct trap_address { - struct sockaddr_storage ss; - struct sockaddr_storage ss_local; - char *sa_community; - struct ber_oid *sa_oid; + struct sockaddr_storage ta_ss; + struct sockaddr_storage ta_sslocal; + int ta_version; + union { + char ta_community[SNMPD_MAXCOMMUNITYLEN]; + struct { + char *ta_usmusername; + struct usmuser *ta_usmuser; + int ta_seclevel; + }; + }; + struct ber_oid *ta_oid; TAILQ_ENTRY(trap_address) entry; }; @@ -655,6 +663,7 @@ struct kif_arp *karp_getaddr(struct sockaddr *, u_short, int); void snmpe(struct privsep *, struct privsep_proc *); void snmpe_shutdown(void); void snmpe_dispatchmsg(struct snmp_message *); +void snmpe_response(struct snmp_message *); int snmp_messagecmp(struct snmp_message *, struct snmp_message *); RB_PROTOTYPE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp) diff --git a/usr.sbin/snmpd/snmpe.c b/usr.sbin/snmpd/snmpe.c index 093cb26ba55..5d8c7ab0c92 100644 --- a/usr.sbin/snmpd/snmpe.c +++ b/usr.sbin/snmpd/snmpe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpe.c,v 1.74 2021/08/09 18:14:53 martijn Exp $ */ +/* $OpenBSD: snmpe.c,v 1.75 2021/09/02 05:41:02 martijn Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -45,7 +45,6 @@ const char *snmpe_pdutype2string(enum snmp_pdutype); int snmpe_parse(struct snmp_message *); void snmpe_tryparse(int, struct snmp_message *); int snmpe_parsevarbinds(struct snmp_message *); -void snmpe_response(struct snmp_message *); void snmpe_sig_handler(int sig, short, void *); int snmpe_bind(struct address *); void snmpe_recvmsg(int fd, short, void *); @@ -98,7 +97,6 @@ snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg) struct address *h; kr_init(); - trap_init(); timer_init(); usm_generate_keys(); @@ -124,6 +122,7 @@ snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg) log_info("snmpe %s: ready", tohexstr(env->sc_engineid, env->sc_engineid_len)); + trap_init(); } void diff --git a/usr.sbin/snmpd/trap.c b/usr.sbin/snmpd/trap.c index 23e406f5801..acdce028ac0 100644 --- a/usr.sbin/snmpd/trap.c +++ b/usr.sbin/snmpd/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.36 2020/09/06 15:51:28 martijn Exp $ */ +/* $OpenBSD: trap.c,v 1.37 2021/09/02 05:41:02 martijn Exp $ */ /* * Copyright (c) 2008 Reyk Floeter @@ -54,17 +54,13 @@ trap_init(void) int trap_send(struct ber_oid *oid, struct ber_element *elm) { - int ret = 0, s; struct trap_address *tr; - struct ber_element *root, *b, *c, *trap; - struct ber ber; - char *cmn; - ssize_t len; - u_int8_t *ptr; + struct ber_element *vblist, *trap; struct ber_oid uptime = OID(MIB_sysUpTime); struct ber_oid trapoid = OID(MIB_snmpTrapOID); char ostr[SNMP_MAX_OID_STRLEN]; struct oid oa, ob; + struct snmp_message *msg; if (TAILQ_EMPTY(&snmpd_env->sc_trapreceivers)) return (0); @@ -85,66 +81,72 @@ trap_send(struct ber_oid *oid, struct ber_element *elm) /* Add mandatory varbind elements */ trap = ober_add_sequence(NULL); - c = ober_printf_elements(trap, "{Odt}{OO}", + vblist = ober_printf_elements(trap, "{Odt}{OO}", &uptime, smi_getticks(), BER_CLASS_APPLICATION, SNMP_T_TIMETICKS, &trapoid, oid); if (elm != NULL) - ober_link_elements(c, elm); - - bzero(&ber, sizeof(ber)); + ober_link_elements(vblist, elm); TAILQ_FOREACH(tr, &snmpd_env->sc_trapreceivers, entry) { - if (tr->sa_oid != NULL && tr->sa_oid->bo_n) { + if (tr->ta_oid != NULL && tr->ta_oid->bo_n) { /* The trap receiver may want only a specified MIB */ - bcopy(&tr->sa_oid->bo_id, &ob.o_oid, + bcopy(&tr->ta_oid->bo_id, &ob.o_oid, sizeof(ob.o_oid)); - ob.o_oidlen = tr->sa_oid->bo_n; + ob.o_oidlen = tr->ta_oid->bo_n; if (smi_oid_cmp(&oa, &ob) != 0) continue; } - if ((s = snmpd_socket_af(&tr->ss, SOCK_DGRAM)) == -1) { - ret = -1; - goto done; + if ((msg = calloc(1, sizeof(*msg))) == NULL) + fatal("malloc"); + msg->sm_sock = snmpd_socket_af(&tr->ta_ss, SOCK_DGRAM); + if (msg->sm_sock == -1) { + log_warn("socket"); + free(msg); + continue; } - if (tr->ss_local.ss_family != 0) { - if (bind(s, (struct sockaddr *)&(tr->ss_local), - tr->ss_local.ss_len) == -1) { - ret = -1; - goto done; - } + memcpy(&(msg->sm_ss), &(tr->ta_ss), sizeof(msg->sm_ss)); + msg->sm_slen = tr->ta_ss.ss_len; + if (tr->ta_sslocal.ss_family != 0) { + memcpy(&(msg->sm_local_ss), &(tr->ta_sslocal), + sizeof(msg->sm_local_ss)); + msg->sm_local_slen = tr->ta_sslocal.ss_len; } - - cmn = tr->sa_community != NULL ? - tr->sa_community : snmpd_env->sc_trcommunity; - - /* SNMP header */ - root = ober_add_sequence(NULL); - b = ober_printf_elements(root, "ds{tddd", - SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2, - arc4random(), 0, 0); - ober_link_elements(b, trap); - -#ifdef DEBUG - smi_debug_elements(root); -#endif - len = ober_write_elements(&ber, root); - if (ober_get_writebuf(&ber, (void *)&ptr) > 0 && - sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss, - tr->ss.ss_len) != -1) { - snmpd_env->sc_stats.snmp_outpkts++; - ret++; + msg->sm_version = tr->ta_version; + msg->sm_pdutype = SNMP_C_TRAPV2; + ober_set_application(&msg->sm_ber, smi_application); + msg->sm_request = arc4random(); + if ((msg->sm_varbindresp = ober_dup(trap->be_sub)) == NULL) + fatal("malloc"); + + switch (msg->sm_version) { + case SNMP_V2: + (void)strlcpy(msg->sm_community, tr->ta_community, + sizeof(msg->sm_community)); + break; + case SNMP_V3: + msg->sm_msgid = msg->sm_request & INT32_MAX; + msg->sm_max_msg_size = READ_BUF_SIZE; + msg->sm_flags = tr->ta_seclevel != -1 ? + tr->ta_seclevel : snmpd_env->sc_min_seclevel; + msg->sm_secmodel = SNMP_SEC_USM; + msg->sm_engine_time = snmpd_engine_time(); + msg->sm_engine_boots = snmpd_env->sc_engine_boots; + memcpy(msg->sm_ctxengineid, snmpd_env->sc_engineid, + snmpd_env->sc_engineid_len); + msg->sm_ctxengineid_len = + snmpd_env->sc_engineid_len; + (void)strlcpy(msg->sm_username, tr->ta_usmusername, + sizeof(msg->sm_username)); + msg->sm_user = tr->ta_usmuser; + arc4random_buf(msg->sm_salt, sizeof(msg->sm_salt)); + break; } - close(s); - ober_unlink_elements(b); - ober_free_elements(root); + snmpe_response(msg); } - - done: ober_free_elements(trap); - ober_free(&ber); - return (ret); + return 0; } -- 2.20.1