Shameless ping!

On Tue, Mar 28, 2017 at 12:45:20PM +0200, alf wrote:
> Further investigation shows that p5-Net-SNMP sends a first packet
> unauthentified and with an empty request. Here snmpd(8) violates
> RFC 3414. It should send OIDVAL_usmErrEngineId but instead sends
> OIDVAL_usmErrSecLevel.
> After that is fixed p5-Net-SNMP sends another empty request to
> sync its time however authentificated this time. This is unexpected
> for snmpd(8) so it doesn't handle this correctly.
> Below is a patch to fix this of the works-for-me fashion, needs some
> checking by someone with better understanding of snmp though.
> 
> 
> Explanations inline, complete diff at end:
> 
> We asume that having the REPORT flag set indicates a discovery so
> set OIDVAL_usmErrEngineid and answer back.
> 
> diff -ur snmpd/snmpe.c snmpd.modified/snmpe.c
> --- snmpd/snmpe.c     Tue Mar 28 12:19:03 2017
> +++ snmpd.modified/snmpe.c    Tue Mar 28 09:33:37 2017
> @@ -220,11 +220,18 @@
>               msg->sm_flags = *flagstr;
>               if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
>                   msg->sm_secmodel != SNMP_SEC_USM) {
> -                     /* XXX currently only USM supported */
> -                     msg->sm_errstr = "unsupported security model";
> -                     stats->snmp_usmbadseclevel++;
> -                     msg->sm_usmerr = OIDVAL_usmErrSecLevel;
> -                     goto parsefail;
> +                     if (MSG_REPORT(msg)) {
> +                             msg->sm_errstr = "no such engineid";
> +                             stats->snmp_usmnosuchengine++;
> +                             msg->sm_usmerr = OIDVAL_usmErrEngineId;
> +                             goto parsefail;
> +                     } else {
> +                             /* XXX currently only USM supported */
> +                             msg->sm_errstr = "unsupported security model";
> +                             stats->snmp_usmbadseclevel++;
> +                             msg->sm_usmerr = OIDVAL_usmErrSecLevel;
> +                             goto parsefail;
> +                     }
>               }
>  
>               if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
> @@ -324,11 +331,12 @@
>               msg->sm_errstr = "invalid PDU";
>               goto fail;
>       }
> 
> This check gets in the way because p5-Net-SNMP sends after the first
> unauthentificated packet a second one with an empty request to sync its time.
> There should be a better way however, as is it is XXX.
> 
> -     if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) {
> -             stats->snmp_silentdrops++;
> -             msg->sm_errstr = "invalid varbind";
> -             goto fail;
> -     }
> +     /* XXX disabled for now */
> +     /* if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) { */
> +     /*      stats->snmp_silentdrops++; */
> +     /*      msg->sm_errstr = "invalid varbind"; */
> +     /*      goto fail; */
> +     /* } */
>  
>       msg->sm_request = req;
>       msg->sm_error = errval;
> 
> Because of the second empty request in an otherwise valid packet first check
> for MSG_REPORT and check if snmpe_parse has failed later.
> @@ -485,7 +493,8 @@
>       struct snmp_stats       *stats = &env->sc_stats;
>       ssize_t                  len;
>       struct snmp_message     *msg;
> -
> +     int                      ret;
> +     
>       if ((msg = calloc(1, sizeof(*msg))) == NULL)
>               return;
>  
> @@ -517,17 +526,19 @@
>       smi_debug_elements(msg->sm_req);
>  #endif
>  
> -     if (snmpe_parse(msg) == -1) {
> -             if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
> -                     usm_make_report(msg);
> -                     snmpe_response(fd, msg);
> -                     return;
> -             } else {
> -                     snmp_msgfree(msg);
> -                     return;
> -             }
> +     ret = snmpe_parse(msg);
> +     
> +     if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
> +             usm_make_report(msg);
> +             snmpe_response(fd, msg);
> +             return;
>       }
>  
> +     if (ret == -1) {
> +             snmp_msgfree(msg);
> +             return;
> +     }
> +     
>       snmpe_dispatchmsg(msg, fd);
>  }
>  
> This check must be done differently so OIDVAL_usmErrTimeWindow gets set and
> reported back to the client.
> diff -ur snmpd/usm.c snmpd.modified/usm.c
> --- snmpd/usm.c       Tue Mar 28 12:19:03 2017
> +++ snmpd.modified/usm.c      Tue Mar 28 09:33:37 2017
> @@ -269,21 +269,20 @@
>               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++;
> +     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++;
> +             if (engine_boots != 0LL && engine_time != 0LL) 
>                       goto done;
> -             }
> +     } else {
> +             msg->sm_engine_boots = (u_int32_t)engine_boots;
> +             msg->sm_engine_time = (u_int32_t)engine_time;
>       }
> -
> -     msg->sm_engine_boots = (u_int32_t)engine_boots;
> -     msg->sm_engine_time = (u_int32_t)engine_time;
> -
> +     
>       memcpy(msg->sm_username, user, userlen);
>       msg->sm_username[userlen] = '\0';
>       msg->sm_user = usm_finduser(msg->sm_username);
> 
> Because the second request is already authentificated we must avoid clearing
> sm_flags and sm_username in the answer otherwise p5-Net-SNMP will not accept
> this packet.
> 
> @@ -481,9 +480,9 @@
>       struct ber_oid           usmstat = OID(MIB_usmStats, 0, 0);
>  
>       /* Always send report in clear-text */
> -     msg->sm_flags = 0;
> +     /* msg->sm_flags = 0; */
>       msg->sm_context = SNMP_C_REPORT;
> -     msg->sm_username[0] = '\0';
> +     /* msg->sm_username[0] = '\0'; */
>       usmstat.bo_id[OIDIDX_usmStats] = msg->sm_usmerr;
>       usmstat.bo_n = OIDIDX_usmStats + 2;
>       if (msg->sm_varbindresp != NULL)
> 
> 
> ------------------------------------------------------------------------------
> Complete diff:
> 
> 
> diff -ur snmpd/snmpe.c snmpd.modified/snmpe.c
> --- snmpd/snmpe.c     Tue Mar 28 12:19:03 2017
> +++ snmpd.modified/snmpe.c    Tue Mar 28 09:33:37 2017
> @@ -220,11 +220,18 @@
>               msg->sm_flags = *flagstr;
>               if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
>                   msg->sm_secmodel != SNMP_SEC_USM) {
> -                     /* XXX currently only USM supported */
> -                     msg->sm_errstr = "unsupported security model";
> -                     stats->snmp_usmbadseclevel++;
> -                     msg->sm_usmerr = OIDVAL_usmErrSecLevel;
> -                     goto parsefail;
> +                     if (MSG_REPORT(msg)) {
> +                             msg->sm_errstr = "no such engineid";
> +                             stats->snmp_usmnosuchengine++;
> +                             msg->sm_usmerr = OIDVAL_usmErrEngineId;
> +                             goto parsefail;
> +                     } else {
> +                             /* XXX currently only USM supported */
> +                             msg->sm_errstr = "unsupported security model";
> +                             stats->snmp_usmbadseclevel++;
> +                             msg->sm_usmerr = OIDVAL_usmErrSecLevel;
> +                             goto parsefail;
> +                     }
>               }
>  
>               if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
> @@ -324,11 +331,12 @@
>               msg->sm_errstr = "invalid PDU";
>               goto fail;
>       }
> -     if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) {
> -             stats->snmp_silentdrops++;
> -             msg->sm_errstr = "invalid varbind";
> -             goto fail;
> -     }
> +     /* XXX disabled for now */
> +     /* if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) { */
> +     /*      stats->snmp_silentdrops++; */
> +     /*      msg->sm_errstr = "invalid varbind"; */
> +     /*      goto fail; */
> +     /* } */
>  
>       msg->sm_request = req;
>       msg->sm_error = errval;
> @@ -485,7 +493,8 @@
>       struct snmp_stats       *stats = &env->sc_stats;
>       ssize_t                  len;
>       struct snmp_message     *msg;
> -
> +     int                      ret;
> +     
>       if ((msg = calloc(1, sizeof(*msg))) == NULL)
>               return;
>  
> @@ -517,17 +526,19 @@
>       smi_debug_elements(msg->sm_req);
>  #endif
>  
> -     if (snmpe_parse(msg) == -1) {
> -             if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
> -                     usm_make_report(msg);
> -                     snmpe_response(fd, msg);
> -                     return;
> -             } else {
> -                     snmp_msgfree(msg);
> -                     return;
> -             }
> +     ret = snmpe_parse(msg);
> +     
> +     if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
> +             usm_make_report(msg);
> +             snmpe_response(fd, msg);
> +             return;
>       }
>  
> +     if (ret == -1) {
> +             snmp_msgfree(msg);
> +             return;
> +     }
> +     
>       snmpe_dispatchmsg(msg, fd);
>  }
>  
> diff -ur snmpd/usm.c snmpd.modified/usm.c
> --- snmpd/usm.c       Tue Mar 28 12:19:03 2017
> +++ snmpd.modified/usm.c      Tue Mar 28 09:33:37 2017
> @@ -269,21 +269,20 @@
>               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++;
> +     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++;
> +             if (engine_boots != 0LL && engine_time != 0LL) 
>                       goto done;
> -             }
> +     } else {
> +             msg->sm_engine_boots = (u_int32_t)engine_boots;
> +             msg->sm_engine_time = (u_int32_t)engine_time;
>       }
> -
> -     msg->sm_engine_boots = (u_int32_t)engine_boots;
> -     msg->sm_engine_time = (u_int32_t)engine_time;
> -
> +     
>       memcpy(msg->sm_username, user, userlen);
>       msg->sm_username[userlen] = '\0';
>       msg->sm_user = usm_finduser(msg->sm_username);
> @@ -481,9 +480,9 @@
>       struct ber_oid           usmstat = OID(MIB_usmStats, 0, 0);
>  
>       /* Always send report in clear-text */
> -     msg->sm_flags = 0;
> +     /* msg->sm_flags = 0; */
>       msg->sm_context = SNMP_C_REPORT;
> -     msg->sm_username[0] = '\0';
> +     /* 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