Hello,

Part 2 of making NET::SNMP work with snmpd(8) in seclevel enc mode.

RFC3414 Chapter 4 states:
If authenticated communication is required, then the discovery
process should also establish time synchronization with the
authoritative SNMP engine.  This may be accomplished by sending an
authenticated Request message with the value of
msgAuthoritativeEngineID set to the newly learned snmpEngineID and
with the values of msgAuthoritativeEngineBoots and
msgAuthoritativeEngineTime set to zero.  For an authenticated Request
message, a valid userName must be used in the msgUserName field.  The
response to this authenticated message will be a Report message
containing the up to date values of the authoritative SNMP engine's
snmpEngineBoots and snmpEngineTime as the value of the
msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime fields
respectively.  It also contains the usmStatsNotInTimeWindows counter
in the varBindList of the Report PDU.  The time synchronization then
happens automatically as part of the procedures in section 3.2 step
7b.  See also section 2.3.

And Chapter 11.4 states:
The use of unsecure reports (i.e., sending them with a securityLevel
of noAuthNoPriv) potentially exposes a non-authoritative SNMP engine
to some form of attacks.  Some people consider these denial of
service attacks, others don't.  An installation should evaluate the
risk involved before deploying unsecure Report PDUs.

Right now we return nothing if engine_boots and engine_time are 0,
so this is incorrect behaviour.

Also, we currently always return reports in plaintext, which is
perfectly fine by the RFC, but NET::SNMP expects the timesync report
to be secure. So I made the diff return the report secure if we
have evaluated the credentials.
With the diff below (on top of the diff send out earlier today) I can
use check_snmp_load.pl (and most likely friends) from the
manubulon-snmp package:

# cat /etc/snmpd.conf
seclevel enc

user test authkey aaaaaaaa auth hmac-sha1 enckey bbbbbbbb enc aes
# snmpd
$ martijn$ ./check_snmp_load.pl -H 127.0.0.1 -l test -x aaaaaaaa -X bbbbbbb  -L 
sha,aes -w 4 -c 10
ERROR opening session: Received usmStatsUnsupportedSecLevels.0 Report-PDU with 
value 1 during discovery.
# ./obj/snmpd
martijn$ ./check_snmp_load.pl -H 127.0.0.1 -l test -x aaaaaaaa -X bbbbbbb  -L 
sha,aes -w 4 -c 10
4 CPU, average load 5.8% > 4% : WARNING

OK?

martijn@

Index: usm.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/usm.c,v
retrieving revision 1.13
diff -u -p -r1.13 usm.c
--- usm.c       12 Aug 2018 22:04:09 -0000      1.13
+++ usm.c       7 May 2019 20:17:25 -0000
@@ -226,6 +226,7 @@ usm_decode(struct snmp_message *msg, str
 
        if (ber_get_nstring(elm, (void *)&usmparams, &len) < 0) {
                *errp = "cannot decode security params";
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -233,6 +234,7 @@ usm_decode(struct snmp_message *msg, str
        usm = ber_read_elements(&ber, NULL);
        if (usm == NULL) {
                *errp = "cannot decode security params";
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -245,6 +247,7 @@ usm_decode(struct snmp_message *msg, str
            &engine_boots, &engine_time, &user, &userlen, &offs2,
            &digest, &digestlen, &salt, &saltlen) != 0) {
                *errp = "cannot decode USM params";
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -257,6 +260,7 @@ usm_decode(struct snmp_message *msg, str
            (digestlen != (MSG_HAS_AUTH(msg) ? SNMP_USM_DIGESTLEN : 0)) ||
            (saltlen != (MSG_HAS_PRIV(msg) ? SNMP_USM_SALTLEN : 0))) {
                *errp = "bad field length";
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -265,21 +269,10 @@ usm_decode(struct snmp_message *msg, str
                *errp = "unknown engine id";
                msg->sm_usmerr = OIDVAL_usmErrEngineId;
                stats->snmp_usmnosuchengine++;
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
-       if (engine_boots != 0LL && engine_time != 0LL) {
-               now = snmpd_engine_time();
-               if (engine_boots != snmpd_env->sc_engine_boots ||
-                   engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW) ||
-                   engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW)) {
-                       *errp = "out of time window";
-                       msg->sm_usmerr = OIDVAL_usmErrTimeWindow;
-                       stats->snmp_usmtimewindow++;
-                       goto done;
-               }
-       }
-
        msg->sm_engine_boots = (u_int32_t)engine_boots;
        msg->sm_engine_time = (u_int32_t)engine_time;
 
@@ -290,12 +283,14 @@ usm_decode(struct snmp_message *msg, str
                *errp = "no such user";
                msg->sm_usmerr = OIDVAL_usmErrUserName;
                stats->snmp_usmnosuchuser++;
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
        if (MSG_SECLEVEL(msg) > msg->sm_user->uu_seclevel) {
                *errp = "unsupported security model";
                msg->sm_usmerr = OIDVAL_usmErrSecLevel;
                stats->snmp_usmbadseclevel++;
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -307,6 +302,7 @@ usm_decode(struct snmp_message *msg, str
                *errp = "bad msg digest";
                msg->sm_usmerr = OIDVAL_usmErrDigest;
                stats->snmp_usmwrongdigest++;
+               msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                goto done;
        }
 
@@ -316,10 +312,22 @@ usm_decode(struct snmp_message *msg, str
                        *errp = "cannot decrypt msg";
                        msg->sm_usmerr = OIDVAL_usmErrDecrypt;
                        stats->snmp_usmdecrypterr++;
+                       msg->sm_flags &= SNMP_MSGFLAG_REPORT;
                        goto done;
                }
                ber_replace_elements(elm, decr);
        }
+
+       now = snmpd_engine_time();
+       if (engine_boots != snmpd_env->sc_engine_boots ||
+           engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW) ||
+           engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW)) {
+               *errp = "out of time window";
+               msg->sm_usmerr = OIDVAL_usmErrTimeWindow;
+               stats->snmp_usmtimewindow++;
+               goto done;
+       }
+
        next = elm->be_next;
 
 done:
@@ -477,10 +485,7 @@ usm_make_report(struct snmp_message *msg
 {
        struct ber_oid           usmstat = OID(MIB_usmStats, 0, 0);
 
-       /* Always send report in clear-text */
-       msg->sm_flags = 0;
        msg->sm_context = SNMP_C_REPORT;
-       msg->sm_username[0] = '\0';
        usmstat.bo_id[OIDIDX_usmStats] = msg->sm_usmerr;
        usmstat.bo_n = OIDIDX_usmStats + 2;
        if (msg->sm_varbindresp != NULL)

Reply via email to