Allow snmpd(8) to send SNMPv3 traps.
authormartijn <martijn@openbsd.org>
Thu, 2 Sep 2021 05:41:02 +0000 (05:41 +0000)
committermartijn <martijn@openbsd.org>
Thu, 2 Sep 2021 05:41:02 +0000 (05:41 +0000)
OK jmatthew@

usr.sbin/snmpd/parse.y
usr.sbin/snmpd/snmpd.conf.5
usr.sbin/snmpd/snmpd.h
usr.sbin/snmpd/snmpe.c
usr.sbin/snmpd/trap.c

index b9f4016..cfd8d4d 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -134,11 +134,11 @@ typedef struct {
 %token HANDLE DEFAULT SRCADDR TCP UDP PFADDRFILTER PORT
 %token <v.string>      STRING
 %token <v.number>      NUMBER
-%type  <v.string>      hostcmn
+%type  <v.string>      usmuser community optcommunity
 %type  <v.number>      listenproto listenflag listenflags
 %type  <v.string>      srcaddr port
 %type  <v.number>      optwrite yesno seclevel
-%type  <v.data>        objtype cmd
+%type  <v.data>        objtype cmd hostauth hostauthv3 usmauthopts usmauthopt
 %type  <v.oid>         oid hostoid trapoid
 %type  <v.auth>        auth
 %type  <v.enc>         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);
                        }
index bf2fd69..2e4de72 100644 (file)
@@ -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 <reyk@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 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
index d5f3b75..778f5ec 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -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)
 
index 093cb26..5d8c7ab 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -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
index 23e406f..acdce02 100644 (file)
@@ -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 <reyk@openbsd.org>
@@ -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;
 }