Re: snmpd: Move SNMPv2-SMI::snmpV2 to application_internal.c

2023-10-29 Thread Martijn van Duren
On Sun, 2023-10-29 at 20:11 +0100, Martijn van Duren wrote:
> Similar reasoning and questions as the move of SNMPv2-MIB::snmp.
> This moves SNMP-FRAMEWORK-MIB::snmpEngine and
> SNMP-USER-BASED-SM-MIB::usmStats from mib.c to application_internal.c,
> under SNMPv2-SMI::snmpV2. The reason for this broader umbrella is that
> other backends have no business fiddling around under this region.
> Since different backends bite eachother with exclusive regions, both
> snmpEngine and usmStats must be moved at the same time.
> 
> This change also breaks snmpd.sh from regress. This places a few custom
> OIDs in the usmUserTable, which is under snmpV2. Since these tests are
> mostly there to test snmp(1) I think it's worth removing these tests and
> build a proper snmp(1) regress suite another day (regress part in next
> mail)
> 
> OK?
> 
> martijn@
> 
Here's the regress diff

Index: snmpd.sh
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd.sh,v
retrieving revision 1.18
diff -u -p -r1.18 snmpd.sh
--- snmpd.sh19 Jan 2022 11:02:38 -  1.18
+++ snmpd.sh29 Oct 2023 19:14:33 -
@@ -271,8 +271,6 @@ read-write community non-default-rw
 oid 1.3.6.1.4.1.30155.42.1 name myName read-only string "humppa"
 oid 1.3.6.1.4.1.30155.42.2 name myStatus read-only integer 1
 # No need to place a full index, we just need the object
-oid 1.3.6.1.6.3.15.1.2.2.1.3.1 name Reyk read-only string "Reyk Fl${oe}ter"
-oid 1.3.6.1.6.3.15.1.2.2.1.3.2 name broken read-only string "br${boe}ken"
 EOF
 
 (cd ${OBJDIR} && nohup snmpd -dvf ./snmpd.conf > snmpd.log 2>&1) &
@@ -344,38 +342,6 @@ fi
 #  echo "Setting of a ro custom oid test unexpectedly succeeded."
 #  FAILED=1
 #fi
-
-snmp_command="snmp get -Oqv -v2c -c non-default-rw localhost \
-usmUserSecurityName.1.0"
-echo === $snmp_command
-reyk="$(eval LC_ALL=en_US.UTF-8 $snmp_command)"
-if [ "$reyk" != "Reyk Fl${oe}ter" ]
-then
-   echo "Printing of UTF-8 string in UTF-8 locale failed"
-   FAILED=1
-fi
-reyk="$(eval LC_ALL=C $snmp_command)"
-if [ "$reyk" != "Reyk Fl.ter" ]
-then
-   echo "Printing of UTF-8 string in C locale failed"
-   FAILED=1
-fi
-
-snmp_command="snmp get -Oqv -v2c -c non-default-rw localhost \
-usmUserSecurityName.2.0"
-echo === $snmp_command
-broken="$(eval LC_ALL=en_US.UTF-8 $snmp_command)"
-if [ "$broken" != "br${replacement}ken" ]
-then
-   echo "Printing of UTF-8 replacement character failed"
-   FAILED=1
-fi
-broken="$(eval LC_ALL=C $snmp_command)"
-if [ "$broken" != "br?ken" ]
-then
-   echo "Printing of question mark in C locale failed"
-   FAILED=1
-fi
 
 kill $(pgrep snmpd) >/dev/null 2>&1
 



snmpd: Move SNMPv2-MIB::system to application_internal.c

2023-10-29 Thread Martijn van Duren
This one is a little more involved than the previous 2. Since the
snmpd.conf's system variables are stored inside a 'struct oid', I want
to move these into their own struct inside 'struct snmpd'.

If we also move mib_tree to smi.c we can completely remove mib.c
As stated in my previous mail, this removes the sysORTable. I'll add
these back later in a proper way. (mib.c removal omitted from diff)

With these the only consumer of mps.c left is the oid keyword of
snmpd.conf.

OK?

martijn@

diff --git a/Makefile b/Makefile
index 3522a38..98bdda5 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ MAN=snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
application_blocklist.c application_internal.c \
application_agentx.c ax.c \
-   mps.c trap.c mib.c smi.c snmpd.c \
+   mps.c trap.c smi.c snmpd.c \
proc.c usm.c traphandler.c util.c
 
 LDADD= -levent -lutil -lcrypto
diff --git a/application_internal.c b/application_internal.c
index 36ab8ef..e682cc7 100644
--- a/application_internal.c
+++ b/application_internal.c
@@ -47,6 +47,7 @@ void appl_internal_getnext(struct appl_backend *, int32_t, 
int32_t,
 struct ber_element *appl_internal_snmp(struct ber_oid *);
 struct ber_element *appl_internal_engine(struct ber_oid *);
 struct ber_element *appl_internal_usmstats(struct ber_oid *);
+struct ber_element *appl_internal_system(struct ber_oid *);
 struct appl_internal_object *appl_internal_object_parent(struct ber_oid *);
 int appl_internal_object_cmp(struct appl_internal_object *,
 struct appl_internal_object *);
@@ -73,6 +74,15 @@ RB_PROTOTYPE_STATIC(appl_internal_objects, 
appl_internal_object, entry,
 void
 appl_internal_init(void)
 {
+   appl_internal_region(&OID(MIB_system));
+   appl_internal_object(&OID(MIB_sysDescr), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysOID), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysUpTime), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysContact), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysName), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysLocation), appl_internal_system, NULL);
+   appl_internal_object(&OID(MIB_sysServices), appl_internal_system, NULL);
+
appl_internal_region(&OID(MIB_snmp));
appl_internal_object(&OID(MIB_snmpInPkts), appl_internal_snmp, NULL);
appl_internal_object(&OID(MIB_snmpOutPkts), appl_internal_snmp, NULL);
@@ -441,6 +451,30 @@ appl_internal_usmstats(struct ber_oid *oid)
return value;
 }
 
+struct ber_element *
+appl_internal_system(struct ber_oid *oid)
+{
+   struct snmp_system *s = &snmpd_env->sc_system;
+   struct ber_element *value = NULL;
+
+   if (ober_oid_cmp(&OID(MIB_sysDescr, 0), oid) == 0)
+   return ober_add_string(NULL, s->sys_descr);
+   else if (ober_oid_cmp(&OID(MIB_sysOID, 0), oid) == 0)
+   return ober_add_oid(NULL, &s->sys_oid);
+   else if (ober_oid_cmp(&OID(MIB_sysUpTime, 0), oid) == 0) {
+   value = ober_add_integer(NULL, smi_getticks());
+   ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+   } else if (ober_oid_cmp(&OID(MIB_sysContact, 0), oid) == 0)
+   return ober_add_string(NULL, s->sys_contact);
+   else if (ober_oid_cmp(&OID(MIB_sysName, 0), oid) == 0)
+   return ober_add_string(NULL, s->sys_name);
+   else if (ober_oid_cmp(&OID(MIB_sysLocation, 0), oid) == 0)
+   return ober_add_string(NULL, s->sys_location);
+   else if (ober_oid_cmp(&OID(MIB_sysServices, 0), oid) == 0)
+   return ober_add_integer(NULL, s->sys_services);
+   return value;
+}
+
 struct appl_internal_object *
 appl_internal_object_parent(struct ber_oid *oid)
 {
diff --git a/parse.y b/parse.y
index 8f9bad0..7ae7ce1 100644
--- a/parse.y
+++ b/parse.y
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
@@ -760,28 +761,87 @@ system: SYSTEM sysmib
;
 
 sysmib : CONTACT STRING{
-   struct ber_oid   o = OID(MIB_sysContact);
-   mps_set(&o, $2, strlen($2));
+   if (conf->sc_system.sys_contact[0] != '\0') {
+   yyerror("system contact already defined");
+   free($2);
+   YYERROR;
+   }
+   if (strlcpy(conf->sc_system.sys_contact, $2,
+   sizeof(conf->sc_system.sys_contact)) >=
+   sizeof(conf->sc_system.sys_contact)) {
+   yyerror("system contact too long");
+   free($2);
+   YYERROR;
+   

snmpd: Move SNMPv2-SMI::snmpV2 to application_internal.c

2023-10-29 Thread Martijn van Duren
Similar reasoning and questions as the move of SNMPv2-MIB::snmp.
This moves SNMP-FRAMEWORK-MIB::snmpEngine and
SNMP-USER-BASED-SM-MIB::usmStats from mib.c to application_internal.c,
under SNMPv2-SMI::snmpV2. The reason for this broader umbrella is that
other backends have no business fiddling around under this region.
Since different backends bite eachother with exclusive regions, both
snmpEngine and usmStats must be moved at the same time.

This change also breaks snmpd.sh from regress. This places a few custom
OIDs in the usmUserTable, which is under snmpV2. Since these tests are
mostly there to test snmp(1) I think it's worth removing these tests and
build a proper snmp(1) regress suite another day (regress part in next
mail)

OK?

martijn@

diff --git a/application_internal.c b/application_internal.c
index c76e8ef..36ab8ef 100644
--- a/application_internal.c
+++ b/application_internal.c
@@ -45,6 +45,8 @@ void appl_internal_get(struct appl_backend *, int32_t, 
int32_t, const char *,
 void appl_internal_getnext(struct appl_backend *, int32_t, int32_t,
 const char *, struct appl_varbind *);
 struct ber_element *appl_internal_snmp(struct ber_oid *);
+struct ber_element *appl_internal_engine(struct ber_oid *);
+struct ber_element *appl_internal_usmstats(struct ber_oid *);
 struct appl_internal_object *appl_internal_object_parent(struct ber_oid *);
 int appl_internal_object_cmp(struct appl_internal_object *,
 struct appl_internal_object *);
@@ -128,6 +130,29 @@ appl_internal_init(void)
NULL);
appl_internal_object(&OID(MIB_snmpProxyDrops), appl_internal_snmp,
NULL);
+
+   appl_internal_region(&OID(MIB_snmpV2));
+   appl_internal_object(&OID(MIB_snmpEngineID), appl_internal_engine,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpEngineBoots), appl_internal_engine,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpEngineTime), appl_internal_engine,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpEngineMaxMsgSize),
+   appl_internal_engine, NULL);
+
+   appl_internal_object(&OID(MIB_usmStatsUnsupportedSecLevels),
+   appl_internal_usmstats, NULL);
+   appl_internal_object(&OID(MIB_usmStatsNotInTimeWindow),
+   appl_internal_usmstats, NULL);
+   appl_internal_object(&OID(MIB_usmStatsUnknownUserNames),
+   appl_internal_usmstats, NULL);
+   appl_internal_object(&OID(MIB_usmStatsUnknownEngineId),
+   appl_internal_usmstats, NULL);
+   appl_internal_object(&OID(MIB_usmStatsWrongDigests),
+   appl_internal_usmstats, NULL);
+   appl_internal_object(&OID(MIB_usmStatsDecryptionErrors),
+   appl_internal_usmstats, NULL);
 }
 
 void
@@ -376,6 +401,46 @@ appl_internal_snmp(struct ber_oid *oid)
return value;
 }
 
+struct ber_element *
+appl_internal_engine(struct ber_oid *oid)
+{
+   if (ober_oid_cmp(&OID(MIB_snmpEngineID, 0), oid) == 0)
+   return ober_add_nstring(NULL, snmpd_env->sc_engineid,
+   snmpd_env->sc_engineid_len);
+   else if (ober_oid_cmp(&OID(MIB_snmpEngineBoots, 0), oid) == 0)
+   return ober_add_integer(NULL, snmpd_env->sc_engine_boots);
+   else if (ober_oid_cmp(&OID(MIB_snmpEngineTime, 0), oid) == 0)
+   return ober_add_integer(NULL, snmpd_engine_time());
+   else if (ober_oid_cmp(&OID(MIB_snmpEngineMaxMsgSize, 0), oid) == 0)
+   return ober_add_integer(NULL, READ_BUF_SIZE);
+   return NULL;
+}
+
+struct ber_element *
+appl_internal_usmstats(struct ber_oid *oid)
+{
+   struct snmp_stats *stats = &snmpd_env->sc_stats;
+   struct ber_element *value = NULL;
+
+   if (ober_oid_cmp(&OID(MIB_usmStatsUnsupportedSecLevels, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmbadseclevel);
+   else if (ober_oid_cmp(&OID(MIB_usmStatsNotInTimeWindow, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmtimewindow);
+   else if (ober_oid_cmp(&OID(MIB_usmStatsUnknownUserNames, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmnosuchuser);
+   else if (ober_oid_cmp(&OID(MIB_usmStatsUnknownEngineId, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmnosuchengine);
+   else if (ober_oid_cmp(&OID(MIB_usmStatsWrongDigests, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmwrongdigest);
+   else if (ober_oid_cmp(&OID(MIB_usmStatsDecryptionErrors, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_usmdecrypterr);
+
+   if (value != NULL)
+   ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+
+   return value;
+}
+
 struct appl_internal_object *
 appl_internal_object_parent(struct ber_oid *oid)
 {
diff --git a/mib.c b/mib.c
index ef34983..481460c 100644
--- a/mib.c
+++ b/mib.c
@@ -216,79 +216,6 @@ mib_sysor(struct oid *oid, struct ber_oid *o, struct 
ber_elem

snmpd: Move SNMPv2-MIB::snmp to application_internal.c

2023-10-29 Thread Martijn van Duren
I think the subject says it all. I'm not a 100% convinced that 
appl_internal_snmp()
should live in application_internal.c, maybe snmpe.c, where these metrics are 
set
is a better place. But since things come from mib.c, let's keep it simply here 
and
we can always shuffle the deckchairs later.

OK?

martijn@

diff --git a/application_internal.c b/application_internal.c
index ff9611e..c76e8ef 100644
--- a/application_internal.c
+++ b/application_internal.c
@@ -44,6 +44,7 @@ void appl_internal_get(struct appl_backend *, int32_t, 
int32_t, const char *,
 struct appl_varbind *);
 void appl_internal_getnext(struct appl_backend *, int32_t, int32_t,
 const char *, struct appl_varbind *);
+struct ber_element *appl_internal_snmp(struct ber_oid *);
 struct appl_internal_object *appl_internal_object_parent(struct ber_oid *);
 int appl_internal_object_cmp(struct appl_internal_object *,
 struct appl_internal_object *);
@@ -70,6 +71,63 @@ RB_PROTOTYPE_STATIC(appl_internal_objects, 
appl_internal_object, entry,
 void
 appl_internal_init(void)
 {
+   appl_internal_region(&OID(MIB_snmp));
+   appl_internal_object(&OID(MIB_snmpInPkts), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpOutPkts), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpInBadVersions), appl_internal_snmp,
+  NULL);
+   appl_internal_object(&OID(MIB_snmpInBadCommunityNames),
+  appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpInBadCommunityUses),
+  appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpInASNParseErrs), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInTooBigs), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpInNoSuchNames), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInBadValues), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInGenErrs), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpInTotalReqVars), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInTotalSetVars), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInGetRequests), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInGetNexts), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInSetRequests), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInGetResponses), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpInTraps), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpOutTooBigs), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutNoSuchNames), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutBadValues), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutGenErrs), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutGetRequests), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutGetNexts), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutSetRequests), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutGetResponses), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpOutTraps), appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpEnableAuthenTraps),
+   appl_internal_snmp, NULL);
+   appl_internal_object(&OID(MIB_snmpSilentDrops), appl_internal_snmp,
+   NULL);
+   appl_internal_object(&OID(MIB_snmpProxyDrops), appl_internal_snmp,
+   NULL);
 }
 
 void
@@ -245,6 +303,79 @@ appl_internal_getnext(struct appl_backend *backend,
appl_response(backend, requestid, APPL_ERROR_GENERR, i + 1, vblist);
 }
 
+struct ber_element *
+appl_internal_snmp(struct ber_oid *oid)
+{
+   struct snmp_stats *stats = &snmpd_env->sc_stats;
+   struct ber_element *value = NULL;
+
+   if (ober_oid_cmp(oid, &OID(MIB_snmpEnableAuthenTraps, 0)) == 0)
+   return ober_add_integer(NULL,
+   stats->snmp_enableauthentraps ? 1 : 2);
+   if (ober_oid_cmp(&OID(MIB_snmpInPkts, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_inpkts);
+   else if (ober_oid_cmp(&OID(MIB_snmpOutPkts, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_outpkts);
+   else if (ober_oid_cmp(&OID(MIB_snmpInBadVersions, 0), oid) == 0)
+   value = ober_add_integer(NULL, stats->snmp_inbadversions);
+   else if (ober_oid_cmp(&OID(MIB_snmpInBadCommunityNames, 0), 

snmpd: introduce application_internal.c

2023-10-29 Thread Martijn van Duren
In my quest to move away from the old code of mps.c and mib.c, here's
the next step: Introduce a new application_internal.c. This is basically
a very simplified version of the libagentx interface.
We register a region at a certain point with application.c, and use an
internal database of objects that can be walked like leaves that
application.c doesn't know about.

Apart from the code being a little easier to read than mps.c (imho), this
new interface has a few advantages over application_legacy.c:
- no objects are registered inside application.c as instance. Since
  application.c needs to account for overlapping regions it's quite a
  bit slower in finding the correct regions. appl_internal just deals
  with objects, which means we can just walk the leaves. This also makes
  the startup logs a bit quieter.
- Since we can now register an entire region we can block reserved
  regions from being claimed by other backends.
- We can now better distinguish between noSuchObject and
  noSuchInstance for GetRequests.

Since our only internal table atm is sysORTable, which only contains
invalid values I left out struct appl_internal_object's getnext() call,
but it should be "easy" enough to add later (I have some code here that
needs a bit of an extra shine before sending out). getnext() is going to
be called for non-scalars, where struct appl_internal_object can't know
the index.

This diff just adds the plumbing. Migrating the remaining mib.c code
will be done in 3 followup diffs, after which mib.c can be removed.
mps.c does need to stick around for a little longer, because of the
oid keyword in snmpd.conf.

OK?

martijn@

diff --git a/Makefile b/Makefile
index 261e89b..3522a38 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 PROG=  snmpd
 MAN=   snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
-   application_blocklist.c \
+   application_blocklist.c application_internal.c \
application_agentx.c ax.c \
mps.c trap.c mib.c smi.c snmpd.c \
proc.c usm.c traphandler.c util.c
diff --git a/application.c b/application.c
index c36f059..2359943 100644
--- a/application.c
+++ b/application.c
@@ -159,6 +159,7 @@ void
 appl_init(void)
 {
appl_blocklist_init();
+   appl_internal_init();
appl_legacy_init();
appl_agentx_init();
 }
@@ -169,6 +170,7 @@ appl_shutdown(void)
struct appl_context *ctx, *tctx;
 
appl_blocklist_shutdown();
+   appl_internal_shutdown();
appl_legacy_shutdown();
appl_agentx_shutdown();
 
diff --git a/application.h b/application.h
index 39e7b5f..b9c95c0 100644
--- a/application.h
+++ b/application.h
@@ -147,3 +147,7 @@ void appl_agentx_backend(int);
 /* application_blocklist.c */
 voidappl_blocklist_init(void);
 voidappl_blocklist_shutdown(void);
+
+/* application_internal.c */
+voidappl_internal_init(void);
+voidappl_internal_shutdown(void);
diff --git a/application_internal.c b/application_internal.c
new file mode 100644
index 000..ff9611e
--- /dev/null
+++ b/application_internal.c
@@ -0,0 +1,271 @@
+/* $OpenBSD$   */
+
+/*
+ * Copyright (c) 2023 Martijn van Duren 
+ *
+ * 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 
+
+#include 
+#include 
+
+#include "application.h"
+#include "log.h"
+#include "mib.h"
+#include "smi.h"
+#include "snmpd.h"
+
+struct appl_internal_object {
+   struct ber_oid   oid;
+   struct ber_element *(*get)(struct ber_oid *);
+   /* No getnext means the object is scalar */
+   struct ber_element *(*getnext)(int8_t, struct ber_oid *);
+
+   RB_ENTRY(appl_internal_object)   entry;
+};
+
+void appl_internal_region(struct ber_oid *);
+void appl_internal_object(struct ber_oid *,
+struct ber_element *(*)(struct ber_oid *),
+struct ber_element *(*)(int8_t, struct ber_oid *));
+void appl_internal_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_internal_getnext(struct appl_backend *, int32_t, int32_t,
+const char *, struct app

snmpd: don't move back in oid tree when on/below instance below region

2023-10-28 Thread Martijn van Duren
Right now when we register a region with below that an instance we
can revert back in the tree. When we request below the instance we
currently use appl_region_next() to find the next region and simply
set the to be find oid to the the oid of the new region. In the
situation described above this means that we return the parent
region and set the oid to that of the parent region, which is smaller
than the instance oid.

The easiest fix is to increment the searched oid to nextsibling of the
instance and let the normal appl_varbind_backend() logic handle it from
there.

OK?

martijn@

Index: usr.sbin/snmpd/application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.25
diff -u -p -r1.25 application.c
--- usr.sbin/snmpd/application.c27 Oct 2023 10:32:11 -  1.25
+++ usr.sbin/snmpd/application.c28 Oct 2023 07:57:23 -
@@ -1368,19 +1368,17 @@ appl_varbind_backend(struct appl_varbind
return -1;
return 0;
}
-   if ((region = appl_region_next(ureq->aru_ctx,
-   &(vb->av_oid), region)) == NULL)
-   goto eomv;
vb->av_oid = region->ar_oid;
+   ober_oid_nextsibling(&(vb->av_oid));
vb->av_include = 1;
+   return appl_varbind_backend(ivb);
}
} else if (cmp == 0) {
if (region->ar_instance && next && !vb->av_include) {
-   if ((region = appl_region_next(ureq->aru_ctx,
-   &(vb->av_oid), region)) == NULL)
-   goto eomv;
vb->av_oid = region->ar_oid;
+   ober_oid_nextsibling(&(vb->av_oid));
vb->av_include = 1;
+   return appl_varbind_backend(ivb);
}
}
ivb->avi_region = region;
Index: regress/usr.sbin/snmpd/Makefile
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/Makefile,v
retrieving revision 1.6
diff -u -p -r1.6 Makefile
--- regress/usr.sbin/snmpd/Makefile 27 Oct 2023 10:26:20 -  1.6
+++ regress/usr.sbin/snmpd/Makefile 28 Oct 2023 07:57:23 -
@@ -170,6 +170,9 @@ BACKEND_TARGETS+=   backend_getnext_stale
 BACKEND_TARGETS+=  backend_getnext_inclusive_backwards
 BACKEND_TARGETS+=  backend_getnext_toofew
 BACKEND_TARGETS+=  backend_getnext_toomany
+BACKEND_TARGETS+=  
backend_getnext_instance_below_region_before_instance
+BACKEND_TARGETS+=  
backend_getnext_instance_below_region_on_instance
+BACKEND_TARGETS+=  
backend_getnext_instance_below_region_below_instance
 BACKEND_TARGETS+=  backend_getbulk_nonrep_zero_maxrep_one
 BACKEND_TARGETS+=  backend_getbulk_nonrep_zero_maxrep_two
 BACKEND_TARGETS+=  backend_getbulk_nonrep_one_maxrep_one
Index: regress/usr.sbin/snmpd/backend.c
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/backend.c,v
retrieving revision 1.1
diff -u -p -r1.1 backend.c
--- regress/usr.sbin/snmpd/backend.c24 Oct 2023 14:34:40 -  1.1
+++ regress/usr.sbin/snmpd/backend.c28 Oct 2023 07:57:23 -
@@ -2633,6 +2633,173 @@ backend_getnext_toomany(void)
 }
 
 void
+backend_getnext_instance_below_region_before_instance(void)
+{
+   struct sockaddr_storage ss;
+   struct sockaddr *sa = (struct sockaddr *)&ss;
+   socklen_t salen;
+   int snmp_s, ax_s;
+   uint32_t sessionid1, sessionid2;
+   struct varbind varbind[] = {
+   {
+   .type = TYPE_NULL,
+   .name = OID_STRUCT(MIB_BACKEND_GETNEXT, 27),
+   .data.int32 = 1
+   },
+   };
+   struct searchrange searchrange[] = {
+   {
+   .start = OID_STRUCT(MIB_BACKEND_GETNEXT, 27),
+   .end = OID_STRUCT(MIB_BACKEND_GETNEXT, 27, 1, 0)
+   },
+   };
+   int32_t requestid;
+   char buf[1024];
+   size_t n;
+
+   ax_s = agentx_connect(axsocket);
+   sessionid1 = agentx_open(ax_s, 0, 0,
+   OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 27, 1),
+   "backend_getnext_instance_below_region_before_instance.1");
+   sessionid2 = agentx_open(ax_s, 0, 0,
+   OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 27, 2),
+   "backend_getnext_instance_below_region_before_instance.2");
+   agentx_register(ax_s, sessionid1, 0, 0, 127, 0,
+   OID_ARG(MIB_BACKEND_GETNEXT, 27), 0);
+   agentx_register(ax_s, sessionid2, 1, 0, 127, 0,
+   OID_ARG(MIB_BACKEND_GETNEXT, 27, 1, 0), 0);
+
+   salen = sn

Re: snmpd: reject OIDS equal to searchrange.end

2023-10-28 Thread Martijn van Duren
On Sat, 2023-10-28 at 09:45 +0200, Martijn van Duren wrote:
> RFC 2741 section 5.2 states that searchrange.end is non-inclusive.
> appl_varbind_valid() and appl_response() currently tests inclusive.
> The appl_varbind_valid() case is for backends that support
> searchrange.end (like agentx) and the appl_response() case for
> those who do not and need a fixup (basically everything else).
> 
> OK?
> 
> martijn
> 
Here's the regress part I forgot to add.

Index: Makefile
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/Makefile,v
retrieving revision 1.6
diff -u -p -r1.6 Makefile
--- Makefile27 Oct 2023 10:26:20 -  1.6
+++ Makefile28 Oct 2023 07:49:31 -
@@ -170,6 +170,7 @@ BACKEND_TARGETS+=   backend_getnext_stale
 BACKEND_TARGETS+=  backend_getnext_inclusive_backwards
 BACKEND_TARGETS+=  backend_getnext_toofew
 BACKEND_TARGETS+=  backend_getnext_toomany
+BACKEND_TARGETS+=  backend_getnext_response_equal_end
 BACKEND_TARGETS+=  backend_getbulk_nonrep_zero_maxrep_one
 BACKEND_TARGETS+=  backend_getbulk_nonrep_zero_maxrep_two
 BACKEND_TARGETS+=  backend_getbulk_nonrep_one_maxrep_one
Index: backend.c
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/backend.c,v
retrieving revision 1.1
diff -u -p -r1.1 backend.c
--- backend.c   24 Oct 2023 14:34:40 -  1.1
+++ backend.c   28 Oct 2023 07:49:31 -
@@ -2633,6 +2633,62 @@ backend_getnext_toomany(void)
 }
 
 void
+backend_getnext_response_equal_end(void)
+{
+   struct sockaddr_storage ss;
+   struct sockaddr *sa = (struct sockaddr *)&ss;
+   socklen_t salen;
+   int snmp_s, ax_s;
+   uint32_t sessionid1, sessionid2;
+   struct varbind varbind[] = {
+   {
+   .type = TYPE_NULL,
+   .name = OID_STRUCT(MIB_BACKEND_GETNEXT, 26),
+   .data.int32 = 1
+   },
+   };
+   struct searchrange searchrange[] = {
+   {
+   .start = OID_STRUCT(MIB_BACKEND_GETNEXT, 26),
+   .end = OID_STRUCT(MIB_BACKEND_GETNEXT, 26, 1, 1)
+   },
+   };
+   int32_t requestid;
+   char buf[1024];
+   size_t n;
+
+   ax_s = agentx_connect(axsocket);
+   sessionid1 = agentx_open(ax_s, 0, 0,
+   OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 26, 1),
+   "backend_getnext_end_equal.1");
+   sessionid2 = agentx_open(ax_s, 0, 0,
+   OID_ARG(MIB_SUBAGENT_BACKEND_GETNEXT, 26, 2),
+   "backend_getnext_end_equal.2");
+   agentx_register(ax_s, sessionid1, 0, 0, 127, 0,
+   OID_ARG(MIB_BACKEND_GETNEXT, 26), 0);
+   agentx_register(ax_s, sessionid2, 0, 0, 127, 0,
+   OID_ARG(MIB_BACKEND_GETNEXT, 26, 1, 1), 0);
+
+   salen = snmp_resolve(SOCK_DGRAM, hostname, servname, sa);
+   snmp_s = snmp_connect(SOCK_DGRAM, sa, salen);
+   requestid = snmpv2_getnext(snmp_s, community, 0, varbind, 1);
+
+   /* Fool agentx_getnext_handle() */
+   varbind[0].name.subid[varbind[0].name.n_subid++] = 1;
+   varbind[0].type = TYPE_INTEGER;
+   n = agentx_read(ax_s, buf, sizeof(buf), 1000);
+   agentx_getnext_handle(__func__, buf, n, 0, sessionid1, searchrange,
+   varbind, 1);
+   varbind[0].name = searchrange[0].end;
+   agentx_response(ax_s, buf, NOERROR, 0, varbind, 1);
+   varbind[0].type = TYPE_NULL;
+   varbind[0].name = OID_STRUCT(MIB_BACKEND_GETNEXT, 26),
+
+   snmpv2_response_validate(snmp_s, 1000, community, requestid, GENERR, 1,
+   varbind, 1);
+}
+
+void
 backend_getbulk_nonrep_zero_maxrep_one(void)
 {
struct sockaddr_storage ss;
Index: regress.h
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/regress.h,v
retrieving revision 1.2
diff -u -p -r1.2 regress.h
--- regress.h   27 Oct 2023 10:26:20 -  1.2
+++ regress.h   28 Oct 2023 07:49:31 -
@@ -293,6 +293,7 @@ void backend_getnext_stale(void);
 void backend_getnext_inclusive_backwards(void);
 void backend_getnext_toofew(void);
 void backend_getnext_toomany(void);
+void backend_getnext_response_equal_end(void);
 void backend_getbulk_nonrep_zero_maxrep_one(void);
 void backend_getbulk_nonrep_zero_maxrep_two(void);
 void backend_getbulk_nonrep_one_maxrep_one(void);
Index: snmpd_regress.c
===
RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd_regress.c,v
retrieving revision 1.2
diff -u -p -r1.2 snmpd_regress.c
--- snmpd_regress.c 27 Oct 2023 10:26:20 -  1.2
+++ snmpd_regress.c 28 Oct 2023 07:49:31 -
@@ -143,6 +143,7 @@ const struct {
{ "backend_getnext_inclusive_backwards", 
bac

snmpd: reject OIDS equal to searchrange.end

2023-10-28 Thread Martijn van Duren
RFC 2741 section 5.2 states that searchrange.end is non-inclusive.
appl_varbind_valid() and appl_response() currently tests inclusive.
The appl_varbind_valid() case is for backends that support
searchrange.end (like agentx) and the appl_response() case for
those who do not and need a fixup (basically everything else).

OK?

martijn

diff --git a/application.c b/application.c
index 33143d6..b3a2603 100644
--- a/application.c
+++ b/application.c
@@ -1100,7 +1100,7 @@ appl_response(struct appl_backend *backend, int32_t 
requestid,
 */
eomv |= !backend->ab_range && next &&
ober_oid_cmp(&(vb->av_oid),
-   &(origvb->avi_varbind.av_oid_end)) > 0;
+   &(origvb->avi_varbind.av_oid_end)) >= 0;
/* RFC 3584 section 4.2.2.1 */
if (ureq->aru_pduversion == SNMP_V1 &&
vb->av_value != NULL &&
@@ -1283,7 +1283,7 @@ appl_varbind_valid(struct appl_varbind *varbind,
}
}
if (range && ober_oid_cmp(&(varbind->av_oid),
-   &(request->avi_varbind.av_oid_end)) > 0) {
+   &(request->avi_varbind.av_oid_end)) >= 0) {
*errstr = "end oid not honoured";
return 0;
}



libagentx: don't return responses >= searchrange.end

2023-10-28 Thread Martijn van Duren
In most cases when a region is registered we have the full ownership.
As soon as a region has been registered below prior mentioned region we
could loose ownership halfway through. This case currently isn't fully
tested and with indices we can return OIDs >= searchrange.end.
The easiest way is to test this case is in agentx_varbind_finalize()
and simply reset a varbind to EOMV if we're outside our range.

I've pulled apart the agentx_request_type cases for clarity and control.

OK?

martijn@

Index: agentx.c
===
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.23
diff -u -p -r1.23 agentx.c
--- agentx.c24 Oct 2023 08:54:52 -  1.23
+++ agentx.c28 Oct 2023 07:40:59 -
@@ -2822,7 +2822,7 @@ getnext:
while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN)
axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo);
if (axo == NULL ||
-   ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) > 0) {
+   ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) >= 0) {
agentx_varbind_endofmibview(axv);
return;
}
@@ -3349,19 +3349,53 @@ agentx_varbind_finalize(struct agentx_va
 #endif
}
}
-   cmp = ax_oid_cmp(&(axv->axv_vb.avb_oid), &oid);
-   if ((agentx_varbind_request(axv) == AGENTX_REQUEST_TYPE_GETNEXT &&
-   cmp >= 0) || cmp > 0) {
+   cmp = ax_oid_cmp(&oid, &(axv->axv_vb.avb_oid));
+   switch (agentx_varbind_request(axv)) {
+   case AGENTX_REQUEST_TYPE_GET:
+   if (cmp != 0) {
 #ifdef AX_DEBUG
-   agentx_log_axg_fatalx(axg, "indices not incremented");
+   agentx_log_axg_fatalx(axg, "index changed");
 #else
-   agentx_log_axg_warnx(axg, "indices not incremented");
-   bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
-   sizeof(axv->axv_start));
-   axv->axv_error = AX_PDU_ERROR_GENERR;
+   agentx_log_axg_warnx(axg, "index changed");
+   bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
+   sizeof(axv->axv_start));
+   axv->axv_error = AX_PDU_ERROR_GENERR;
+   break;
 #endif
-   } else
+   }
+   break;
+   case AGENTX_REQUEST_TYPE_GETNEXT:
+   if (cmp <= 0) {
+#ifdef AX_DEBUG
+   agentx_log_axg_fatalx(axg, "indices not incremented");
+#else
+   agentx_log_axg_warnx(axg, "indices not incremented");
+   bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
+   sizeof(axv->axv_start));
+   axv->axv_error = AX_PDU_ERROR_GENERR;
+   break;
+#endif
+   }
+   /* FALLTHROUGH */
+   case AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE:
+   if (cmp < 0) {
+#ifdef AX_DEBUG
+   agentx_log_axg_fatalx(axg, "index decremented");
+#else
+   agentx_log_axg_warnx(axg, "index decremented");
+   bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
+   sizeof(axv->axv_start));
+   axv->axv_error = AX_PDU_ERROR_GENERR;
+   break;
+#endif
+   }
+   if (axv->axv_end.aoi_idlen != 0 &&
+   ax_oid_cmp(&oid, &(axv->axv_end)) >= 0) {
+   agentx_varbind_endofmibview(axv);
+   return;
+   }
bcopy(&oid, &(axv->axv_vb.avb_oid), sizeof(oid));
+   }
 done:
agentx_object_unlock(axv->axv_axo);
agentx_get_finalize(axv->axv_axg);



Re: snmpd; Fix use after free for appl_request_upstream

2023-10-27 Thread Martijn van Duren
On Thu, 2023-10-26 at 21:38 +0200, Theo Buehler wrote:
> On Thu, Oct 26, 2023 at 11:51:00AM +0200, Martijn van Duren wrote:
> > This case is covered by the new regress' backend_get_toofew and
> > backend_get_toomany tests. However, even with MALLOC_OPTIONS cranked
> > to the max it's really hard to trigger (I had to run
> > backend_get_wrongorder, backend_get_toofew, backend_get_toomany
> > sequentially in a tight loop killing snmpd between iterations for the
> > best chance).
> > 
> > When we receive an invalid varbindlist in a response we set the invalid
> > variable. This in turn calls appl_varbind_error(), but the avi_state
> > of the varbinds remains in APPL_VBSTATE_PENDING. Directly following
> > we call appl_request_downstream_free(), which sees that the varbinds
> > haven't been resolved, triggering a call to
> > appl_request_upstream_resolve(). This call in turn sees that the
> > error has been set and just sends out the error-response to the client
> > and frees the appl_request_upstream. From here we return back to
> > appl_response(), which also calls appl_request_upstream_resolve(),
> > resulting in a use after free.
> > 
> > The main tool for fixing this issue is making use of
> > appl_request_upstream's aru_locked member, which will cause
> > appl_request_upstream_resolve() to return instantly. The simplest fix is
> > to set aru_locked before calling appl_request_downstream_free() and
> > unsetting it directly afterwards inside appl_response().
> > 
> > The second one is the diff proposed below, which shrinks the code.
> > 
> > appl_request_upstream_free() is only called once from
> > appl_request_upstream_reply(). appl_request_upstream_reply() in turn
> > is only called by appl_request_upstream_resolve().
> > appl_request_upstream_resolve() is called in 3 places:
> > - appl_processpdu(): to kick things off
> > - appl_request_downstream_free(): For when a backend disappears with
> > outstanding requests
> > - appl_response(): To kickstart the next round of resolving.
> > 
> > Since appl_request_downstream_free() is always called from
> > appl_response(), we can leverage that function and make it call
> > appl_request_upstream_resolve() unconditionally.
> > 
> > appl_request_downstream_free() is called from the following locations:
> > - appl_close(): When a backend has disappeared.
> > - appl_request_upstream_free(): We send out a reply early, because an
> > error has been detected.
> > - appl_response(): We received a response
> > 
> > appl_request_upstream_free() can't reenter into
> > appl_request_upstream_resolve(), or it would potentially trigger new
> > appl_request_downstreams. This can be prevented by setting aru_locked
> > before calling appl_request_downstream_free().
> > For all other cases we should rely on appl_request_upstream_resolve()'s
> > logic to handle varbinds in any state, so there's no reason make calls
> > from other contexts conditional.
> 
> Your description of the bug makes sense and your choice of resolving it
> as well. Thanks for the in-depth explanation, that helped a lot. Still,
> I must say that I don't really feel at ease with the amount of complexity
> and entanglement here. I simply can't fit all of this into my head within
> a reasonable amount of time.
> 
Well, the S in SNMP doesn't stand for "I want to pull my hair out" for
no reason.

So for people interested, this is the high-over flow of the code:
When a message is received snmpe.c sends the PDU part to
appl_processpdu(). This function pulls apart the request in structs that
can be worked with internally (1 struct appl_request_upstream + n
struct appl_varbind_internal).
After things are pulled apart appl_processpdu() calls
appl_request_upstream_resolve(), which is responsible for retrieving
the values for the requested varbinds. It does this by going over all
varbinds in the APPL_VBSTATE_NEW state, and calling
appl_varbind_backend() to find a matching backend for the request. In
the case of a getnext/getbulk request it can also increment the oid
to the next matching region (if needed) and it also fills the end oid
where the ownership of the backend ends. After all APPL_VBSTATE_NEW
varbinds have found their backend, they are grouped together by backend
in a struct appl_request_downstream and send out via
appl_request_downstream_send().

Once a backend has a response available it calls appl_response(). This
function verifies the returned data via appl_varbind_valid() and
appl_error_valid(), and checks if the data matches the supported
datatypes (e.g. counter64 on SNMPv1). After that the data is put 

snmpd; Fix use after free for appl_request_upstream

2023-10-26 Thread Martijn van Duren
This case is covered by the new regress' backend_get_toofew and
backend_get_toomany tests. However, even with MALLOC_OPTIONS cranked
to the max it's really hard to trigger (I had to run
backend_get_wrongorder, backend_get_toofew, backend_get_toomany
sequentially in a tight loop killing snmpd between iterations for the
best chance).

When we receive an invalid varbindlist in a response we set the invalid
variable. This in turn calls appl_varbind_error(), but the avi_state
of the varbinds remains in APPL_VBSTATE_PENDING. Directly following
we call appl_request_downstream_free(), which sees that the varbinds
haven't been resolved, triggering a call to
appl_request_upstream_resolve(). This call in turn sees that the
error has been set and just sends out the error-response to the client
and frees the appl_request_upstream. From here we return back to
appl_response(), which also calls appl_request_upstream_resolve(),
resulting in a use after free.

The main tool for fixing this issue is making use of
appl_request_upstream's aru_locked member, which will cause
appl_request_upstream_resolve() to return instantly. The simplest fix is
to set aru_locked before calling appl_request_downstream_free() and
unsetting it directly afterwards inside appl_response().

The second one is the diff proposed below, which shrinks the code.

appl_request_upstream_free() is only called once from
appl_request_upstream_reply(). appl_request_upstream_reply() in turn
is only called by appl_request_upstream_resolve().
appl_request_upstream_resolve() is called in 3 places:
- appl_processpdu(): to kick things off
- appl_request_downstream_free(): For when a backend disappears with
outstanding requests
- appl_response(): To kickstart the next round of resolving.

Since appl_request_downstream_free() is always called from
appl_response(), we can leverage that function and make it call
appl_request_upstream_resolve() unconditionally.

appl_request_downstream_free() is called from the following locations:
- appl_close(): When a backend has disappeared.
- appl_request_upstream_free(): We send out a reply early, because an
error has been detected.
- appl_response(): We received a response

appl_request_upstream_free() can't reenter into
appl_request_upstream_resolve(), or it would potentially trigger new
appl_request_downstreams. This can be prevented by setting aru_locked
before calling appl_request_downstream_free().
For all other cases we should rely on appl_request_upstream_resolve()'s
logic to handle varbinds in any state, so there's no reason make calls
from other contexts conditional.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.24
diff -u -p -r1.24 application.c
--- application.c   24 Oct 2023 14:21:58 -  1.24
+++ application.c   26 Oct 2023 09:40:23 -
@@ -710,6 +710,7 @@ appl_request_upstream_free(struct appl_r
if (ureq == NULL)
return;
 
+   ureq->aru_locked = 1;
for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) {
vb = &(ureq->aru_vblist[i]);
ober_free_elements(vb->avi_varbind.av_value);
@@ -726,7 +727,6 @@ void
 appl_request_downstream_free(struct appl_request_downstream *dreq)
 {
struct appl_varbind_internal *vb;
-   int retry = 0;
 
if (dreq == NULL)
return;
@@ -736,14 +736,11 @@ appl_request_downstream_free(struct appl
 
for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) {
vb->avi_request_downstream = NULL;
-   if (vb->avi_state == APPL_VBSTATE_PENDING) {
+   if (vb->avi_state == APPL_VBSTATE_PENDING)
vb->avi_state = APPL_VBSTATE_NEW;
-   retry = 1;
-   }
}
 
-   if (retry)
-   appl_request_upstream_resolve(dreq->ard_request);
+   appl_request_upstream_resolve(dreq->ard_request);
free(dreq);
 }
 
@@ -1172,9 +1169,6 @@ appl_response(struct appl_backend *backe
backend->ab_name);
backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR);
}
-
-   if (ureq != NULL)
-   appl_request_upstream_resolve(ureq);
 }
 
 int



snmpd: Fix close after protocol error case

2023-10-26 Thread Martijn van Duren
So here's an elusive one that can be triggered every now and then by the
new regression test. Once an AgentX session is opened and we send an
invalid packet appl_agentx_recv() goes to appl_agentx_free(), since
there's no recovery. appl_agentx_free() tries to neatly close all
open sessions by sending a close-pdu, followed by calling
appl_agentx_send() directly.
However, if the socket has been closed in the meantime we hit
appl_agentx_send()'s error path, which also calls appl_agentx_free().
This in turn leads to use after free cases.

To fix this don't call appl_agentx_send() directly anymore, but just
schedule it via conn_wev. To make sure as much data as possible is
written out do a last unchecked courtesy flush before definitively
freeing the connection. Since appl_agentx_forceclose() arms conn_wev
move the event_del() calls down in appl_agentx_free().

Other calls of appl_agentx_send() should be fine, but just convert
all of them to be consistent and safe.

OK?

martijn@

Index: usr.sbin/snmpd/application_agentx.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v
retrieving revision 1.12
diff -u -p -r1.12 application_agentx.c
--- usr.sbin/snmpd/application_agentx.c 24 Oct 2023 14:11:14 -  1.12
+++ usr.sbin/snmpd/application_agentx.c 26 Oct 2023 08:43:02 -
@@ -254,9 +254,6 @@ appl_agentx_free(struct appl_agentx_conn
 {
struct appl_agentx_session *session;
 
-   event_del(&(conn->conn_rev));
-   event_del(&(conn->conn_wev));
-
while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
if (conn->conn_ax == NULL)
appl_agentx_session_free(session);
@@ -265,7 +262,12 @@ appl_agentx_free(struct appl_agentx_conn
reason);
}
 
+   event_del(&(conn->conn_rev));
+   event_del(&(conn->conn_wev));
+
RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
+   if (conn->conn_ax != NULL)
+   (void)ax_send(conn->conn_ax);
ax_free(conn->conn_ax);
if (conn->conn_backend)
fatalx("AgentX(%"PRIu32"): disappeared unexpected",
@@ -419,7 +421,7 @@ appl_agentx_recv(int fd, short event, vo
pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, smi_getticks(),
APPL_ERROR_NOERROR, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
break;
case AX_PDU_TYPE_INDEXALLOCATE:
case AX_PDU_TYPE_INDEXDEALLOCATE:
@@ -431,7 +433,7 @@ appl_agentx_recv(int fd, short event, vo
APPL_ERROR_PROCESSINGERROR, 1,
pdu->ap_payload.ap_vbl.ap_varbind,
pdu->ap_payload.ap_vbl.ap_nvarbind);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
break;
case AX_PDU_TYPE_ADDAGENTCAPS:
case AX_PDU_TYPE_REMOVEAGENTCAPS:
@@ -451,7 +453,7 @@ appl_agentx_recv(int fd, short event, vo
pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, smi_getticks(),
error, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
ax_pdu_free(pdu);
 
if (session == NULL || error != APPL_ERROR_PARSEERROR)
@@ -560,13 +562,13 @@ appl_agentx_open(struct appl_agentx_conn
ax_response(conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
 
return;
  fail:
ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
if (session != NULL)
free(session->sess_descr.aos_string);
free(session);
@@ -592,7 +594,7 @@ appl_agentx_close(struct appl_agentx_ses
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
smi_getticks(), error, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   event_add(&(conn->conn_wev), NULL);
if (error == APPL_ERROR_NOERROR)
return;
 
@@ -612,7 +614,7 @@ appl_agentx_forceclose(struct appl_backe
session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
ax_close(session->sess_conn->conn_ax, session->sess_id,
(enum ax_close_reason) reason);
-   appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+   event_add(&(session->sess_conn->conn_wev), NULL);
 
strlcpy(name, session->sess_backend.ab_name, sizeof(name));
   

snmpd: remove filter-pf-addresses support

2023-10-19 Thread Martijn van Duren
OpenBSD 7.4 is here. upgrade72.html already mentions it's deprecation.

OK?

martijn@

Index: parse.y
===
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.78
diff -u -p -r1.78 parse.y
--- parse.y 6 Oct 2022 14:41:08 -   1.78
+++ parse.y 19 Oct 2023 14:12:55 -
@@ -140,7 +140,7 @@ typedef struct {
 %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
 %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
 %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR
-%token HANDLE DEFAULT SRCADDR TCP UDP PFADDRFILTER BLOCKLIST PORT
+%token HANDLE DEFAULT SRCADDR TCP UDP BLOCKLIST PORT
 %token   STRING
 %token   NUMBER
 %typeusmuser community optcommunity
@@ -336,26 +336,6 @@ main   : LISTEN ON listen_udptcp
else
conf->sc_rtfilter = 0;
}
-   /* XXX Remove after 7.4 */
-   | PFADDRFILTER yesno{
-   struct ber_oid *blocklist;
-
-   log_warnx("filter-pf-addresses is deprecated. "
-   "Please use blocklist pfTblAddrTable instead.");
-   if ($2) {
-   blocklist = recallocarray(conf->sc_blocklist,
-   conf->sc_nblocklist,
-   conf->sc_nblocklist + 1,
-   sizeof(*blocklist));
-   if (blocklist == NULL) {
-   yyerror("malloc");
-   YYERROR;
-   }
-   conf->sc_blocklist = blocklist;
-   smi_string2oid("pfTblAddrTable",
-   &(blocklist[conf->sc_nblocklist++]));
-   }
-   }
| seclevel {
conf->sc_min_seclevel = $1;
}
@@ -1195,7 +1175,6 @@ lookup(char *s)
{ "enc",ENC },
{ "enckey", ENCKEY },
{ "engineid",   ENGINEID },
-   { "filter-pf-addresses",PFADDRFILTER },
{ "filter-routes",  RTFILTER },
{ "group",  GROUP },
{ "handle", HANDLE },



snmpd_metrics: differentiate between hrSWRunName and hrSWRunPath

2023-10-18 Thread Martijn van Duren
Right now we return the same value for both hrSWRunName and hrSWRunPath.
hrSWRunPath should return the full path of the binary, and hrSWRunName
a description of the running software.

Afaik there's no proper way to retrieve the full path of the running
binary, However, in a lot of cases argv[0] can contain the full or
relative path. But if argv[0] gets overwritten (like most of our
daemons' children) it gives a more descriptive name, which is more in
line with hrSWRunName.

netsnmp's snmpd uses argv[0] for hrSWRunPath and kinfo_proc's p_comm for
hrSWRunName, and snmptop defaults to hrSWRunName. top(1) also defaults
to p_comm, but contrary to top(1), snmptop allows us to switch between
hrSWRunName, and hrSWRunPath and toggling of hrSWRunParameters
independently, where top(1) toggles between p_comm and argv[].

So there's an argument to be made either way, but for this diff I stuck
with netsnmp's choices.

While here, change the buffer length from 128 to 129. HOST-RESOURCES-MIB
allows up to 128 characters in the response, so make room for the
terminating NUL.

Thoughts? OK?

martijn@

Index: mib.c
===
RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v
retrieving revision 1.4
diff -u -p -r1.4 mib.c
--- mib.c   4 Jul 2023 11:34:19 -   1.4
+++ mib.c   18 Oct 2023 11:47:00 -
@@ -103,7 +103,9 @@ int  kinfo_proc_comp(const void *, const
 int kinfo_proc(u_int32_t, struct kinfo_proc **);
 voidkinfo_timer_cb(int, short, void *);
 voidkinfo_proc_free(void);
-int kinfo_args(struct kinfo_proc *, char **);
+int kinfo_args(struct kinfo_proc *, char ***);
+int kinfo_path(struct kinfo_proc *, char **);
+int kinfo_parameters(struct kinfo_proc *, char **);
 
 /* IF-MIB */
 struct agentx_index *ifIdx;
@@ -669,13 +671,21 @@ mib_hrswrun(struct agentx_varbind *vb)
 
if (obj == hrSWRunIndex)
agentx_varbind_integer(vb, kinfo->p_pid);
-   else if (obj == hrSWRunName || obj == hrSWRunPath)
+   else if (obj == hrSWRunName)
agentx_varbind_string(vb, kinfo->p_comm);
-   else if (obj == hrSWRunID)
+   else if (obj == hrSWRunPath) {
+   if (kinfo_path(kinfo, &s) == -1) {
+   log_warn("kinfo_path");
+   agentx_varbind_error(vb);
+   return;
+   }
+   
+   agentx_varbind_string(vb, s);
+   } else if (obj == hrSWRunID)
agentx_varbind_oid(vb, AGENTX_OID(0, 0));
else if (obj == hrSWRunParameters) {
-   if (kinfo_args(kinfo, &s) == -1) {
-   log_warn("kinfo_args");
+   if (kinfo_parameters(kinfo, &s) == -1) {
+   log_warn("kinfo_parameters");
agentx_varbind_error(vb);
return;
}
@@ -811,25 +821,22 @@ kinfo_proc_free(void)
 }
 
 int
-kinfo_args(struct kinfo_proc *kinfo, char **s)
+kinfo_args(struct kinfo_proc *kinfo, char ***s)
 {
-   static char  str[128];
static char *buf = NULL;
static size_tbuflen = 128;
 
int  mib[] = { CTL_KERN, KERN_PROC_ARGS,
kinfo->p_pid, KERN_PROC_ARGV };
-   char*nbuf, **argv;
+   char*nbuf;
 
+   *s = NULL;
if (buf == NULL) {
buf = malloc(buflen);
if (buf == NULL)
return (-1);
}
 
-   str[0] = '\0';
-   *s = str;
-
while (sysctl(mib, nitems(mib), buf, &buflen, NULL, 0) == -1) {
if (errno != ENOMEM) {
/* some errors are expected, dont get too upset */
@@ -844,11 +851,41 @@ kinfo_args(struct kinfo_proc *kinfo, cha
buflen += 128;
}
 
-   argv = (char **)buf;
-   if (argv[0] == NULL)
-   return (0);
+   *s = (char **)buf;
+   return (0);
+}
+
+int
+kinfo_path(struct kinfo_proc *kinfo, char **s)
+{
+   static char  str[129];
+   char**argv;
+
+   if (kinfo_args(kinfo, &argv) == -1)
+   return (-1);
 
+   str[0] = '\0';
+   *s = str;
+   if (argv != NULL && argv[0] != NULL)
+   strlcpy(str, argv[0], sizeof(str));
+   return (0);
+}
+
+int
+kinfo_parameters(struct kinfo_proc *kinfo, char **s)
+{
+   static char  str[129];
+   char**argv;
+
+   if (kinfo_args(kinfo, &argv) == -1)
+   return (-1);
+
+   str[0] = '\0';
+   *s = str;
+   if (argv == NULL || argv[0] == NULL)
+   return (0);
argv++;
+
while (*argv != NULL) {
strlcat(str, *argv, sizeof(str));
argv++;



snmpd_metrics: add HOST-RESOURCES-MIB:hrSWRunPerfTable

2023-10-18 Thread Martijn van Duren
This diff adds the two entries from the hrSWRunPerfTable:
hrSWRunPerfCPU, and hrSWRunPerfMem. This allows snmptop from the
net-snmp package to work. Math stolen^Wcopied from top/machine.c.

OK?

martijn@

Index: mib.c
===
RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v
retrieving revision 1.4
diff -u -p -r1.4 mib.c
--- mib.c   4 Jul 2023 11:34:19 -   1.4
+++ mib.c   18 Oct 2023 09:52:07 -
@@ -69,6 +69,10 @@ struct event  connev;
 const char *agentxsocket = NULL;
 int agentxfd = -1;
 
+int pageshift;
+#define pagetok(size) ((size) << pageshift)
+
+voidpageshift_init(void);
 voidsnmp_connect(struct agentx *, void *, int);
 voidsnmp_tryconnect(int, short, void *);
 voidsnmp_read(int, short, void *);
@@ -89,6 +93,7 @@ struct agentx_object *hrProcessorLoad;
 struct agentx_index *hrSWRunIdx;
 struct agentx_object *hrSWRunIndex, *hrSWRunName, *hrSWRunID, *hrSWRunPath;
 struct agentx_object *hrSWRunParameters, *hrSWRunType, *hrSWRunStatus;
+struct agentx_object *hrSWRunPerfCPU, *hrSWRunPerfMem;
 
 voidmib_hrsystemuptime(struct agentx_varbind *);
 voidmib_hrsystemdate(struct agentx_varbind *);
@@ -634,6 +639,7 @@ mib_hrswrun(struct agentx_varbind *vb)
struct agentx_object*obj;
enum agentx_request_type req;
int32_t  idx;
+   int32_t  time;
struct kinfo_proc   *kinfo;
char*s;
 
@@ -711,6 +717,13 @@ mib_hrswrun(struct agentx_varbind *vb)
agentx_varbind_integer(vb, 4);
break;
}
+   } else if (obj == hrSWRunPerfCPU) {
+   time = kinfo->p_rtime_sec * 100;
+   time += (kinfo->p_rtime_usec + 5000) / 1;
+   agentx_varbind_integer(vb, time);
+   } else if (obj == hrSWRunPerfMem) {
+   agentx_varbind_integer(vb, pagetok(kinfo->p_vm_tsize +
+   kinfo->p_vm_dsize + kinfo->p_vm_ssize));
} else
fatal("%s: Unexpected object", __func__);
 }
@@ -3278,6 +3291,7 @@ main(int argc, char *argv[])
kr_init();
pf_init();
timer_init();
+   pageshift_init();
 
if (agentxsocket != NULL) {
if (strlcpy(agentxsocketdir, agentxsocket,
@@ -3375,6 +3389,10 @@ main(int argc, char *argv[])
(hrSWRunType = agentx_object(host, AGENTX_OID(HRSWRUNTYPE),
&hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL ||
(hrSWRunStatus = agentx_object(host, AGENTX_OID(HRSWRUNSTATUS),
+   &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL ||
+   (hrSWRunPerfCPU = agentx_object(host, AGENTX_OID(HRSWRUNPERFCPU),
+   &hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL ||
+   (hrSWRunPerfMem = agentx_object(host, AGENTX_OID(HRSWRUNPERFMEM),
&hrSWRunIdx, 1, 0, mib_hrswrun)) == NULL)
fatal("agentx_object");
 
@@ -4318,6 +4336,22 @@ main(int argc, char *argv[])
log_setverbose(verbose);
 
event_dispatch();
+}
+
+#define LOG1024 10
+void
+pageshift_init(void)
+{
+   long pagesize;
+
+   if ((pagesize = sysconf(_SC_PAGESIZE)) == -1)
+   fatal("sysconf(_SC_PAGESIZE)");
+   while (pagesize > 1) {
+   pageshift++;
+   pagesize >>= 1;
+   }
+   /* we only need the amount of log(2)1024 for our conversion */
+   pageshift -= LOG1024;
 }
 
 void



snmpd [16/16]: Keep track of exact registrations via AgentX

2023-10-17 Thread Martijn van Duren
This diff I'm least convinced about, but I do want to put it out there.
As far as I'm aware RFC2741 places no restrictions on direct mapping of
unregistrations on registrations. Meaning that if I register
1.3.6.1.2.[1-10] and I just unregister 1.3.6.1.2.5 this should leave
1.3.6.1.2.[1-4] and 1.3.6.1.2.[6-10] and this is what currently is
implemented (assuming previous (un)register diffs).

However, RFC2742 specifies AgentxRegistrationEntry, which presents
the the data as presented in the agentx-register-pdu. This implies
that an unregister is expected to be an exact counterpart to 
agentx-register-pdu, because else an AgentxRegistrationEntry would
need to be split up, which would be a horror-show.

Do we want to add this restriction and keep the road open to
AGENTX-MIB support? Since range support is currently broken anyway
this won't cause any problems.

Thoughts? OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 79900d6..60dc4df 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -70,11 +70,28 @@ struct appl_agentx_session {
struct ax_oid sess_oid;
struct ax_ostring sess_descr;
struct appl_backend sess_backend;
+   TAILQ_HEAD(, appl_agentx_registration) sess_registrations;
 
RB_ENTRY(appl_agentx_session) sess_entry;
TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
 };
 
+/* RFC2742: AGENTX-MIB:AgentxRegistrationEntry */
+struct appl_agentx_registration {
+   uint32_t reg_index;
+   char reg_context[APPL_CONTEXTNAME_MAX + 1];
+   struct ax_oid reg_start;
+   uint8_t reg_rangesubid;
+   uint32_t reg_upperbound;
+   uint8_t reg_priority;
+   uint8_t reg_timeout;
+   uint8_t reg_instance;
+
+   struct appl_agentx_session *reg_session;
+
+   TAILQ_ENTRY(appl_agentx_registration) reg_entry;
+};
+
 void appl_agentx_listen(struct agentx_master *);
 void appl_agentx_accept(int, short, void *);
 void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason);
@@ -106,6 +123,7 @@ struct appl_backend_functions appl_agentx_functions = {
.ab_getnext = appl_agentx_getnext,
.ab_getbulk = NULL, /* not properly supported in application.c and 
libagentx */
 };
+static uint32_t appl_agentx_reg_index = 1;
 
 RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
 RB_INITIALIZER(&appl_agentx_conns);
@@ -477,6 +495,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
goto fail;
}
session->sess_descr.aos_string = NULL;
+   TAILQ_INIT(&(session->sess_registrations));
 
session->sess_conn = conn;
if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
@@ -624,6 +643,12 @@ void
 appl_agentx_session_free(struct appl_agentx_session *session)
 {
struct appl_agentx_connection *conn = session->sess_conn;
+   struct appl_agentx_registration *reg;
+
+   while ((reg = TAILQ_FIRST(&(session->sess_registrations))) != NULL) {
+   TAILQ_REMOVE(&(session->sess_registrations), reg, reg_entry);
+   free(reg);
+   }
 
appl_close(&(session->sess_backend));
 
@@ -642,6 +667,7 @@ appl_agentx_register(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
struct ber_oid oid;
enum appl_error error;
int subtree = 0;
+   struct appl_agentx_registration *reg;
 
timeout = pdu->ap_payload.ap_register.ap_timeout;
timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
@@ -660,6 +686,13 @@ appl_agentx_register(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
goto fail;
}
 
+   if ((reg = malloc(sizeof(*reg))) == NULL) {
+   log_warn("%s: Failed to register",
+   session->sess_backend.ab_name);
+   error = APPL_ERROR_PROCESSINGERROR;
+   goto fail;
+   }
+
error = appl_register(pdu->ap_context.aos_string, timeout,
pdu->ap_payload.ap_register.ap_priority, &oid, 
pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION,
@@ -667,6 +700,26 @@ appl_agentx_register(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
pdu->ap_payload.ap_register.ap_upper_bound,
&(session->sess_backend));
 
+   if (error == APPL_ERROR_NOERROR) {
+   reg->reg_index = appl_agentx_reg_index++;
+   (void)strlcpy(reg->reg_context, pdu->ap_context.aos_slen == 0 ?
+   "" : (const char *)pdu->ap_context.aos_string,
+   sizeof(reg->reg_context));
+   reg->reg_start = pdu->ap_payload.ap_register.ap_subtree;
+   reg->reg_rangesubid =
+   pdu->ap_payload.ap_register.ap_range_subid;
+   reg->reg_upperbound =
+   pdu->ap_payload.ap_register.ap_upper_bound;
+   reg->reg_priority = pdu->ap_payload.ap_register.ap_priority;
+   reg->r

snmpd [15/16]: When we have an error, all oids must be identical to the request

2023-10-17 Thread Martijn van Duren
RFC3416 section 4.2.1 (and others) tells us that if an error occurs the
varbindlist in the response must be identical to the original request.

There might be other edge-cases here, but let's at least make sure that
the OIDs haven't been tampered with.

OK?

martijn@

diff --git a/application.c b/application.c
index 53e793d..d98918f 100644
--- a/application.c
+++ b/application.c
@@ -1186,6 +1186,9 @@ appl_varbind_valid(struct appl_varbind *varbind,
int cmp;
int eomv = 0;
 
+   if (null)
+   next = 0;
+
if (varbind->av_value == NULL) {
if (!null) {
*errstr = "missing value";



snmpd [14/16]: Validate the returned error code

2023-10-17 Thread Martijn van Duren


Certain error codes are only intended for certain request-types. Add an
appl_error_valid() function to test for this.

OK?

martijn@

diff --git a/application.c b/application.c
index 6ddeb39..53e793d 100644
--- a/application.c
+++ b/application.c
@@ -130,6 +130,7 @@ void appl_request_downstream_timeout(int, short, void *);
 void appl_request_upstream_reply(struct appl_request_upstream *);
 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *,
 int, int, int, const char **);
+int appl_error_valid(enum appl_error, enum snmp_pdutype);
 int appl_varbind_backend(struct appl_varbind_internal *);
 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
 void appl_report(struct snmp_message *, int32_t, struct ber_oid *,
@@ -1069,6 +1070,11 @@ appl_response(struct appl_backend *backend, int32_t 
requestid,
next = pdutype == SNMP_C_GETNEXTREQ ||
pdutype == SNMP_C_GETBULKREQ;
origvb = dreq->ard_vblist;
+   if (!appl_error_valid(error, dreq->ard_requesttype)) {
+   log_warnx("%s: %"PRIu32" Invalid error",
+   backend->ab_name, requestid);
+   invalid = 1;
+   }
}
 
vb = vblist;
@@ -1295,6 +1301,37 @@ appl_varbind_valid(struct appl_varbind *varbind,
return 1;
 }
 
+int
+appl_error_valid(enum appl_error error, enum snmp_pdutype type)
+{
+   switch (error) {
+   case APPL_ERROR_NOERROR:
+   case APPL_ERROR_TOOBIG:
+   case APPL_ERROR_NOSUCHNAME:
+   case APPL_ERROR_GENERR:
+   return 1;
+   case APPL_ERROR_BADVALUE:
+   case APPL_ERROR_READONLY:
+   case APPL_ERROR_NOACCESS:
+   case APPL_ERROR_WRONGTYPE:
+   case APPL_ERROR_WRONGLENGTH:
+   case APPL_ERROR_WRONGENCODING:
+   case APPL_ERROR_WRONGVALUE:
+   case APPL_ERROR_NOCREATION:
+   case APPL_ERROR_INCONSISTENTVALUE:
+   case APPL_ERROR_RESOURCEUNAVAILABLE:
+   case APPL_ERROR_COMMITFAILED:
+   case APPL_ERROR_UNDOFAILED:
+   case APPL_ERROR_NOTWRITABLE:
+   case APPL_ERROR_INCONSISTENTNAME:
+   return type == SNMP_C_SETREQ;
+   case APPL_ERROR_AUTHORIZATIONERROR:
+   return type == SNMP_C_GETREQ || type == SNMP_C_SETREQ;
+   default:
+   return 0;
+   }
+}
+
 int
 appl_varbind_backend(struct appl_varbind_internal *ivb)
 {



snmpd [13/16]: registered instances must not return below OID

2023-10-17 Thread Martijn van Duren
If a backend registers as an instance it must never return OIDs below
their registration. Add a test for this in appl_varbind_valid().

OK?

martijn@

diff --git a/application.c b/application.c
index dfa7220..6ddeb39 100644
--- a/application.c
+++ b/application.c
@@ -128,8 +128,8 @@ void appl_request_upstream_resolve(struct 
appl_request_upstream *);
 void appl_request_downstream_send(struct appl_request_downstream *);
 void appl_request_downstream_timeout(int, short, void *);
 void appl_request_upstream_reply(struct appl_request_upstream *);
-int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int,
-int, const char **);
+int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *,
+int, int, int, const char **);
 int appl_varbind_backend(struct appl_varbind_internal *);
 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
 void appl_report(struct snmp_message *, int32_t, struct ber_oid *,
@@ -1073,9 +1073,9 @@ appl_response(struct appl_backend *backend, int32_t 
requestid,
 
vb = vblist;
for (i = 1; vb != NULL; vb = vb->av_next, i++) {
-if (!appl_varbind_valid(vb, origvb == NULL ?
-   NULL : &(origvb->avi_varbind), next,
-error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) {
+if (!appl_varbind_valid(vb, origvb == NULL ?  NULL : origvb,
+   next, error != APPL_ERROR_NOERROR, backend->ab_range,
+   &errstr)) {
smi_oid2string(&(vb->av_oid), oidbuf,
sizeof(oidbuf), 0);
log_warnx("%s: %"PRIu32" %s: %s",
@@ -1173,8 +1173,9 @@ appl_response(struct appl_backend *backend, int32_t 
requestid,
 }
 
 int
-appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request,
-int next, int null, int range, const char **errstr)
+appl_varbind_valid(struct appl_varbind *varbind,
+struct appl_varbind_internal *request, int next, int null, int range,
+const char **errstr)
 {
int cmp;
int eomv = 0;
@@ -1259,24 +1260,32 @@ appl_varbind_valid(struct appl_varbind *varbind, struct 
appl_varbind *request,
if (request == NULL)
return 1;
 
-   cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid));
-   if (next && !eomv) {
-   if (request->av_include) {
-   if (cmp > 0) {
-   *errstr = "oid not incrementing";
-   return 0;
+   cmp = ober_oid_cmp(&(request->avi_varbind.av_oid), &(varbind->av_oid));
+   if (next) {
+   if (request->avi_region->ar_instance &&
+   ober_oid_cmp(&(request->avi_region->ar_oid),
+   &(varbind->av_oid)) != 0) {
+   *errstr = "oid below instance";
+   return 0;
+   }
+   if (!eomv) {
+   if (request->avi_varbind.av_include) {
+   if (cmp > 0) {
+   *errstr = "oid not incrementing";
+   return 0;
+   }
+   } else {
+   if (cmp >= 0) {
+   *errstr = "oid not incrementing";
+   return 0;
+   }
}
-   } else {
-   if (cmp >= 0) {
-   *errstr = "oid not incrementing";
+   if (range && ober_oid_cmp(&(varbind->av_oid),
+   &(request->avi_varbind.av_oid_end)) > 0) {
+   *errstr = "end oid not honoured";
return 0;
}
}
-   if (range && ober_oid_cmp(&(varbind->av_oid),
-   &(request->av_oid_end)) > 0) {
-   *errstr = "end oid not honoured";
-   return 0;
-   }
} else {
if (cmp != 0) {
*errstr = "oids not equal";



snmpd [12/16]: Make ab_range in application_agentx explicit 1

2023-10-17 Thread Martijn van Duren
appl_agentx_session doesn't set ab_range explicitly to 1, and thus
relies on malloc randomness to set it. Sit it explicitly.

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 680725d..79900d6 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -548,6 +548,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
session->sess_backend.ab_cookie = session;
session->sess_backend.ab_retries = 0;
session->sess_backend.ab_fn = &appl_agentx_functions;
+   session->sess_backend.ab_range = 1;
RB_INIT(&(session->sess_backend.ab_requests));
TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
 



snmpd [11/16]: When a request results in EOMV we must return the requesting OID

2023-10-17 Thread Martijn van Duren
According to RFC3416 section 4.2.2 and 4.2.3 case "(2)" when an
endOfMibView is returned the OID must be identical to originally
requested OID. Currently this can fail when the original request
is in a !last registered region and all subsequent regions also
return EOMV.

Store the original OID and use that on EOMV.

OK?

martijn@

diff --git a/application.c b/application.c
index e780025..dfa7220 100644
--- a/application.c
+++ b/application.c
@@ -99,6 +99,7 @@ struct appl_varbind_internal {
enum appl_varbind_state avi_state;
struct appl_varbind avi_varbind;
struct appl_region *avi_region;
+   struct ber_oid avi_origid;
int16_t avi_index;
struct appl_request_upstream *avi_request_upstream;
struct appl_request_downstream *avi_request_downstream;
@@ -679,6 +680,8 @@ appl_processpdu(struct snmp_message *statereference, const 
char *ctxname,
}
ober_get_oid(varbind->be_sub,
&(ureq->aru_vblist[i].avi_varbind.av_oid));
+   ureq->aru_vblist[i].avi_origid =
+   ureq->aru_vblist[i].avi_varbind.av_oid;
if (i + 1 < ureq->aru_varbindlen) {
ureq->aru_vblist[i].avi_next =
&(ureq->aru_vblist[i + 1]);
@@ -1008,6 +1011,10 @@ appl_request_upstream_reply(struct appl_request_upstream 
*ureq)
vb = &(ureq->aru_vblist[i]);
vb->avi_varbind.av_next =
&(ureq->aru_vblist[i + 1].avi_varbind);
+   value = vb->avi_varbind.av_value;
+   if (value->be_class == BER_CLASS_CONTEXT &&
+   value->be_type == APPL_EXC_ENDOFMIBVIEW)
+   vb->avi_varbind.av_oid = vb->avi_origid;
}
 
ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL;



snmpd [10/16]: Make retries on open session where connection is closed return early

2023-10-17 Thread Martijn van Duren
Here's a special case unlikely to be found in the wild:
When opening 2 sessions on an agentx connection (already unusual) and
registering 2 overlapping regions on the different sessions, e.g. by
differing in priority (even more unusual) and we close the underlying
connection with an outstanding request to the dominant region we will
call appl_agentx_free(), which sequentially closes all sessions.
If the session with the outstanding request is closed before the
second session the request is retried before said session is cleaned
up and it will try to send it over a conn_ax which at that point has
been set to NULL, resulting in a SIGSEGV.

Simply return early and let this second request be cancelled by the
cleanup of the second session.

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 2231d4c..680725d 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -712,6 +712,9 @@ appl_agentx_get(struct appl_backend *backend, int32_t 
transactionid,
struct ax_searchrange *srl;
size_t i, j, nsr;
 
+   if (session->sess_conn->conn_ax == NULL)
+   return;
+
for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
nsr++;
 
@@ -760,6 +763,9 @@ appl_agentx_getnext(struct appl_backend *backend, int32_t 
transactionid,
struct ax_searchrange *srl;
size_t i, j, nsr;
 
+   if (session->sess_conn->conn_ax == NULL)
+   return;
+
for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
nsr++;
 



snmpd [9/16]: Fix range handling with appl_unregister

2023-10-17 Thread Martijn van Duren
Right now (un)registering a region with range_subid set to !0 will
fail. Apparently nothing in the wild uses this, but let's fix it.

This is the unregister part.

OK? martijn@

diff --git a/application.c b/application.c
index f8709b8..e780025 100644
--- a/application.c
+++ b/application.c
@@ -115,6 +115,8 @@ struct snmp_target_mib {
 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t,
 struct ber_oid *, int, int, struct appl_backend *);
 void appl_region_free(struct appl_context *, struct appl_region *);
+enum appl_error appl_region_unregister_match(struct appl_context *, uint8_t,
+struct ber_oid *, char *, struct appl_backend *, int);
 struct appl_region *appl_region_find(struct appl_context *,
 const struct ber_oid *);
 struct appl_region *appl_region_next(struct appl_context *,
@@ -400,9 +402,10 @@ enum appl_error
 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid,
 uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend)
 {
-   struct appl_region *region, search;
struct appl_context *ctx;
char oidbuf[1024], subidbuf[11];
+   enum appl_error error;
+   uint32_t lower_bound;
size_t i;
 
oidbuf[0] = '\0';
@@ -444,34 +447,45 @@ appl_unregister(const char *ctxname, uint8_t priority, 
struct ber_oid *oid,
return APPL_ERROR_PARSEERROR;
}
 
-   if (range_subid > oid->bo_n) {
+   if (range_subid == 0)
+   return appl_region_unregister_match(ctx, priority, oid, oidbuf,
+   backend, 1);
+
+   range_subid--;
+   if (range_subid >= oid->bo_n) {
log_warnx("%s: Can't unregiser %s: range_subid too large",
backend->ab_name, oidbuf);
return APPL_ERROR_PARSEERROR;
}
-   if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
-   log_warnx("%s: Can't unregister %s: upper bound smaller or "
-   "equal to range_subid", backend->ab_name, oidbuf);
+   if (oid->bo_id[range_subid] > upper_bound) {
+   log_warnx("%s: Can't unregister %s: upper bound smaller than "
+   "range_subid", backend->ab_name, oidbuf);
return APPL_ERROR_PARSEERROR;
}
 
+   lower_bound = oid->bo_id[range_subid];
+   do {
+   if ((error = appl_region_unregister_match(ctx, priority, oid,
+   oidbuf, backend, 0)) != APPL_ERROR_NOERROR)
+   return error;
+   } while (oid->bo_id[range_subid]++ != upper_bound);
+
+   oid->bo_id[range_subid] = lower_bound;
+   do {
+   (void)appl_region_unregister_match(ctx, priority, oid, oidbuf,
+   backend, 1);
+   } while (oid->bo_id[range_subid]++ != upper_bound);
+
+   return APPL_ERROR_NOERROR;
+}
+
+enum appl_error
+appl_region_unregister_match(struct appl_context *ctx, uint8_t priority,
+struct ber_oid *oid, char *oidbuf, struct appl_backend *backend, int 
dofree)
+{
+   struct appl_region *region, search;
+
search.ar_oid = *oid;
-   while (range_subid != 0 &&
-   search.ar_oid.bo_id[range_subid] != upper_bound) {
-   region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
-   while (region != NULL && region->ar_priority < priority)
-   region = region->ar_next;
-   if (region == NULL || region->ar_priority != priority) {
-   log_warnx("%s: Can't unregister %s: region not found",
-   backend->ab_name, oidbuf);
-   return APPL_ERROR_UNKNOWNREGISTRATION;
-   }
-   if (region->ar_backend != backend) {
-   log_warnx("%s: Can't unregister %s: region not owned "
-   "by backend", backend->ab_name, oidbuf);
-   return APPL_ERROR_UNKNOWNREGISTRATION;
-   }
-   }
region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
while (region != NULL && region->ar_priority < priority)
region = region->ar_next;
@@ -485,20 +499,8 @@ appl_unregister(const char *ctxname, uint8_t priority, 
struct ber_oid *oid,
"by backend", backend->ab_name, oidbuf);
return APPL_ERROR_UNKNOWNREGISTRATION;
}
-
-   search.ar_oid = *oid;
-   while (range_subid != 0 &&
-   search.ar_oid.bo_id[range_subid] != upper_bound) {
-   region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
-   while (region != NULL && region->ar_priority != priority)
-   region = region->ar_next;
+   if (dofree)
appl_region_free(ctx, region);
-   }
-   region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
-   while (region != NULL && region->ar_priority != priority)
-   region = region->ar_next;
-  

snmpd [8/16]: Fix range handling with appl_register

2023-10-17 Thread Martijn van Duren
Right now (un)registering a region with range_subid set to !0 will
fail. Apparently nothing in the wild uses this, but let's fix it.

This is the register part.

OK?

martijn@

diff --git a/application.c b/application.c
index a02260b..f8709b8 100644
--- a/application.c
+++ b/application.c
@@ -347,28 +347,29 @@ appl_register(const char *ctxname, uint32_t timeout, 
uint8_t priority,
backend->ab_name, oidbuf);
return APPL_ERROR_PARSEERROR;
}
-   if (range_subid > oid->bo_n) {
+
+   if (range_subid == 0)
+   return appl_region(ctx, timeout, priority, oid, instance,
+   subtree, backend);
+
+   range_subid--;
+   if (range_subid >= oid->bo_n) {
log_warnx("%s: Can't register %s: range_subid too large",
backend->ab_name, oidbuf);
return APPL_ERROR_PARSEERROR;
}
-   if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
-   log_warnx("%s: Can't register %s: upper bound smaller or equal "
-   "to range_subid", backend->ab_name, oidbuf);
+   if (oid->bo_id[range_subid] > upper_bound) {
+   log_warnx("%s: Can't register %s: upper bound smaller than "
+   "range_subid", backend->ab_name, oidbuf);
return APPL_ERROR_PARSEERROR;
}
-   if (range_subid != 0)
-   lower_bound = oid->bo_id[range_subid];
-
-   if (range_subid == 0)
-   return appl_region(ctx, timeout, priority, oid, instance,
-   subtree, backend);
 
+   lower_bound = oid->bo_id[range_subid];
do {
if ((error = appl_region(ctx, timeout, priority, oid, instance,
subtree, backend)) != APPL_ERROR_NOERROR)
goto fail;
-   } while (oid->bo_id[range_subid] != upper_bound);
+   } while (oid->bo_id[range_subid]++ != upper_bound);
if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree,
backend)) != APPL_ERROR_NOERROR)
goto fail;



snmpd [7/16]: Treat agentx-close-pdu with reasonByManager as a parseerror

2023-10-17 Thread Martijn van Duren


RFC2741 section 6.2.2 says that reasonByManager can only be used by the
agentx master. Treat this reason as a parseerror.

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index d1efae2..2231d4c 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -576,16 +576,29 @@ appl_agentx_close(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
 {
struct appl_agentx_connection *conn = session->sess_conn;
char name[100];
+   enum appl_error error = APPL_ERROR_NOERROR;
 
strlcpy(name, session->sess_backend.ab_name, sizeof(name));
-   appl_agentx_session_free(session);
-   log_info("%s: Closed by subagent (%s)", name,
-   ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
+   if (pdu->ap_payload.ap_close.ap_reason == AX_CLOSE_BYMANAGER) {
+   log_warnx("%s: Invalid close reason", name);
+   error = APPL_ERROR_PARSEERROR;
+   } else {
+   appl_agentx_session_free(session);
+   log_info("%s: Closed by subagent (%s)", name,
+   ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
+   }
 
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
-   smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
+   smi_getticks(), error, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
+   if (error == APPL_ERROR_NOERROR)
+   return;
+
+   appl_agentx_forceclose(&(session->sess_backend),
+   APPL_CLOSE_REASONPARSEERROR);
+   if (TAILQ_EMPTY(&(conn->conn_sessions)))
+   appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
 }
 
 void



snmpd [6/16]: support close reason for appl_agentx_free

2023-10-17 Thread Martijn van Duren
appl_agentx_free() closes any potential open sessions before closing the
connection and cleaning up. This function is called from multiple
contexts and the current APPL_CLOSE_REASONSHUTDOWN is not always
applicable. Add a second reason parameter that can be passed onto
appl_agentx_forceclose().

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 6a4ed49..d1efae2 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -77,7 +77,7 @@ struct appl_agentx_session {
 
 void appl_agentx_listen(struct agentx_master *);
 void appl_agentx_accept(int, short, void *);
-void appl_agentx_free(struct appl_agentx_connection *);
+void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason);
 void appl_agentx_recv(int, short, void *);
 void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
 void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
@@ -178,7 +178,7 @@ appl_agentx_shutdown(void)
struct appl_agentx_connection *conn, *tconn;
 
RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
-   appl_agentx_free(conn);
+   appl_agentx_free(conn, APPL_CLOSE_REASONSHUTDOWN);
 }
 
 void
@@ -249,7 +249,8 @@ appl_agentx_backend(int fd)
 }
 
 void
-appl_agentx_free(struct appl_agentx_connection *conn)
+appl_agentx_free(struct appl_agentx_connection *conn,
+enum appl_close_reason reason)
 {
struct appl_agentx_session *session;
 
@@ -261,7 +262,7 @@ appl_agentx_free(struct appl_agentx_connection *conn)
appl_agentx_session_free(session);
else
appl_agentx_forceclose(&(session->sess_backend),
-   APPL_CLOSE_REASONSHUTDOWN);
+   reason);
}
 
RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
@@ -294,7 +295,8 @@ appl_agentx_recv(int fd, short event, void *cookie)
ax_free(conn->conn_ax);
conn->conn_ax = NULL;
}
-   appl_agentx_free(conn);
+   appl_agentx_free(conn, errno == EPROTO ?
+   APPL_CLOSE_REASONPROTOCOLERROR : APPL_CLOSE_REASONOTHER);
return;
}
 
@@ -458,7 +460,7 @@ appl_agentx_recv(int fd, short event, void *cookie)
appl_agentx_forceclose(&(session->sess_backend),
APPL_CLOSE_REASONPARSEERROR);
if (TAILQ_EMPTY(&(conn->conn_sessions)))
-   appl_agentx_free(conn);
+   appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
 }
 
 void
@@ -847,7 +849,7 @@ appl_agentx_send(int fd, short event, void *cookie)
log_warn("AgentX(%"PRIu32")", conn->conn_id);
ax_free(conn->conn_ax);
conn->conn_ax = NULL;
-   appl_agentx_free(conn);
+   appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
return;
case 0:
return;



snmpd [5/16]: Check context existence in appl_agentx_recv

2023-10-17 Thread Martijn van Duren
application.c checks the context where applicable, but not every
agentx-pdu goes through there (e.g. agentx-ping-pdu). Make sure
we always check the context in appl_agentx_recv()

OK?

martijn@

diff --git a/application.c b/application.c
index dd92864..a02260b 100644
--- a/application.c
+++ b/application.c
@@ -175,7 +175,7 @@ appl_shutdown(void)
}
 }
 
-static struct appl_context *
+struct appl_context *
 appl_context(const char *name, int create)
 {
struct appl_context *ctx;
diff --git a/application.h b/application.h
index 8b2c567..949bbde 100644
--- a/application.h
+++ b/application.h
@@ -121,6 +121,7 @@ struct appl_backend {
 void appl(void);
 void appl_init(void);
 void appl_shutdown(void);
+struct appl_context *appl_context(const char *, int);
 enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid 
*,
 int, int, uint8_t, uint32_t, struct appl_backend *);
 enum appl_error appl_unregister(const char *, uint8_t, struct ber_oid *,
diff --git a/application_agentx.c b/application_agentx.c
index c11b666..6a4ed49 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -370,6 +370,12 @@ appl_agentx_recv(int fd, short event, void *cookie)
error = APPL_ERROR_PARSEERROR;
goto fail;
}
+   if (appl_context(pdu->ap_context.aos_string, 0) == 0) {
+   log_warnx("%s: %s: Unsupported context",
+   name, ax_pdutype2string(pdu->ap_header.aph_flags));
+   error = APPL_ERROR_UNSUPPORTEDCONTEXT;
+   goto fail;
+   }
}
switch (pdu->ap_header.aph_type) {
case AX_PDU_TYPE_OPEN:



snmpd [4/16]: check agentx-pdu-header flags for validity

2023-10-17 Thread Martijn van Duren
RFC2741 section 6.1 specifies which PDUs can contain which header flags.
Check that that incoming agentx PDUs have valid flags in
appl_agentx_recv(). While here I cleaned up a few log messages some
minor restructuring to prevent the function growing too large.

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 9cc98eb..c11b666 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -276,13 +276,16 @@ void
 appl_agentx_recv(int fd, short event, void *cookie)
 {
struct appl_agentx_connection *conn = cookie;
-   struct appl_agentx_session *session;
+   struct appl_agentx_session *session = NULL;
struct ax_pdu *pdu;
+   enum appl_error error;
+   char name[100];
 
+   snprintf(name, sizeof(name), "AgentX(%"PRIu32")", conn->conn_id);
if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
if (errno == EAGAIN)
return;
-   log_warn("AgentX(%"PRIu32")", conn->conn_id);
+   log_warn("%s", name);
/*
 * Either the connection is dead, or we had garbage on the line.
 * Both make sure we can't continue on this stream.
@@ -306,16 +309,12 @@ appl_agentx_recv(int fd, short event, void *cookie)
break;
}
if (session == NULL) {
-   log_warnx("AgentX(%"PRIu32"): Session %"PRIu32" not "
-   "found for request", conn->conn_id,
-   pdu->ap_header.aph_sessionid);
-   ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
-   pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, smi_getticks(),
-   APPL_ERROR_NOTOPEN, 0, NULL, 0);
-   appl_agentx_send(-1, EV_WRITE, conn);
+   log_warnx("%s: Session %"PRIu32" not found for request",
+   name, pdu->ap_header.aph_sessionid);
+   error = APPL_ERROR_NOTOPEN;
goto fail;
}
+   strlcpy(name, session->sess_backend.ab_name, sizeof(name));
/*
 * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
 * the response should be. My best guess is that it makes more
@@ -327,6 +326,51 @@ appl_agentx_recv(int fd, short event, void *cookie)
 */
}
 
+   if (pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION) {
+   if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER) {
+   log_warnx("%s: %s: Invalid INSTANCE_REGISTRATION flag",
+   name, ax_pdutype2string(pdu->ap_header.aph_flags));
+   error = APPL_ERROR_PARSEERROR;
+   goto fail;
+   }
+   }
+   if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NEW_INDEX) {
+   if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
+   log_warnx("%s: %s: Invalid NEW_INDEX flag", name,
+   ax_pdutype2string(pdu->ap_header.aph_flags));
+   error = APPL_ERROR_PARSEERROR;
+   goto fail;
+   }
+   }
+   if (pdu->ap_header.aph_flags & AX_PDU_FLAG_ANY_INDEX) {
+   if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
+   log_warnx("%s: %s: Invalid ANY_INDEX flag", name,
+   ax_pdutype2string(pdu->ap_header.aph_flags));
+   error = APPL_ERROR_PARSEERROR;
+   goto fail;
+   }
+   }
+   if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
+   if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_UNREGISTER &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_ADDAGENTCAPS &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_REMOVEAGENTCAPS &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_GET &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_GETNEXT &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_NOTIFY &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_TESTSET &&
+   pdu->ap_header.aph_type != AX_PDU_TYPE_PING) {
+   log_warnx("%s: %s: Invalid NON_DEFAULT_CONTEXT flag",
+   name, ax_pdutype2string(pdu->ap_header.ap

snmpd [3/16]: Distinguish between parseerror and openfailed for agentx-open-pdu

2023-10-17 Thread Martijn van Duren
RFC2741 section 7.1.1 tells us that if a pdu can't be parsed we must
return a parseerror. Section 7.1 gives an example of "An unrecognized
value is encountered". The spec is vague is a bit vague on what
constitutes a parseerror, vs a protocol error, so I don't want to
muddle too much with that part, but let's at least return an
appropriate error when a client sends invalid data in the open request.

The spec clearly states that any error, which is not a parseerror must
be a openfailed error. So no processingerrors.

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index 0d73e08..9cc98eb 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -411,9 +411,11 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
struct appl_agentx_session *session;
struct ber_oid oid;
char oidbuf[1024];
+   enum appl_error error = APPL_ERROR_NOERROR;
 
if ((session = malloc(sizeof(*session))) == NULL) {
log_warn(NULL);
+   error = APPL_ERROR_OPENFAILED;
goto fail;
}
session->sess_descr.aos_string = NULL;
@@ -432,12 +434,14 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
} else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
conn->conn_id);
+   error = APPL_ERROR_PARSEERROR;
goto fail;
}
/* RFC 2742 agentxSessionDescr */
if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
"Failed", conn->conn_id);
+   error = APPL_ERROR_PARSEERROR;
goto fail;
}
/*
@@ -451,6 +455,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
"Open Failed", conn->conn_id);
+   error = APPL_ERROR_PARSEERROR;
goto fail;
}
 
@@ -463,6 +468,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
if (session->sess_descr.aos_string == NULL) {
log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
conn->conn_id);
+   error = APPL_ERROR_OPENFAILED;
goto fail;
}
}
@@ -478,6 +484,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
conn->conn_id, session->sess_id) == -1) {
log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
conn->conn_id);
+   error = APPL_ERROR_OPENFAILED;
goto fail;
}
session->sess_backend.ab_cookie = session;
@@ -499,7 +506,7 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
return;
  fail:
ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, 0, APPL_ERROR_OPENFAILED, 0, NULL, 0);
+   pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
if (session != NULL)
free(session->sess_descr.aos_string);



snmpd [2.2/16]: Don't set NON_DEFAULT_CONTEXT for agentx-response-pdu

2023-10-17 Thread Martijn van Duren
> According to RFC2741 section 6.1.1 an agentx-response-pdu shouldn't have
> the NON_DEFAULT_CONTEXT set. Remove the argument from ax_response().

Here's the libagentx counterpart.

OK?

martijn@

Index: agentx.c
===
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.22
diff -u -p -r1.22 agentx.c
--- agentx.c27 Dec 2022 17:10:05 -  1.22
+++ agentx.c9 Oct 2023 20:24:40 -
@@ -2733,8 +2733,7 @@ agentx_get_finalize(struct agentx_get *a
free(logmsg);
 
if (ax_response(ax->ax_ax, axs->axs_id, axg->axg_transactionid,
-   axg->axg_packetid, AGENTX_CONTEXT_CTX(axc), 0, error, index,
-   vbl, nvarbind) == -1) {
+   axg->axg_packetid, 0, error, index, vbl, nvarbind) == -1) {
agentx_log_axg_warn(axg, "Couldn't parse request");
agentx_reset(ax);
} else
@@ -4041,7 +4040,6 @@ agentx_read(struct agentx *ax)
if (ax_response(ax->ax_ax, axs->axs_id,
pdu->ap_header.aph_transactionid,
pdu->ap_header.aph_packetid,
-   axc == NULL ? NULL : AGENTX_CONTEXT_CTX(axc),
0, error, 1, NULL, 0) == -1)
agentx_log_axc_warn(axc,
"transaction: %u packetid: %u: failed to send "
Index: ax.c
===
RCS file: /cvs/src/lib/libagentx/ax.c,v
retrieving revision 1.8
diff -u -p -r1.8 ax.c
--- ax.c24 Oct 2021 17:43:38 -  1.8
+++ ax.c9 Oct 2023 20:24:40 -
@@ -553,11 +553,11 @@ ax_unregister(struct ax *ax, uint32_t se
 
 int
 ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
-uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime,
-uint16_t error, uint16_t index, struct ax_varbind *vblist, size_t nvb)
+uint32_t packetid, uint32_t sysuptime, uint16_t error, uint16_t index,
+struct ax_varbind *vblist, size_t nvb)
 {
if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid,
-   transactionid, packetid, context) == -1)
+   transactionid, packetid, NULL) == -1)
return -1;
 
if (ax_pdu_add_uint32(ax, sysuptime) == -1 ||
Index: ax.h
===
RCS file: /cvs/src/lib/libagentx/ax.h,v
retrieving revision 1.4
diff -u -p -r1.4 ax.h
--- ax.h2 Jan 2021 01:06:31 -   1.4
+++ ax.h9 Oct 2023 20:24:40 -
@@ -220,8 +220,7 @@ uint32_t ax_register(struct ax *, uint8_
 uint32_t ax_unregister(struct ax *, uint32_t, struct ax_ostring *,
 uint8_t, uint8_t, struct ax_oid *, uint32_t);
 int ax_response(struct ax *, uint32_t, uint32_t, uint32_t,
-struct ax_ostring *, uint32_t, uint16_t, uint16_t,
-struct ax_varbind *, size_t);
+uint32_t, uint16_t, uint16_t, struct ax_varbind *, size_t);
 void ax_pdu_free(struct ax_pdu *);
 void ax_varbind_free(struct ax_varbind *);
 const char *ax_error2string(enum ax_pdu_error);



Re: snmpd [1.1/16]: Don't overflow oid in agentx parser

2023-10-17 Thread Martijn van Duren
> Currently ax.c doesn't check the maximum length of an OID ax_pdutooid.
> This can lead to a buffer overflow. Even though it must be fixed, I
> don't think there's a big risk here, since an attacker would need to have
> access to the agentx socket, which by default is disabled and defaults
> to root:_agentx when enabled.

Here's the libagentx counterpart.

OK?

martijn@

Index: ax.c
===
RCS file: /cvs/src/lib/libagentx/ax.c,v
retrieving revision 1.8
diff -u -p -r1.8 ax.c
--- ax.c24 Oct 2021 17:43:38 -  1.8
+++ ax.c9 Oct 2023 20:14:13 -
@@ -1262,6 +1262,8 @@ ax_pdutooid(struct ax_pdu_header *header
}
buf++;
oid->aoi_include = *buf;
+   if (oid->aoi_idlen > AX_OID_MAX_LEN)
+   goto fail;
for (buf += 2; i < oid->aoi_idlen; i++, buf += 4)
oid->aoi_id[i] = ax_pdutoh32(header, buf);
 



snmpd [2.1/16]: Don't set NON_DEFAULT_CONTEXT for agentx-response-pdu

2023-10-17 Thread Martijn van Duren
According to RFC2741 section 6.1.1 an agentx-response-pdu shouldn't have
the NON_DEFAULT_CONTEXT set. Remove the argument from ax_response().

OK?

martijn@

diff --git a/application_agentx.c b/application_agentx.c
index c7a2a26..0d73e08 100644
--- a/application_agentx.c
+++ b/application_agentx.c
@@ -311,8 +311,7 @@ appl_agentx_recv(int fd, short event, void *cookie)
pdu->ap_header.aph_sessionid);
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid,
-   &(pdu->ap_context), smi_getticks(),
+   pdu->ap_header.aph_packetid, smi_getticks(),
APPL_ERROR_NOTOPEN, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
goto fail;
@@ -370,8 +369,8 @@ appl_agentx_recv(int fd, short event, void *cookie)
case AX_PDU_TYPE_PING:
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, &(pdu->ap_context),
-   smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
+   pdu->ap_header.aph_packetid, smi_getticks(),
+   APPL_ERROR_NOERROR, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
break;
case AX_PDU_TYPE_INDEXALLOCATE:
@@ -380,8 +379,8 @@ appl_agentx_recv(int fd, short event, void *cookie)
ax_pdutype2string(pdu->ap_header.aph_type));
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, &(pdu->ap_context),
-   smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
+   pdu->ap_header.aph_packetid, smi_getticks(),
+   APPL_ERROR_PROCESSINGERROR, 1,
pdu->ap_payload.ap_vbl.ap_varbind,
pdu->ap_payload.ap_vbl.ap_nvarbind);
appl_agentx_send(-1, EV_WRITE, conn);
@@ -392,8 +391,8 @@ appl_agentx_recv(int fd, short event, void *cookie)
ax_pdutype2string(pdu->ap_header.aph_type));
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, &(pdu->ap_context),
-   smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
+   pdu->ap_header.aph_packetid, smi_getticks(),
+   APPL_ERROR_PROCESSINGERROR, 1,
NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
break;
@@ -492,16 +491,15 @@ appl_agentx_open(struct appl_agentx_connection *conn, 
struct ax_pdu *pdu)
log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
session->sess_descr.aos_string);
 
-   ax_response(conn->conn_ax, session->sess_id, 
pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, NULL, smi_getticks(), 
APPL_ERROR_NOERROR, 0,
-   NULL, 0);
+   ax_response(conn->conn_ax, session->sess_id,
+   pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
+   smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
 
return;
  fail:
ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
-   pdu->ap_header.aph_packetid, NULL, 0, APPL_ERROR_OPENFAILED, 0,
-   NULL, 0);
+   pdu->ap_header.aph_packetid, 0, APPL_ERROR_OPENFAILED, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
if (session != NULL)
free(session->sess_descr.aos_string);
@@ -521,7 +519,7 @@ appl_agentx_close(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
 
ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
-   &(pdu->ap_context), smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
+   smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, conn);
 }
 
@@ -593,7 +591,7 @@ appl_agentx_register(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
  fail:
ax_response(session->sess_conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
-   &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0);
+   smi_getticks(), error, 0, NULL, 0);
appl_agentx_send(-1, EV_WRITE, session->sess_conn);
 }
 
@@ -620,7 +618,7 @@ appl_agentx_unregister(struct appl_agentx_session *session, 
struct ax_pdu *pdu)
  fail:
ax_response(session->sess_conn->conn_ax, session->sess_id,
pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid

snmpd [1.1/16]: Don't overflow oid in agentx parser

2023-10-17 Thread Martijn van Duren
Currently ax.c doesn't check the maximum length of an OID ax_pdutooid.
This can lead to a buffer overflow. Even though it must be fixed, I
don't think there's a big risk here, since an attacker would need to have
access to the agentx socket, which by default is disabled and defaults
to root:_agentx when enabled.

OK?

martijn@

diff --git a/ax.c b/ax.c
index 63add68..27580a6 100644
--- a/ax.c
+++ b/ax.c
@@ -1442,6 +1442,8 @@ ax_pdutooid(struct ax_pdu_header *header, struct ax_oid 
*oid,
}
buf++;
oid->aoi_include = *buf;
+   if (oid->aoi_idlen > AX_OID_MAX_LEN)
+   goto fail;
for (buf += 2; i < oid->aoi_idlen; i++, buf += 4)
oid->aoi_id[i] = ax_pdutoh32(header, buf);
 



Re: ober_scanf_elements() and empty sequences

2023-08-22 Thread Martijn van Duren
On Tue, 2023-08-22 at 10:16 +, Gerhard Roth wrote:
> On Tue, 2023-08-22 at 11:16 +0200, Martijn van Duren wrote:
> > On Mon, 2023-08-21 at 07:35 +, Gerhard Roth wrote:
> > > Hi Martijn,
> > > 
> > > last November you fixed ber.c so that sequences won't generate
> > > an uninitialized subelement.
> > > 
> > > This revealed another bug in ober_scanf_elements(): it couldn't
> > > process sequences with an empty list of subelements. The following
> > > code failed in ober_scanf_elements():
> > > 
> > > struct ber_element  *root;
> > > struct ber_element  *sub;
> > > 
> > > if ((root = ober_add_sequence(NULL)) == NULL)
> > > err(1, "ober_add_sequence() failed");
> > > 
> > > errno = 0;
> > > if (ober_scanf_elements(root, "{e", &sub))
> > > err(1, "ober_scanf_elements() failed");
> > > 
> > > printf("sub = %p\n", sub);
> > > 
> > > 
> > > The patch below fixes that.
> > > 
> > > Gerhard
> > > 
> > > 
> > > Index: lib/libutil/ber.c
> > > ===
> > > RCS file: /cvs/src/lib/libutil/ber.c,v
> > > retrieving revision 1.24
> > > diff -u -p -u -p -r1.24 ber.c
> > > --- lib/libutil/ber.c   3 Nov 2022 17:58:10 -   1.24
> > > +++ lib/libutil/ber.c   21 Aug 2023 07:24:21 -
> > > @@ -700,7 +700,8 @@ ober_scanf_elements(struct ber_element *
> > >  
> > > va_start(ap, fmt);
> > > while (*fmt) {
> > > -   if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != 
> > > ')')
> > > +   if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != 
> > > ')' &&
> > > +   *fmt != 'e')
> > > goto fail;
> > 
> > I'm not sure about this part. An ober_scanf_elements of "{}e" on your
> > example above also fails. The 'e' element might not increment the ber
> > pointer, but I do think it should fail if an expected element is
> > missing.
> 
> You're right. And digging into it, only makes it look worse.
> 
> I think, the 'e' format should behave differently.
> 
> 1) in case of '{e' it is the first element of a sequence.
>   Empty sequences are allowed and we should not fail but
>   set the argument to NULL instead.
> 
> 2) in all other cases, 'e' is the next element in the list.
>   And here we should fail if there are less elements in the
>   list than expected.
> 
> Below is an updated diff that fixes the problem by remembering
> whether the last traversal was via 'be_sub' (fsub == 1) or 'be_next'
> and then behaves differently. Not very elegant although.

I'm not sure yet. Do you have a particular usecase for this?
Usually you would just do something like
ober_scanf_elements(elm, "e{", seq);
if (seq->be_sub != NULL) ...

> 
> > 
> > > switch (*fmt++) {
> > > case '$':
> > > @@ -797,7 +798,7 @@ ober_scanf_elements(struct ber_element *
> > > if (ber->be_encoding != BER_TYPE_SEQUENCE &&
> > >     ber->be_encoding != BER_TYPE_SET)
> > > goto fail;
> > > -   if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
> > > +   if (level >= _MAX_SEQ-1)
> > 
> > This part is OK martijn@
> > 
> > > goto fail;
> > > parent[++level] = ber;
> > > ber = ber->be_sub;
> > > 
> > 
> 
> Index: lib/libutil/ber.c
> ===
> RCS file: /cvs/src/lib/libutil/ber.c,v
> retrieving revision 1.24
> diff -u -p -u -p -r1.24 ber.c
> --- lib/libutil/ber.c 3 Nov 2022 17:58:10 -   1.24
> +++ lib/libutil/ber.c 22 Aug 2023 10:10:43 -
> @@ -695,12 +695,14 @@ ober_scanf_elements(struct ber_element *
>   off_t   *pos;
>   struct ber_oid  *o;
>   struct ber_element  *parent[_MAX_SEQ], **e;
> + int   

Re: ober_scanf_elements() and empty sequences

2023-08-22 Thread Martijn van Duren
On Mon, 2023-08-21 at 07:35 +, Gerhard Roth wrote:
> Hi Martijn,
> 
> last November you fixed ber.c so that sequences won't generate
> an uninitialized subelement.
> 
> This revealed another bug in ober_scanf_elements(): it couldn't
> process sequences with an empty list of subelements. The following
> code failed in ober_scanf_elements():
> 
>   struct ber_element  *root;
>   struct ber_element  *sub;
> 
>   if ((root = ober_add_sequence(NULL)) == NULL)
>   err(1, "ober_add_sequence() failed");
> 
>   errno = 0;
>   if (ober_scanf_elements(root, "{e", &sub))
>   err(1, "ober_scanf_elements() failed");
> 
>   printf("sub = %p\n", sub);
> 
> 
> The patch below fixes that.
> 
> Gerhard
> 
> 
> Index: lib/libutil/ber.c
> ===
> RCS file: /cvs/src/lib/libutil/ber.c,v
> retrieving revision 1.24
> diff -u -p -u -p -r1.24 ber.c
> --- lib/libutil/ber.c 3 Nov 2022 17:58:10 -   1.24
> +++ lib/libutil/ber.c 21 Aug 2023 07:24:21 -
> @@ -700,7 +700,8 @@ ober_scanf_elements(struct ber_element *
>  
>   va_start(ap, fmt);
>   while (*fmt) {
> - if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')')
> + if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')' &&
> + *fmt != 'e')
>   goto fail;

I'm not sure about this part. An ober_scanf_elements of "{}e" on your
example above also fails. The 'e' element might not increment the ber
pointer, but I do think it should fail if an expected element is
missing.

>   switch (*fmt++) {
>   case '$':
> @@ -797,7 +798,7 @@ ober_scanf_elements(struct ber_element *
>   if (ber->be_encoding != BER_TYPE_SEQUENCE &&
>   ber->be_encoding != BER_TYPE_SET)
>   goto fail;
> - if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
> + if (level >= _MAX_SEQ-1)

This part is OK martijn@

>   goto fail;
>   parent[++level] = ber;
>   ber = ber->be_sub;
> 



ber.c: Don't best effort on unknown encodings

2022-12-23 Thread Martijn van Duren
This one has been irking me for some time. At the moment when decoding a
ber message there is no way for br_application to indicate that the
tag/type is unrecognised and if br_application is not set we default to
BER_TYPE_NULL. In basically all cases any wrong encoding should be
caught by ober_scanf_elements and friends, but I'd rather fail sooner
than later.

By scanning the tree I found BER_TYPE_DEFAULT only used by ber.c, so I
decided to rename it to BER_TYPE_UNKNOWN so that it can be used as a
failure return value from br_application. The value of -1 can safely be
used here, since get_id() allows max sizeof(unsigned int) iterations and
each iteration adds 7 bits to the value, so we never reach negative
numbers through natural means.

In a similar fashion: don't simply ignore elements in
ober_write_elements() if the encoding is not recognised.

ober_set_application() is currently not documented, so on this front
there is no update, I think this should be done as a separate endeavour.

I've put this diff through snmp, snmpd, ldapd, libutil regress without
problems, but I can't fully rule out any fallout (valid, or invalid).

thoughts? OK?

martijn@

Index: ber.c
===
RCS file: /cvs/src/lib/libutil/ber.c,v
retrieving revision 1.24
diff -u -p -r1.24 ber.c
--- ber.c   3 Nov 2022 17:58:10 -   1.24
+++ ber.c   23 Dec 2022 11:42:38 -
@@ -64,7 +64,7 @@ ober_get_element(unsigned int encoding)
return NULL;
 
elm->be_encoding = encoding;
-   ober_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
+   ober_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_UNKNOWN);
 
return elm;
 }
@@ -73,7 +73,7 @@ void
 ober_set_header(struct ber_element *elm, int class, unsigned int type)
 {
elm->be_class = class & BER_CLASS_MASK;
-   if (type == BER_TYPE_DEFAULT)
+   if (type == BER_TYPE_UNKNOWN)
type = elm->be_encoding;
elm->be_type = type;
 }
@@ -899,7 +899,7 @@ ober_read_elements(struct ber *ber, stru
struct ber_element *root = elm;
 
if (root == NULL) {
-   if ((root = ober_get_element(0)) == NULL)
+   if ((root = ober_get_element(BER_TYPE_UNKNOWN)) == NULL)
return NULL;
}
 
@@ -1078,6 +1078,9 @@ ober_dump_element(struct ber *ber, struc
if (root->be_sub && ober_dump_element(ber, root->be_sub) == -1)
return -1;
break;
+   default:
+   errno = EINVAL;
+   return -1;
}
 
if (root->be_next == NULL)
@@ -1291,7 +1294,7 @@ ober_read_element(struct ber *ber, struc
elm->be_offs = ber->br_offs;/* element position within stream */
elm->be_class = class;
 
-   if (elm->be_encoding == 0) {
+   if (elm->be_encoding == BER_TYPE_UNKNOWN) {
/* try to figure out the encoding via class, type and cstruct */
if (cstruct)
elm->be_encoding = BER_TYPE_SEQUENCE;
@@ -1304,9 +1307,12 @@ ober_read_element(struct ber *ber, struc
 * type is defined as 4 byte OCTET STRING.
 */
elm->be_encoding = (*ber->br_application)(elm);
-   } else
-   /* last resort option */
-   elm->be_encoding = BER_TYPE_NULL;
+   }
+
+   if (elm->be_encoding == BER_TYPE_UNKNOWN) {
+   errno = EINVAL;
+   return -1;
+   }
}
 
switch (elm->be_encoding) {
@@ -1376,7 +1382,8 @@ ober_read_element(struct ber *ber, struc
case BER_TYPE_SEQUENCE:
case BER_TYPE_SET:
if (len > 0 && elm->be_sub == NULL) {
-   if ((elm->be_sub = ober_get_element(0)) == NULL)
+   elm->be_sub = ober_get_element(BER_TYPE_UNKNOWN);
+   if (elm->be_sub == NULL)
return -1;
}
next = elm->be_sub;
@@ -1403,7 +1410,8 @@ ober_read_element(struct ber *ber, struc
elements++;
len -= r;
if (len > 0 && next->be_next == NULL) {
-   next->be_next = ober_get_element(0);
+   next->be_next =
+   ober_get_element(BER_TYPE_UNKNOWN);
if (next->be_next == NULL)
return -1;
}
Index: ber.h
===
RCS file: /cvs/src/lib/libutil/ber.h,v
retrieving revision 1.5
diff -u -p -r1.5 ber.h
--- ber.h   31 Oct 2021 16:42:08 -  1.5
+++ ber.h   23 Dec 2022 11:42:38 -
@@ -58,7 +58,7 @@ struct ber {
 };
 
 /* well-known ber_element types */
-#d

Re: 7.2: snmp mibtree command broken

2022-12-20 Thread Martijn van Duren
On Tue, 2022-12-20 at 10:28 +, Gerhard Roth wrote:
> Hi Martijn,
> 
> On Tue, 2022-12-20 at 11:10 +0100, Martijn van Duren wrote:
> > On Tue, 2022-12-20 at 10:21 +0100, Matthias Pitzl wrote:
> > > Hi,
> > > 
> > > Since the release of OpenBSD 7.2, snmp mibtree is broken:
> > > > root@host:~# snmp mibtree
> > > > snmp: No securityName specified
> > > 
> > > Greetings,
> > > Matthias
> > 
> > Apparently no one has used this one in a long time (including me).
> > This got broken in snmpc.c r1.37 on 2021/08/11 when changing snmp(1)'s
> > default to v3.
> > 
> > This may not the prettiest solution, but this is the shortest I can come
> > up with.
> 
> no sub-command that hasn't 'snmp_app->usecommonopt' set can have any of
> the SNMPv2 or SNMPv3 credentials. Sure, currently 'mibtree' is the only
> one. But for future updates, wouldn't it be better to check
> 
>   if (!snmp_app->usecommonopt)
> 
> instead of
> 
>   if (strcmp(snmp_app->name, "mibtree") == 0)
> 
> Greetings,
> 
> Gerhard
> 
> 
You're right, that's better.

Index: snmpc.c
===
RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v
retrieving revision 1.38
diff -u -p -r1.38 snmpc.c
--- snmpc.c 21 Oct 2021 08:17:34 -  1.38
+++ snmpc.c 20 Dec 2022 10:33:58 -
@@ -468,7 +468,9 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
 
-   if (version == SNMP_V1 || version == SNMP_V2C) {
+   if (!snmp_app->usecommonopt) {
+   /* No SNMP protocol settings */
+   } else if (version == SNMP_V1 || version == SNMP_V2C) {
if (community == NULL || community[0] == '\0')
errx(1, "No community name specified.");
} else if (version == SNMP_V3) {



Re: 7.2: snmp mibtree command broken

2022-12-20 Thread Martijn van Duren
On Tue, 2022-12-20 at 10:21 +0100, Matthias Pitzl wrote:
> Hi,
> 
> Since the release of OpenBSD 7.2, snmp mibtree is broken:
> > root@host:~# snmp mibtree
> > snmp: No securityName specified
> 
> Greetings,
> Matthias

Apparently no one has used this one in a long time (including me).
This got broken in snmpc.c r1.37 on 2021/08/11 when changing snmp(1)'s
default to v3.

This may not the prettiest solution, but this is the shortest I can come
up with.

martijn@

Index: snmpc.c
===
RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v
retrieving revision 1.38
diff -u -p -r1.38 snmpc.c
--- snmpc.c 21 Oct 2021 08:17:34 -  1.38
+++ snmpc.c 20 Dec 2022 10:08:29 -
@@ -468,7 +468,9 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
 
-   if (version == SNMP_V1 || version == SNMP_V2C) {
+   if (strcmp(snmp_app->name, "mibtree") == 0) {
+   /* No SNMP protocol settings */
+   } else if (version == SNMP_V1 || version == SNMP_V2C) {
if (community == NULL || community[0] == '\0')
errx(1, "No community name specified.");
} else if (version == SNMP_V3) {



tcpdump(8): grok Alexey Kuznetzov's modified libpcap format.

2022-12-01 Thread Martijn van Duren
I received a pcap file from someone containing kuznetzov's pcap format.
According to "upstream" libpcap[0] this format uses
struct pcap_sf_patched_pkthdr, which is:
struct pcap_sf_patched_pkthdr {
struct pcap_timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len;/* length of this packet (off wire) */
int index;
unsigned short protocol;
unsigned char pkt_type;
};

So compared to a normal pkthdr this adds[1]:
- index: interface index
- protocol: ethernet packet type
- pkt_type: broadcast/multicast/etc. indication

I don't think this information is particularly useful per say, so why
not just skip the 8 bytes and continue business as usual?

No public header change and allows me read the dump.

OK?

martijn@

[0] https://www.tcpdump.org/
[1] https://wiki.wireshark.org/Development/LibpcapFileFormat#modified-pcap

Index: usr.sbin/tcpdump/privsep.h
===
RCS file: /cvs/src/usr.sbin/tcpdump/privsep.h,v
retrieving revision 1.12
diff -u -p -r1.12 privsep.h
--- usr.sbin/tcpdump/privsep.h  18 Mar 2019 00:09:22 -  1.12
+++ usr.sbin/tcpdump/privsep.h  1 Dec 2022 19:53:47 -
@@ -19,8 +19,6 @@
 
 #include 
 
-#define TCPDUMP_MAGIC 0xa1b2c3d4
-
 enum cmd_types {
PRIV_OPEN_BPF,  /* open a bpf descriptor */
PRIV_OPEN_DUMP, /* open dump file for reading */
Index: usr.sbin/tcpdump/privsep_pcap.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/privsep_pcap.c,v
retrieving revision 1.25
diff -u -p -r1.25 privsep_pcap.c
--- usr.sbin/tcpdump/privsep_pcap.c 28 Jun 2019 13:32:51 -  1.25
+++ usr.sbin/tcpdump/privsep_pcap.c 1 Dec 2022 19:53:47 -
@@ -334,6 +334,7 @@ priv_pcap_live(const char *dev, int slen
 static void
 swap_hdr(struct pcap_file_header *hp)
 {
+   hp->magic = swap32(hp->magic);
hp->version_major = swap16(hp->version_major);
hp->version_minor = swap16(hp->version_minor);
hp->thiszone = swap32(hp->thiszone);
@@ -390,20 +391,25 @@ priv_pcap_offline(const char *fname, cha
goto bad;
}
 
-   if (hdr.magic != TCPDUMP_MAGIC) {
-   if (swap32(hdr.magic) != TCPDUMP_MAGIC) {
-   snprintf(errbuf, PCAP_ERRBUF_SIZE,
-   "bad dump file format");
-   goto bad;
-   }
+   switch (hdr.magic) {
+   case TCPDUMP_MAGIC:
+   case TCPDUMP_MAGIC_KUZNETZOV:
+   break;
+   case swap32(TCPDUMP_MAGIC):
+   case swap32(TCPDUMP_MAGIC_KUZNETZOV):
p->sf.swapped = 1;
swap_hdr(&hdr);
+   break;
+   default:
+   snprintf(errbuf, PCAP_ERRBUF_SIZE, "bad dump file format");
+   goto bad;
}
if (hdr.version_major < PCAP_VERSION_MAJOR) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
goto bad;
}
 
+   p->sf.magic = hdr.magic;
p->tzoff = hdr.thiszone;
p->snapshot = hdr.snaplen;
p->linktype = hdr.linktype;
Index: lib/libpcap/pcap-int.h
===
RCS file: /cvs/src/lib/libpcap/pcap-int.h,v
retrieving revision 1.14
diff -u -p -r1.14 pcap-int.h
--- lib/libpcap/pcap-int.h  5 Apr 2018 03:47:27 -   1.14
+++ lib/libpcap/pcap-int.h  1 Dec 2022 19:53:47 -
@@ -51,6 +51,9 @@ struct pcap_opt {
int immediate;  /* immediate mode - deliver packets as soon as 
they arrive */
 };
 
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+#define TCPDUMP_MAGIC_KUZNETZOV 0xa1b2cd34
+
 /*
  * Savefile
  */
@@ -59,6 +62,7 @@ struct pcap_sf {
int swapped;
int version_major;
int version_minor;
+   uint32_t magic;
u_char *base;
 };
 
Index: lib/libpcap/savefile.c
===
RCS file: /cvs/src/lib/libpcap/savefile.c,v
retrieving revision 1.17
diff -u -p -r1.17 savefile.c
--- lib/libpcap/savefile.c  27 May 2020 04:24:01 -  1.17
+++ lib/libpcap/savefile.c  1 Dec 2022 19:53:47 -
@@ -45,8 +45,6 @@
 
 #include "pcap-int.h"
 
-#define TCPDUMP_MAGIC 0xa1b2c3d4
-
 /*
  * We use the "receiver-makes-right" approach to byte order,
  * because time is at a premium when we are writing the file.
@@ -90,6 +88,7 @@ sf_write_header(FILE *fp, int linktype, 
 static void
 swap_hdr(struct pcap_file_header *hp)
 {
+   hp->magic = SWAPLONG(hp->magic);
hp->version_major = SWAPSHORT(hp->version_major);
hp->version_minor = SWAPSHORT(hp->version_minor);
hp->thiszone = SWAPLONG(hp->thiszone);
@@ -145,19 +144,24 @@ pcap_fopen_offline(FILE *fp, char *errbu
pcap_strerror(errno));
goto bad;
}
-   if (hdr.magic != TCPDUMP_MAGIC) {
-   if 

Re: netstart(8): remove sed

2022-11-23 Thread Martijn van Duren
On Wed, 2022-11-23 at 10:03 +, Klemens Nanni wrote:
> On Wed, Nov 23, 2022 at 10:48:22AM +0100, Martijn van Duren wrote:
> > On Wed, 2022-11-23 at 09:25 +, Klemens Nanni wrote:
> > > On Wed, Nov 23, 2022 at 10:15:20AM +0100, Martijn van Duren wrote:
> > > > Here's an attempt to remove sed from netstart.
> > > 
> > > I don't see the point in this.
> > 
> > On Mon, 2022-11-21 at 20:42 -0700, Theo de Raadt wrote:
> > > Oh, except that using grep, head, or awk.  That breaks NFS diskless
> > > machines, because they are in /usr, which may not be mounted yet.
> > > 
> > > So this has to be done using shell features or commands in /bin and /sbin.
> > > You will see these interesting hacks in other parts of rc and netstart.
> > > 
> > > 
> > 
> > https://marc.info/?l=openbsd-misc&m=166908823920806&w=2
> 
> Ah, NFS /usr.
> 
> Instead of rolling our own shell functions, maybe this is a good reason
> to implement ${parameter/pattern/string} in ksh(1)?
> 
> This should make shell scripting in base a lot easier where awk/sed is
> not available;  the installer could probably use this as well.
> 
> At least Bash has this (and other pattern substitution) syntax.
> 
Maybe... I'm not a big fan of feature-bloat in the shell and this
function is small enough, so personally I see no reason. But if you
write it and no one else objects why not...



Re: netstart(8): remove sed

2022-11-23 Thread Martijn van Duren
On Wed, 2022-11-23 at 09:25 +, Klemens Nanni wrote:
> On Wed, Nov 23, 2022 at 10:15:20AM +0100, Martijn van Duren wrote:
> > Here's an attempt to remove sed from netstart.
> 
> I don't see the point in this.

On Mon, 2022-11-21 at 20:42 -0700, Theo de Raadt wrote:
> Oh, except that using grep, head, or awk.  That breaks NFS diskless
> machines, because they are in /usr, which may not be mounted yet.
> 
> So this has to be done using shell features or commands in /bin and /sbin.
> You will see these interesting hacks in other parts of rc and netstart.
> 
> 

https://marc.info/?l=openbsd-misc&m=166908823920806&w=2



netstart(8): remove sed

2022-11-23 Thread Martijn van Duren
Here's an attempt to remove sed from netstart. Since we use sed in a
simple string replacement without any fancy regex stuff I think we can
relatively easy use something based on shell built-ins.

Risk of the current code is that if someone places search inside
replacement we get an infinite loop, but since both search and
replacement are under our control I don't think it's worth making the
code larger than needed.

Only lightly tested

thoughts?

martijn@

Index: netstart
===
RCS file: /cvs/src/etc/netstart,v
retrieving revision 1.229
diff -u -p -r1.229 netstart
--- netstart5 Nov 2022 12:06:05 -   1.229
+++ netstart23 Nov 2022 09:12:28 -
@@ -35,6 +35,18 @@ stripcom() {
done <$_file
 }
 
+# Usage: gsub search replace line
+gsub() {
+   local _s="$1" _r="$2" _l
+   shift 2
+   _l="$@"
+
+   while [[ "$_l" == *$_s* ]]; do
+   _l="${_l%%$_s*}$_r${_l#*$_s}"
+   done
+   print -n -- "$_l"
+}
+
 # Parse and "unpack" a hostname.if(5) line given as positional parameters.
 # Fill the _cmds array with the resulting interface configuration commands.
 parse_hn_line() {
@@ -82,7 +94,7 @@ parse_hn_line() {
dhcp)   _cmds[${#_cmds[*]}]="ifconfig $_if inet autoconf"
V4_AUTOCONF=true
;;
-   '!'*)   _cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g')
+   '!'*)   _cmd=$(gsub '$if' $_if "${_c[*]}")
_cmds[${#_cmds[*]}]="${_cmd#!}"
;;
*)  _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"



Re: lladdr support for netstart/hostname.if

2022-11-22 Thread Martijn van Duren
On Tue, 2022-11-22 at 11:25 +0100, Claudio Jeker wrote:
> On Tue, Nov 22, 2022 at 09:25:08AM +, Stuart Henderson wrote:
> > Need to query (and set $if, which might be used in route commands etc) I 
> > think.
> > 
> 
> I would prefer if people took a step back from configuring interfaces by
> MAC address. It feels like a overly specific hack is introduced for
> a case that should be handled in a different way.
> 
> Not all interfaces have MAC addresses. E.g. umb(4) would need a
> different identifier (most probably the IMEI). Some interfaces inherit
> the MAC address from an other interface (vlan, trunk).
> 
> This requires the use of interface groups to 'rename' these interfaces
> e.g. as 'green' and 'blue' or 'in' and 'out'. So that you can use these
> handles in pf.conf and other commands (rad comes to mind). Not all
> commands work with interface groups. route(8) is such an example but there
> are more commands needing this.
> 
> Btw. a lot of this can be done already now by using '!' in hostname.if
> It wont be pretty but it is also not a common use case.

Maybe you're right, but as theo said doing it via !-commands is just
horrendous. Best I can think of is letting people write their own script
and call it via `!/path/to/script $if`, which is guaranteed going to end
messy.

Here's a completely other idea that might be more generic and hopefully
doesn't cause this same level of confusion. What if instead of adding
a new extension we let people do that themselves and add support for
including other files? Diff below does this and must still be considered
a PoC. And yes I know sed, which isn't allowed. But the command above it
does the same thing, so I'm not going to apologise for it in a PoC.

One downside in the current parse_hn_line format is that spaces other
than a single SP are completely butchered, but I'm not expecting much
problems there.

In addition to $if which is available for '!'-commands I also added
support for $lladdr.

$ cat /etc/hostname.em0 
. /etc/hostname.$lladdr
$ cat /etc/hostname.88\:a4\:c2\:fb\:84\:77 
autoconf
up
$ doas sh ./netstart -n em0
{ ifconfig em0 || ifconfig em0 create; }
ifconfig em0 autoconf
ifconfig em0 up

martijn@

Index: netstart
===
RCS file: /cvs/src/etc/netstart,v
retrieving revision 1.229
diff -u -p -r1.229 netstart
--- netstart5 Nov 2022 12:06:05 -   1.229
+++ netstart23 Nov 2022 07:22:07 -
@@ -35,10 +35,38 @@ stripcom() {
done <$_file
 }
 
+LLGLOB='[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]'
+LLGLOB="$LLGLOB:$LLGLOB:$LLGLOB"
+set -A LLADDR_MAP -- $(
+   ifconfig | while IFS= read -- _line; do
+   if [[ "$_line" = +([[:alpha:]])+([[:digit:]]):* ]]; then
+   _if=${_line%:*}
+   elif [[ -n "$_if"
+&& "$_line" = +([[:space:]])lladdr\ $LLGLOB ]]; then
+   print "$_if,${_line#*lladdr }"
+   _if=
+   fi
+   done
+)
+
+# Find lladdr for if
+# Usage: if2lladdr if1
+if2lladdr() {
+   local _if=$1
+   for m in "${LLADDR_MAP[@]}"; do
+   if [[ "$_if" = "${m%,*}" ]]; then
+   print -- "${m#*,}"
+   break
+   fi
+   done
+   return 0
+}
+
 # Parse and "unpack" a hostname.if(5) line given as positional parameters.
 # Fill the _cmds array with the resulting interface configuration commands.
 parse_hn_line() {
-   local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp 
_i
+   local _af=0 _name=1 _mask=2 _bc=3 _prefix=2 _c _cmd _prev _daddr _dhcp
+   local _i _file _line
set -A _c -- "$@"
set -o noglob
 
@@ -84,6 +112,17 @@ parse_hn_line() {
;;
'!'*)   _cmd=$(print -- "${_c[@]}" | sed 's/\$if/'$_if'/g')
_cmds[${#_cmds[*]}]="${_cmd#!}"
+   ;;
+   '.')unset _c[_af]
+   _file="$(print -- "${_c[@]}" | sed -e 's/\$if/'$_if'/g' \
+   -e 's/\$lladdr/'$(if2lladdr $_if)'/g')"
+   if [[ ! -f $_file ]]; then
+   print -u2 "${0##*/}: $_file: No such file or directory."
+   return
+   fi
+   while IFS= read -- _line; do
+   parse_hn_line $_line
+   done<"$_file"
;;
*)  _cmds[${#_cmds[*]}]="ifconfig $_if ${_c[@]}"
;;



lladdr support for netstart/hostname.if (was: Re: Locking network card configuration)

2022-11-21 Thread Martijn van Duren
On Sun, 2022-11-20 at 19:35 -0700, Theo de Raadt wrote:
> Steve Litt  wrote:
> 
> > Vitaliy Makkoveev said on Mon, 21 Nov 2022 03:48:21 +0300
> > 
> > > > On 20 Nov 2022, at 18:06, Odd Martin Baanrud 
> > > > wrote:
> > > > 
> > > > Hello,
> > > > 
> > > > I have a Raspberry Pi 4 with 2 USB NIC’s attached.
> > > > One via USB3 (ure0), and the other via USB2 (ure1).
> > > > Since they are connected to different USB interfaces, I thaught they
> > > > would get configured the same way on reboot. But that’s not the case.
> > > > They became swapped on reboot.
> > > > Is there a way to “lock” the configuration I want?
> > > > So the USB3 NIC always become ure0, and the USB2 ure1.
> > > > 
> > > > Regards, Martin
> > > >   
> > > 
> > > You could parse ifconfig(8) output to determine which names network
> > > interfaces received. But unfortunately, you can’t rename interfaces.
> > 
> > During your parsing you could assign each one to an environment
> > variable such that, for instance, $lan contains the network card name
> > of the LAN one, and $wan contains the network name of the one going to
> > the Internet. Unfortunately, this would probably mean changing a lot of
> > existing shellscripts, but it's doable.
> 
> But that is not the problem.
> 
> hostname.* installs addresses on an interface, based upon the name of that
> interface.
> 
> So it is too late for what you suggest.
> 
> Unless the suggestion is have each hostname.* do a !command to a script which
> does the assigning.  That is pretty crazy.
> 
> pf.conf is not the problem either, because that can be entirely written using
> egress and groups.
> 
> 
> 
> There is a problem with device attachment -> naming a device at that
> moment -> using that name in netstart.. but I am not sure how we could
> solve this without creating bigger problems for everyone else in the
> other non-hot-plug configurations, which is the majority of users with
> > 1 network device.
> 
> We also hit this problem with disks, and we worked around it with the
> DUID subsystem.
> 
> 
> I suppose there is some argument that we should support hostname.MAC
> files
> 
I don't have a usecase for this myself, but it seemed like a nice
exercise and might get the ball rolling. I also don't have much
experience with our rc/netstart shellcode, so I'm expecting this diff
should be taken as a starting-point iff we want something like this.

I've chosen to error out on missing lladdr, duplicate lladdr and when
there's a hostname.if for both the lladdr and the if variant. This means
that there's smaller chance for order confusion or doubly applied
configs. Downside is that if someone decided to backup their hostname.if
to hostname.lladdr that will break their setup. However, I don't expect
people to backup their config files in this manner, but you never know.

Errors:
On duplicate lladdr (in this case em0 and iwx0 in trunk0):
$ doas sh /usr/src/etc/netstart 88:a4:c2:fb:84:77 
netstart: /etc/hostname.88:a4:c2:fb:84:77: unique if for lladdr not found.

On missing lladdr:
$ doas sh /usr/src/etc/netstart 88:a4:c2:fb:84:76 
netstart: /etc/hostname.88:a4:c2:fb:84:76: unique if for lladdr not found.

And having both hostname.if and hostname.lladdr installed:
$ doas sh ./netstart 00:11:22:33:44:55
netstart: /etc/hostname.00:11:22:33:44:55: duplicate config found in 
/etc/hostname.vio0.
$ doas sh ./netstart vio0 
netstart: /etc/hostname.vio0: duplicate config found in 
/etc/hostname.00:11:22:33:44:55.

Two omissions I considered but didn't implement:
1) I didn't test if the lladdr is owned by one of `ifconfig -C`
   interfaces. Not sure if this is an upside or downside.
2) Allowing /etc/netstart if1 and parsing the hostname.lladdr1 and vice
   versa.

martijn@

Index: etc/netstart
===
RCS file: /cvs/src/etc/netstart,v
retrieving revision 1.219
diff -u -p -r1.219 netstart
--- etc/netstart3 Jul 2022 12:14:36 -   1.219
+++ etc/netstart21 Nov 2022 15:40:23 -
@@ -92,6 +92,40 @@ parse_hn_line() {
set +o noglob
 }
 
+# Find if for lladdr
+# Usage: lladdr2if xx:xx:xx:xx:xx:xx
+#   Duplicate lladdrs result in error.
+LLADDR_GLOB='[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:'
+LLADDR_GLOB=$LLADDR_GLOB'[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:'
+LLADDR_GLOB=$LLADDR_GLOB'[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]'
+lladdr2if() {
+   local _line _if _lladdr="$1"
+
+   _if="$(ifconfig | while IFS= read -- _line; do
+   [[ -n "${_line%%[[:space:]]*}" ]] && _if="${_line%%:*}"
+   if [[ -z "${_line%%[[:space:]]lladdr $LLADDR_GLOB}" && \
+   "${_line##* }" = "$_lladdr" ]]; then
+   print -n -- "$_if "
+   fi
+   done)"
+   [[ -z "$_if" || -n "${_if#* }" ]] && return 1
+   print -- $_if
+}
+
+# Find lladdr for if
+# Usage: if2lladdr if1
+if2lladdr() {
+   local _line _if="$1"
+
+   ifconfig $_if 2>/d

Re: ber.c: Fix some minor issues in ober_read_element()

2022-11-02 Thread Martijn van Duren
On Wed, 2022-11-02 at 18:34 +0100, Claudio Jeker wrote:
> On Wed, Nov 02, 2022 at 05:56:21PM +0100, Martijn van Duren wrote:
> > On Wed, 2022-11-02 at 17:47 +0100, Claudio Jeker wrote:
> > > On Wed, Nov 02, 2022 at 05:25:12PM +0100, Martijn van Duren wrote:
> > > > On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote:
> > > > > On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote:
> > > > > > I found 2 minor issues in the handling of sequences/sets in
> > > > > > ober_read_element():
> > > > > > 1) An empty sequence/set (which is basically always) unconditionally
> > > > > >creates an (uninitialised) sub-element. Add the same length check
> > > > > >used to check the next element
> > > > > > 2) For each sub-element r is only checked for -1, but not if it
> > > > > >overflows the length of the sequence itself. This is not a big 
> > > > > > risk
> > > > > >since each sub-element is length-checked against the buffer
> > > > > >availability and simply returns an ECANCELED, which would be no
> > > > > >worse memory-wise than sending an extremely large packet.
> > > > > > 
> > > > > > While here, only having the NULL of the comparison on the next line
> > > > > > annoyed me.
> > > > > > 
> > > > > > OK?
> > > > > 
> > > > > See below.
> > > > >  
> > > > > > martijn@
> > > > > > 
> > > > > > Index: ber.c
> > > > > > ===
> > > > > > RCS file: /cvs/src/lib/libutil/ber.c,v
> > > > > > retrieving revision 1.23
> > > > > > diff -u -p -r1.23 ber.c
> > > > > > --- ber.c   21 Oct 2021 08:17:33 -  1.23
> > > > > > +++ ber.c   2 Nov 2022 06:28:33 -
> > > > > > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc
> > > > > > break;
> > > > > > case BER_TYPE_SEQUENCE:
> > > > > > case BER_TYPE_SET:
> > > > > > -   if (elm->be_sub == NULL) {
> > > > > > +   if (len > 0 && elm->be_sub == NULL) {
> > > > > > if ((elm->be_sub = ober_get_element(0)) == NULL)
> > > > > > return -1;
> > > > > > }
> > > > > 
> > > > > OK, be_sub == NULL is something checked and better than an empty 
> > > > > object.
> > > > 
> > > > I'm not sure I understand what you mean here.
> > > 
> > > I just wanted to say that elm->be_sub == NULL is a condition that most
> > > other code handles well. While having an empty element in elm->be_sub is
> > > something other code may not handle well.
> > 
> > Ah ok, so confirming that the change is OK. Thanks.
> > > 
> > > > > 
> > > > > > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc
> > > > > > return -1;
> > > > > > }
> > > > > > r = ober_read_element(ber, next);
> > > > > > -   if (r == -1)
> > > > > > +   if (r == -1) {
> > > > > > +   /* sub-element overflows sequence/set */
> > > > > 
> > > > > This comment is not quite right. I think the proper comment here is:
> > > > 
> > > > I'm not sure. Yes the sub-element overflows the buffer, so it is more
> > > > accurate in the literal sense. However, since the sequence/set already
> > > > has its entire length in the buffer it implies that an ECANCELED is an
> > > > overflow of the sub-element of the sequence and therefore makes it
> > > > clearer why the errno is overwritten.
> > > 
> > > Actually why do we need to reset the errno anyway?
> > > Is anything in userland looking at ECANCELED and retries after receiving
> > > more data?
> > 
> > Yes, aldap.c and snmpe.c do this. And even if it didn't, going with
> > EINVAL is more concise than ECANCELED.
> 
> I really dislike such errno magic but fixing that is a much bigger
> project

Re: ber.c: Fix some minor issues in ober_read_element()

2022-11-02 Thread Martijn van Duren
On Wed, 2022-11-02 at 17:47 +0100, Claudio Jeker wrote:
> On Wed, Nov 02, 2022 at 05:25:12PM +0100, Martijn van Duren wrote:
> > On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote:
> > > On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote:
> > > > I found 2 minor issues in the handling of sequences/sets in
> > > > ober_read_element():
> > > > 1) An empty sequence/set (which is basically always) unconditionally
> > > >creates an (uninitialised) sub-element. Add the same length check
> > > >used to check the next element
> > > > 2) For each sub-element r is only checked for -1, but not if it
> > > >overflows the length of the sequence itself. This is not a big risk
> > > >since each sub-element is length-checked against the buffer
> > > >availability and simply returns an ECANCELED, which would be no
> > > >worse memory-wise than sending an extremely large packet.
> > > > 
> > > > While here, only having the NULL of the comparison on the next line
> > > > annoyed me.
> > > > 
> > > > OK?
> > > 
> > > See below.
> > >  
> > > > martijn@
> > > > 
> > > > Index: ber.c
> > > > ===
> > > > RCS file: /cvs/src/lib/libutil/ber.c,v
> > > > retrieving revision 1.23
> > > > diff -u -p -r1.23 ber.c
> > > > --- ber.c   21 Oct 2021 08:17:33 -  1.23
> > > > +++ ber.c   2 Nov 2022 06:28:33 -
> > > > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc
> > > > break;
> > > > case BER_TYPE_SEQUENCE:
> > > > case BER_TYPE_SET:
> > > > -   if (elm->be_sub == NULL) {
> > > > +   if (len > 0 && elm->be_sub == NULL) {
> > > > if ((elm->be_sub = ober_get_element(0)) == NULL)
> > > > return -1;
> > > > }
> > > 
> > > OK, be_sub == NULL is something checked and better than an empty object.
> > 
> > I'm not sure I understand what you mean here.
> 
> I just wanted to say that elm->be_sub == NULL is a condition that most
> other code handles well. While having an empty element in elm->be_sub is
> something other code may not handle well.

Ah ok, so confirming that the change is OK. Thanks.
> 
> > > 
> > > > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc
> > > > return -1;
> > > > }
> > > > r = ober_read_element(ber, next);
> > > > -   if (r == -1)
> > > > +   if (r == -1) {
> > > > +   /* sub-element overflows sequence/set */
> > > 
> > > This comment is not quite right. I think the proper comment here is:
> > 
> > I'm not sure. Yes the sub-element overflows the buffer, so it is more
> > accurate in the literal sense. However, since the sequence/set already
> > has its entire length in the buffer it implies that an ECANCELED is an
> > overflow of the sub-element of the sequence and therefore makes it
> > clearer why the errno is overwritten.
> 
> Actually why do we need to reset the errno anyway?
> Is anything in userland looking at ECANCELED and retries after receiving
> more data?

Yes, aldap.c and snmpe.c do this. And even if it didn't, going with
EINVAL is more concise than ECANCELED.

So is the comment OK as is, do you still want to go with your comment,
or do you want to suggest something else?
> 
> > > 
> > >   /* sub-element overflows buffer */
> > > > +   if (errno == ECANCELED)
> > > > +   errno = EINVAL;
> > > > return -1;
> > > > +   }
> > > > +   if (r > len) {
> > > 
> > > This check here is actually checking that the sub_element does not
> > > overflow the sequence/set. So maybe move the abcve comment down here.
> > 
> > I think it does exactly the same thing, with the only difference that
> > the ECANCELED case didn't reside fully in the buffer. The reason I
> > didn't add the comment here was becaus

Re: ber.c: Fix some minor issues in ober_read_element()

2022-11-02 Thread Martijn van Duren
On Wed, 2022-11-02 at 17:00 +0100, Claudio Jeker wrote:
> On Wed, Nov 02, 2022 at 07:33:14AM +0100, Martijn van Duren wrote:
> > I found 2 minor issues in the handling of sequences/sets in
> > ober_read_element():
> > 1) An empty sequence/set (which is basically always) unconditionally
> >creates an (uninitialised) sub-element. Add the same length check
> >used to check the next element
> > 2) For each sub-element r is only checked for -1, but not if it
> >overflows the length of the sequence itself. This is not a big risk
> >since each sub-element is length-checked against the buffer
> >availability and simply returns an ECANCELED, which would be no
> >worse memory-wise than sending an extremely large packet.
> > 
> > While here, only having the NULL of the comparison on the next line
> > annoyed me.
> > 
> > OK?
> 
> See below.
>  
> > martijn@
> > 
> > Index: ber.c
> > ===
> > RCS file: /cvs/src/lib/libutil/ber.c,v
> > retrieving revision 1.23
> > diff -u -p -r1.23 ber.c
> > --- ber.c   21 Oct 2021 08:17:33 -  1.23
> > +++ ber.c   2 Nov 2022 06:28:33 -
> > @@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc
> > break;
> > case BER_TYPE_SEQUENCE:
> > case BER_TYPE_SET:
> > -   if (elm->be_sub == NULL) {
> > +   if (len > 0 && elm->be_sub == NULL) {
> > if ((elm->be_sub = ober_get_element(0)) == NULL)
> > return -1;
> > }
> 
> OK, be_sub == NULL is something checked and better than an empty object.

I'm not sure I understand what you mean here.
> 
> > @@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc
> > return -1;
> > }
> > r = ober_read_element(ber, next);
> > -   if (r == -1)
> > +   if (r == -1) {
> > +   /* sub-element overflows sequence/set */
> 
> This comment is not quite right. I think the proper comment here is:

I'm not sure. Yes the sub-element overflows the buffer, so it is more
accurate in the literal sense. However, since the sequence/set already
has its entire length in the buffer it implies that an ECANCELED is an
overflow of the sub-element of the sequence and therefore makes it
clearer why the errno is overwritten.
> 
>   /* sub-element overflows buffer */
> > +   if (errno == ECANCELED)
> > +   errno = EINVAL;
> > return -1;
> > +   }
> > +   if (r > len) {
> 
> This check here is actually checking that the sub_element does not
> overflow the sequence/set. So maybe move the abcve comment down here.

I think it does exactly the same thing, with the only difference that
the ECANCELED case didn't reside fully in the buffer. The reason I
didn't add the comment here was because I think it's clear from the
context, where that context might be less obvious at ECANCELED.
> 
> > +   errno = EINVAL;
> > +   return -1;
> > +   }
> > elements++;
> > len -= r;
> > if (len > 0 && next->be_next == NULL) {
> > -   if ((next->be_next = ober_get_element(0)) ==
> > -   NULL)
> > +   next->be_next = ober_get_element(0);
> > +   if (next->be_next == NULL)
> > return -1;
> > }
> > next = next->be_next;
> > 
> 
> Apart from that OK claudio@
> 



ber.c: Fix some minor issues in ober_read_element()

2022-11-01 Thread Martijn van Duren
I found 2 minor issues in the handling of sequences/sets in
ober_read_element():
1) An empty sequence/set (which is basically always) unconditionally
   creates an (uninitialised) sub-element. Add the same length check
   used to check the next element
2) For each sub-element r is only checked for -1, but not if it
   overflows the length of the sequence itself. This is not a big risk
   since each sub-element is length-checked against the buffer
   availability and simply returns an ECANCELED, which would be no
   worse memory-wise than sending an extremely large packet.

While here, only having the NULL of the comparison on the next line
annoyed me.

OK?

martijn@

Index: ber.c
===
RCS file: /cvs/src/lib/libutil/ber.c,v
retrieving revision 1.23
diff -u -p -r1.23 ber.c
--- ber.c   21 Oct 2021 08:17:33 -  1.23
+++ ber.c   2 Nov 2022 06:28:33 -
@@ -1375,7 +1375,7 @@ ober_read_element(struct ber *ber, struc
break;
case BER_TYPE_SEQUENCE:
case BER_TYPE_SET:
-   if (elm->be_sub == NULL) {
+   if (len > 0 && elm->be_sub == NULL) {
if ((elm->be_sub = ober_get_element(0)) == NULL)
return -1;
}
@@ -1390,13 +1390,21 @@ ober_read_element(struct ber *ber, struc
return -1;
}
r = ober_read_element(ber, next);
-   if (r == -1)
+   if (r == -1) {
+   /* sub-element overflows sequence/set */
+   if (errno == ECANCELED)
+   errno = EINVAL;
return -1;
+   }
+   if (r > len) {
+   errno = EINVAL;
+   return -1;
+   }
elements++;
len -= r;
if (len > 0 && next->be_next == NULL) {
-   if ((next->be_next = ober_get_element(0)) ==
-   NULL)
+   next->be_next = ober_get_element(0);
+   if (next->be_next == NULL)
return -1;
}
next = next->be_next;



Re: snmpd_metrics: Don't stop walking on empty table

2022-11-01 Thread Martijn van Duren
ping

On Wed, 2022-10-26 at 10:45 +0200, Martijn van Duren wrote:
> Found by Alec on misc@.
> When there's an empty table pfta_get_nextaddr jumps to fail and
> mib.c returns an agentx_varbind_notfound not libagentx, resulting
> in the upper code assuming that the object has no more entries
> after that. Note that this is not just an issue of the new code,
> but apparently no one ever noticed.
> 
> I see no risk at removing the ba.pfrb_size == 0 check, since
> PFRB_FOREACH calls pfr_buf_next which does that check as well.
> 
> Alec already confirmed that this diff works for him.
> 
> OK?
> 
> martijn@
> 
Index: pf.c
===
RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 pf.c
--- pf.c1 Sep 2022 14:20:33 -   1.1.1.1
+++ pf.c26 Oct 2022 08:44:27 -
@@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras
sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name))
goto fail;
 
-   if (pfta_get(&ba, &filter) || ba.pfrb_size == 0)
+   if (pfta_get(&ba, &filter))
goto fail;
 
PFRB_FOREACH(as, &ba) {



Re: snmpd_metrics: Don't stop walking on empty table

2022-10-26 Thread Martijn van Duren
I should've probably been more clear: This is about OPENBSD-PF-MIB's
pfTblAddrTable.

On Wed, 2022-10-26 at 10:45 +0200, Martijn van Duren wrote:
> Found by Alec on misc@.
> When there's an empty table pfta_get_nextaddr jumps to fail and
> mib.c returns an agentx_varbind_notfound not libagentx, resulting
> in the upper code assuming that the object has no more entries
> after that. Note that this is not just an issue of the new code,
> but apparently no one ever noticed.
> 
> I see no risk at removing the ba.pfrb_size == 0 check, since
> PFRB_FOREACH calls pfr_buf_next which does that check as well.
> 
> Alec already confirmed that this diff works for him.
> 
> OK?
> 
> martijn@
> 
> Index: pf.c
> ===
> RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v
> retrieving revision 1.1.1.1
> diff -u -p -r1.1.1.1 pf.c
> --- pf.c  1 Sep 2022 14:20:33 -   1.1.1.1
> +++ pf.c  26 Oct 2022 08:44:27 -
> @@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras
>   sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name))
>   goto fail;
>  
> - if (pfta_get(&ba, &filter) || ba.pfrb_size == 0)
> + if (pfta_get(&ba, &filter))
>   goto fail;
>  
>   PFRB_FOREACH(as, &ba) {
> 



snmpd_metrics: Don't stop walking on empty table

2022-10-26 Thread Martijn van Duren
Found by Alec on misc@.
When there's an empty table pfta_get_nextaddr jumps to fail and
mib.c returns an agentx_varbind_notfound not libagentx, resulting
in the upper code assuming that the object has no more entries
after that. Note that this is not just an issue of the new code,
but apparently no one ever noticed.

I see no risk at removing the ba.pfrb_size == 0 check, since
PFRB_FOREACH calls pfr_buf_next which does that check as well.

Alec already confirmed that this diff works for him.

OK?

martijn@

Index: pf.c
===
RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/pf.c,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 pf.c
--- pf.c1 Sep 2022 14:20:33 -   1.1.1.1
+++ pf.c26 Oct 2022 08:44:27 -
@@ -418,7 +418,7 @@ pfta_get_nextaddr(struct pfr_astats *ras
sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name))
goto fail;
 
-   if (pfta_get(&ba, &filter) || ba.pfrb_size == 0)
+   if (pfta_get(&ba, &filter))
goto fail;
 
PFRB_FOREACH(as, &ba) {



Fix description in OPENBSD-PF-MIB

2022-10-18 Thread Martijn van Duren
As pointed out by Alec on misc@, there's a discrepancy between the
name and description of several objects inside the pfIfTable.

Looks like a simple copy-paste error.

OK?

martijn@

Index: OPENBSD-PF-MIB.txt
===
RCS file: /cvs/src/share/snmp/OPENBSD-PF-MIB.txt,v
retrieving revision 1.7
diff -u -p -r1.7 OPENBSD-PF-MIB.txt
--- OPENBSD-PF-MIB.txt  23 Mar 2021 19:37:51 -  1.7
+++ OPENBSD-PF-MIB.txt  19 Oct 2022 06:36:22 -
@@ -36,7 +36,7 @@ IMPORTS
FROM SNMPv2-CONF;
 
 pfMIBObjects MODULE-IDENTITY
-LAST-UPDATED "202103231933Z"
+LAST-UPDATED "202210190830Z"
 ORGANIZATION "OpenBSD"
 CONTACT-INFO "
   Author: Joel Knight
@@ -46,6 +46,8 @@ pfMIBObjects MODULE-IDENTITY
 DESCRIPTION "The MIB module for gathering information from
OpenBSD's packet filter.
 "
+REVISION "202210190830Z"
+DESCRIPTION "Fix description of several objects from bytes to the 
obviously intended packets"
 REVISION "202103231933Z"
 DESCRIPTION "Use DisplayString/SnmpAdminString not OCTET STRING where 
appropriate"
 REVISION "201506091728Z"
@@ -794,7 +796,7 @@ pfIfOut4PassPkts OBJECT-TYPE
MAX-ACCESS  read-only
STATUS  current
DESCRIPTION
-   "The number of IPv4 bytes passed out."
+   "The number of IPv4 packets passed out."
::= { pfIfEntry 10 }
 
 pfIfOut4PassBytes OBJECT-TYPE
@@ -810,7 +812,7 @@ pfIfOut4BlockPkts OBJECT-TYPE
MAX-ACCESS  read-only
STATUS  current
DESCRIPTION
-   "The number of outgoing IPv4 bytes blocked."
+   "The number of outgoing IPv4 packets blocked."
::= { pfIfEntry 12 }
 
 pfIfOut4BlockBytes OBJECT-TYPE
@@ -858,7 +860,7 @@ pfIfOut6PassPkts OBJECT-TYPE
MAX-ACCESS  read-only
STATUS  current
DESCRIPTION
-   "The number of IPv6 bytes passed out."
+   "The number of IPv6 packets passed out."
::= { pfIfEntry 18 }
 
 pfIfOut6PassBytes OBJECT-TYPE
@@ -874,7 +876,7 @@ pfIfOut6BlockPkts OBJECT-TYPE
MAX-ACCESS  read-only
STATUS  current
DESCRIPTION
-   "The number of outgoing IPv6 bytes blocked."
+   "The number of outgoing IPv6 packets blocked."
::= { pfIfEntry 20 }
 
 pfIfOut6BlockBytes OBJECT-TYPE



Re: snmpd(8): don't link to libkvm

2022-10-14 Thread Martijn van Duren
On Fri, 2022-10-14 at 09:31 -0600, Theo de Raadt wrote:
> Martijn van Duren  wrote:
> 
> > This one got overlooked when all the metrics moved to snmpd_metrics.
> > 
> > OK?
> > 
> > martijn@
> > 
> > Index: Makefile
> > ===
> > RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
> > retrieving revision 1.21
> > diff -u -p -r1.21 Makefile
> > --- Makefile6 Oct 2022 14:41:08 -   1.21
> > +++ Makefile14 Oct 2022 15:28:48 -
> > @@ -8,7 +8,7 @@ SRCS=   parse.y log.c snmpe.c application
> > mps.c trap.c mib.c smi.c snmpd.c \
> > proc.c usm.c traphandler.c util.c
> >  
> > -LDADD= -levent -lutil -lkvm -lcrypto
> > +LDADD= -levent -lutil -lcrypto
> >  DPADD= ${LIBEVENT} ${LIBUTIL}
> >  CFLAGS+=   -Wall -I${.CURDIR}
> >  CFLAGS+=   -Wstrict-prototypes -Wmissing-prototypes
> > 
> 
> No kidding, but your DPADD dependency is also wrong.

right...

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.21
diff -u -p -r1.21 Makefile
--- Makefile6 Oct 2022 14:41:08 -   1.21
+++ Makefile14 Oct 2022 15:49:22 -
@@ -8,8 +8,8 @@ SRCS=   parse.y log.c snmpe.c application
mps.c trap.c mib.c smi.c snmpd.c \
proc.c usm.c traphandler.c util.c
 
-LDADD= -levent -lutil -lkvm -lcrypto
-DPADD= ${LIBEVENT} ${LIBUTIL}
+LDADD= -levent -lutil -lcrypto
+DPADD= ${LIBEVENT} ${LIBUTIL} ${LIBCRYPTO}
 CFLAGS+=   -Wall -I${.CURDIR}
 CFLAGS+=   -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+=   -Wmissing-declarations



snmpd(8): don't link to libkvm

2022-10-14 Thread Martijn van Duren
This one got overlooked when all the metrics moved to snmpd_metrics.

OK?

martijn@

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.21
diff -u -p -r1.21 Makefile
--- Makefile6 Oct 2022 14:41:08 -   1.21
+++ Makefile14 Oct 2022 15:28:48 -
@@ -8,7 +8,7 @@ SRCS=   parse.y log.c snmpe.c application
mps.c trap.c mib.c smi.c snmpd.c \
proc.c usm.c traphandler.c util.c
 
-LDADD= -levent -lutil -lkvm -lcrypto
+LDADD= -levent -lutil -lcrypto
 DPADD= ${LIBEVENT} ${LIBUTIL}
 CFLAGS+=   -Wall -I${.CURDIR}
 CFLAGS+=   -Wstrict-prototypes -Wmissing-prototypes



vmd: allow agentx to reconnect closed components

2022-10-11 Thread Martijn van Duren
This builds on top of the previous two diffs.

Easiest way to test is to add the following line to vm.conf in
combination with snmpd(8):
agentx context foo

This results in:
[fd:6 sess:4009008336 ctx:foo]: region .1.3.6.1.2.1.236: opening
[fd:6 sess:4009008336 ctx:foo]: region .1.3.6.1.2.1.236: Unsupported context

If you then remove the "context foo" part and run vmctl reload vmd
should properly register:
[fd:6 sess:4009008336 ctx:]: region .1.3.6.1.2.1.236: opening
[fd:6 sess:4009008336 ctx:]: region .1.3.6.1.2.1.236: open

OK?

martijn@

Index: vm_agentx.c
===
RCS file: /cvs/src/usr.sbin/vmd/vm_agentx.c,v
retrieving revision 1.1
diff -u -p -r1.1 vm_agentx.c
--- vm_agentx.c 13 Sep 2022 10:28:19 -  1.1
+++ vm_agentx.c 11 Oct 2022 18:52:45 -
@@ -321,6 +321,7 @@ vm_agentx_configure(struct vmd_agentx *e
changed = 1;
}
 
+   agentx_retry(conn->agentx);
if (!changed)
return;
 



libagentx: Allow not enabled components to retry

2022-10-11 Thread Martijn van Duren
This one is on top of the don't reset errors diff.

This diff includes a minor bump. Don't know if we're still to close
after unlock.

If certain components (session, agentcaps, region, index, object) are
in a closed state while they are expected to be open, there is no way
to retry them without destroying the entire structure and rebuilding
it from scratch.

This diff adds agentx_retry, which walks the tree and retries all in
state AX_CSTATE_CLOSE.

This function could be triggered by the admin, e.g. on a daemon
reload.

OK? If so, now?

martijn@

diff --git a/Symbols.list b/Symbols.list
index 6eda2be..a5c8bf7 100644
--- a/Symbols.list
+++ b/Symbols.list
@@ -4,6 +4,7 @@ agentx_log_info
 agentx_log_debug
 agentx
 agentx_connect
+agentx_retry
 agentx_read
 agentx_write
 agentx_wantwrite
diff --git a/agentx.3 b/agentx.3
index d45a3ae..81322c6 100644
--- a/agentx.3
+++ b/agentx.3
@@ -24,6 +24,7 @@
 .Nm agentx_log_debug ,
 .Nm agentx ,
 .Nm agentx_connect ,
+.Nm agentx_retry ,
 .Nm agentx_read ,
 .Nm agentx_write ,
 .Nm agentx_wantwrite ,
@@ -95,6 +96,8 @@
 .Ft void
 .Fn agentx_connect "struct agentx *sa" "int fd"
 .Ft void
+.Fn agentx_retry "struct agentx *sa"
+.Ft void
 .Fn agentx_read "struct agentx *sa"
 .Ft void
 .Fn agentx_write "struct agentx *sa"
@@ -367,6 +370,12 @@ is ready for a write, the function
 .Fn agentx_write
 should be called.
 .Pp
+If any of the session, agentcaps, region, index, or objects failed to enable
+correctly
+.Pq as can be seen by the admin through the logs
+they can be retried through
+.Fn agentx_retry.
+.Pp
 .Fa sa
 can be freed via
 .Fn agentx_free .
diff --git a/agentx.c b/agentx.c
index 8df1032..5f4f382 100644
--- a/agentx.c
+++ b/agentx.c
@@ -133,6 +133,7 @@ void (*agentx_wantwrite)(struct agentx *, int) =
 agentx_wantwritenow;
 static void agentx_reset(struct agentx *);
 static void agentx_free_finalize(struct agentx *);
+static int agentx_session_retry(struct agentx_session *);
 static int agentx_session_start(struct agentx_session *);
 static int agentx_session_finalize(struct ax_pdu *, void *);
 static int agentx_session_close(struct agentx_session *,
@@ -140,6 +141,7 @@ static int agentx_session_close(struct agentx_session *,
 static int agentx_session_close_finalize(struct ax_pdu *, void *);
 static void agentx_session_free_finalize(struct agentx_session *);
 static void agentx_session_reset(struct agentx_session *);
+static int agentx_context_retry(struct agentx_context *);
 static void agentx_context_start(struct agentx_context *);
 static void agentx_context_free_finalize(struct agentx_context *);
 static void agentx_context_reset(struct agentx_context *);
@@ -149,6 +151,7 @@ static int agentx_agentcaps_close(struct agentx_agentcaps 
*);
 static int agentx_agentcaps_close_finalize(struct ax_pdu *, void *);
 static void agentx_agentcaps_free_finalize(struct agentx_agentcaps *);
 static void agentx_agentcaps_reset(struct agentx_agentcaps *);
+static int agentx_region_retry(struct agentx_region *);
 static int agentx_region_start(struct agentx_region *);
 static int agentx_region_finalize(struct ax_pdu *, void *);
 static int agentx_region_close(struct agentx_region *);
@@ -229,6 +232,25 @@ agentx_connect(struct agentx *ax, int fd)
agentx_finalize(ax, fd);
 }
 
+void
+agentx_retry(struct agentx *ax)
+{
+   struct agentx_session *axs;
+
+   if (ax->ax_fd == -1)
+   return;
+
+   TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) {
+   if (axs->axs_cstate == AX_CSTATE_OPEN) {
+   if (agentx_session_retry(axs) == -1)
+   return;
+   } else if (axs->axs_cstate == AX_CSTATE_CLOSE) {
+   if (agentx_session_start(axs) == -1)
+   return;
+   }
+   }
+}
+
 static void
 agentx_start(struct agentx *ax)
 {
@@ -401,6 +423,26 @@ agentx_session(struct agentx *ax, uint32_t oid[],
return axs;
 }
 
+static int
+agentx_session_retry(struct agentx_session *axs)
+{
+   struct agentx_context *axc;
+
+#ifdef AX_DEBUG
+   if (axs->axs_cstate != AX_CSTATE_OPEN)
+   agentx_log_axs_fatalx(axs, "%s: unexpected retry", __func__);
+#endif
+
+   TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) {
+   if (axc->axc_cstate == AX_CSTATE_OPEN) {
+   if (agentx_context_retry(axc) == -1)
+   return -1;
+   } else if (axc->axc_cstate == AX_CSTATE_CLOSE)
+   agentx_context_start(axc);
+   }
+   return 0;
+}
+
 static int
 agentx_session_start(struct agentx_session *axs)
 {
@@ -628,6 +670,36 @@ agentx_context(struct agentx_session *axs, const char 
*name)
return axc;
 }
 
+static int
+agentx_context_retry(struct agentx_context *axc)
+{
+   struct agentx_agentcaps *axa;
+   struct agentx_region *axr;
+
+#ifdef AX_DEBUG
+   if (axc->axc_cstate != AX_CSTATE_OPEN)
+   a

libagentx: don't reset on server errors

2022-10-11 Thread Martijn van Duren
There's a couple of cases in libagentx where we call agentx_reset on
error cases returned by the server, which in turn results in the
connection being closed and retried.

There's no reason to expect the server to return another response on
the next try. I think it's better to just keep the different structs
in a closed state and just miss that particular part of the
functionality until it's fixed.

Found by explicitly setting vmd's agentx context, which is supported
by net-snmpd, but not by snmpd(8), which just returns an error and
creates an infinite loop.

OK?

martijn

diff --git a/agentx.c b/agentx.c
index 3ee05e6..8df1032 100644
--- a/agentx.c
+++ b/agentx.c
@@ -444,7 +444,7 @@ agentx_session_finalize(struct ax_pdu *pdu, void *cookie)
if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
agentx_log_ax_warnx(ax, "failed to open session: %s",
ax_error2string(pdu->ap_payload.ap_response.ap_error));
-   agentx_reset(ax);
+   axs->axs_cstate = AX_CSTATE_CLOSE;
return -1;
}
 
@@ -1112,8 +1112,6 @@ agentx_region_finalize(struct ax_pdu *pdu, void *cookie)
 {
struct agentx_region *axr = cookie;
struct agentx_context *axc = axr->axr_axc;
-   struct agentx_session *axs = axc->axc_axs;
-   struct agentx *ax = axs->axs_ax;
struct agentx_index *axi;
struct agentx_object *axo;
 
@@ -1140,22 +1138,11 @@ agentx_region_finalize(struct ax_pdu *pdu, void *cookie)
agentx_log_axc_info(axc, "region %s: duplicate, can't "
"reduce priority, ignoring",
ax_oid2string(&(axr->axr_oid)));
-   } else if (pdu->ap_payload.ap_response.ap_error ==
-   AX_PDU_ERROR_REQUESTDENIED) {
+   } else {
axr->axr_cstate = AX_CSTATE_CLOSE;
agentx_log_axc_warnx(axc, "region %s: %s",
 ax_oid2string(&(axr->axr_oid)),
 ax_error2string(pdu->ap_payload.ap_response.ap_error));
-   /*
-* If we can't register a region, related objects are useless.
-* But no need to retry.
-*/
-   return 0;
-   } else {
-   agentx_log_axc_info(axc, "region %s: %s",
-   ax_oid2string(&(axr->axr_oid)),
-   ax_error2string(pdu->ap_payload.ap_response.ap_error));
-   agentx_reset(ax);
return -1;
}
 
@@ -1648,8 +1635,6 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie)
struct agentx_index *axi = cookie;
struct agentx_region *axr = axi->axi_axr;
struct agentx_context *axc = axr->axr_axc;
-   struct agentx_session *axs = axc->axc_axs;
-   struct agentx *ax = axs->axs_ax;
struct ax_pdu_response *resp;
size_t i;
 
@@ -1675,20 +1660,20 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie)
if (resp->ap_nvarbind != 1) {
agentx_log_axc_warnx(axc, "index %s: unexpected number of "
"indices", ax_oid2string(&(axr->axr_oid)));
-   agentx_reset(ax);
+   axi->axi_cstate = AX_CSTATE_CLOSE;
return -1;
}
if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) {
agentx_log_axc_warnx(axc, "index %s: unexpected index type",
ax_oid2string(&(axr->axr_oid)));
-   agentx_reset(ax);
+   axi->axi_cstate = AX_CSTATE_CLOSE;
return -1;
}
if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid),
&(axi->axi_vb.avb_oid)) != 0) {
agentx_log_axc_warnx(axc, "index %s: unexpected oid",
ax_oid2string(&(axr->axr_oid)));
-   agentx_reset(ax);
+   axi->axi_cstate = AX_CSTATE_CLOSE;
return -1;
}
 
@@ -1702,7 +1687,7 @@ agentx_index_finalize(struct ax_pdu *pdu, void *cookie)
resp->ap_varbindlist[0].avb_data.avb_int32) {
agentx_log_axc_warnx(axc, "index %s: unexpected "
"index value", ax_oid2string(&(axr->axr_oid)));
-   agentx_reset(ax);
+   axi->axi_cstate = AX_CSTATE_CLOSE;
return -1;
}
agentx_log_axc_info(axc, "index %s: allocated '%d'",



snmp: Add support for PF_LIMIT_ANCHORS

2022-10-06 Thread Martijn van Duren
Just before lock mbuhl pointed out a new limit placed in pf, not
exported yet over snmp. Here's a diff to add support for
PF_LIMIT_ANCHORS.

the OPENBSD-PF-MIB.txt DESCRIPTION is adapted from pfLimitMaxTables.
The snmp{,d} parts are there just for pretty printing.

OK?

martijn@

Index: share/snmp/OPENBSD-PF-MIB.txt
===
RCS file: /cvs/src/share/snmp/OPENBSD-PF-MIB.txt,v
retrieving revision 1.7
diff -u -p -r1.7 OPENBSD-PF-MIB.txt
--- share/snmp/OPENBSD-PF-MIB.txt   23 Mar 2021 19:37:51 -  1.7
+++ share/snmp/OPENBSD-PF-MIB.txt   6 Oct 2022 16:14:32 -
@@ -493,6 +493,14 @@ pfLimitMaxTableEntries OBJECT-TYPE
tables."
::= { pfLimits 5 }
 
+pfLimitAnchors OBJECT-TYPE
+   SYNTAX  Unsigned32
+   MAX-ACCESS  read-only
+   STATUS  current
+   DESCRIPTION
+   "The maximum number of anchors that can be created as part of the
+   active ruleset."
+   ::= { pfLimits 6 }
 
 -- pfTimeouts
 
Index: usr.bin/snmp/mib.h
===
RCS file: /cvs/src/usr.bin/snmp/mib.h,v
retrieving revision 1.10
diff -u -p -r1.10 mib.h
--- usr.bin/snmp/mib.h  23 Mar 2021 22:05:21 -  1.10
+++ usr.bin/snmp/mib.h  6 Oct 2022 16:14:32 -
@@ -580,6 +580,7 @@
 #define MIB_pfLimitFragments   MIB_pfLimits, 3
 #define MIB_pfLimitMaxTables   MIB_pfLimits, 4
 #define MIB_pfLimitMaxTableEntries MIB_pfLimits, 5
+#define MIB_pfLimitAnchors MIB_pfLimits, 6
 #define MIB_pfTimeouts MIB_pfMIBObjects, 7
 #define MIB_pfTimeoutTcpFirst  MIB_pfTimeouts, 1
 #define MIB_pfTimeoutTcpOpeningMIB_pfTimeouts, 2
@@ -1217,6 +1218,7 @@
{ MIBDECL(pfLimitFragments) },  \
{ MIBDECL(pfLimitMaxTables) },  \
{ MIBDECL(pfLimitMaxTableEntries) },\
+   { MIBDECL(pfLimitAnchors) },\
{ MIBDECL(pfTimeouts) },\
{ MIBDECL(pfTimeoutTcpFirst) }, \
{ MIBDECL(pfTimeoutTcpOpening) },   \
Index: usr.sbin/snmpd/mib.h
===
RCS file: /cvs/src/usr.sbin/snmpd/mib.h,v
retrieving revision 1.41
diff -u -p -r1.41 mib.h
--- usr.sbin/snmpd/mib.h19 Jan 2022 10:26:37 -  1.41
+++ usr.sbin/snmpd/mib.h6 Oct 2022 16:14:32 -
@@ -550,6 +550,7 @@
 #define MIB_pfLimitFragments   MIB_pfLimits, 3
 #define MIB_pfLimitMaxTables   MIB_pfLimits, 4
 #define MIB_pfLimitMaxTableEntries MIB_pfLimits, 5
+#define MIB_pfLimitAnchors MIB_pfLimits, 6
 #define MIB_pfTimeouts MIB_pfMIBObjects, 7
 #define MIB_pfTimeoutTcpFirst  MIB_pfTimeouts, 1
 #define MIB_pfTimeoutTcpOpeningMIB_pfTimeouts, 2
@@ -1126,6 +1127,7 @@
{ MIBDECL(pfLimitFragments) },  \
{ MIBDECL(pfLimitMaxTables) },  \
{ MIBDECL(pfLimitMaxTableEntries) },\
+   { MIBDECL(pfLimitAnchors) },\
{ MIBDECL(pfTimeouts) },\
{ MIBDECL(pfTimeoutTcpFirst) }, \
{ MIBDECL(pfTimeoutTcpOpening) },   \
Index: libexec/snmpd/snmpd_metrics/mib.c
===
RCS file: /cvs/src/libexec/snmpd/snmpd_metrics/mib.c,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 mib.c
--- libexec/snmpd/snmpd_metrics/mib.c   1 Sep 2022 14:20:34 -   1.1.1.1
+++ libexec/snmpd/snmpd_metrics/mib.c   6 Oct 2022 16:14:32 -
@@ -146,6 +146,7 @@ struct agentx_object *pfSrcTrackCount, *
 struct agentx_object *pfSrcTrackRemovals;
 struct agentx_object *pfLimitStates, *pfLimitSourceNodes, *pfLimitFragments;
 struct agentx_object *pfLimitMaxTables, *pfLimitMaxTableEntries;
+struct agentx_object *pfLimitAnchors;
 struct agentx_object *pfTimeoutTcpFirst, *pfTimeoutTcpOpening;
 struct agentx_object *pfTimeoutTcpEstablished, *pfTimeoutTcpClosing;
 struct agentx_object *pfTimeoutTcpFinWait, *pfTimeoutTcpClosed;
@@ -1404,6 +1405,8 @@ mib_pflimits(struct agentx_varbind *vb)
pl.index = PF_LIMIT_TABLES;
else if (obj == pfLimitMaxTableEntries)
pl.index = PF_LIMIT_TABLE_ENTRIES;
+   else if (obj == pfLimitAnchors)
+   pl.index = PF_LIMIT_ANCHORS;
else
fatal("%s: Unexpected object", __func__);
 
@@ -3614,6 +3617,9 @@ main(int argc, char *argv[])
AGENTX_OID(PFLIMITMAXTABLES), NULL, 0, 0, mib_pflimits)) == NULL ||
(pfLimitMaxTableEntries = agentx_object(pfMIBObjects,
AGENTX_OID(PFLIMITMAXTABLEENTRIES), NULL, 0, 0,
+   mib_pflimits)) == NULL ||
+   (pfLimitAnchors = agentx_object(pfMIBObjects,
+   AGENTX_OID(PFLIMITANCHORS), NULL, 0, 0,
mib_pflimits)

Re: Remove some unnecessary setproctitle(3) format strings

2022-09-27 Thread Martijn van Duren
On Tue, 2022-09-27 at 11:23 +0200, Florian Obser wrote:
> On 2022-09-27 09:26 +02, Martijn van Duren  wrote:
> > The caveats section talks about "user-supplied data". These string are
> > constant and don't contain any '%'. Most other daemons in base use the
> > setproctitle("title"); format as well.
> 
> It's not that clear cut, snmpd(8), a daemon you might be familiar with,
> uses
>   setproctitle("%s", p->p_title);
> 
> while p->p_title is not user supplied ;)

True, but proc.c doesn't know how p_title is filled :-)
> 
> The diff is probably not wrong (I only glanced at it), but I don't think
> it solves a problem. It then comes down to style and I prefer things the
> way they are.

I only made the remark in regards to the caveat argument. If it's
personal preference, that's a good enough reason for me to keep it as
is. Especially for daemons that I don't work on. I addressed the
argument, not the conclusion.
> 
> > 
> > On Tue, 2022-09-27 at 08:07 +0100, Stuart Henderson wrote:
> > > These programs seem OK as-is, they are following the advice in
> > > https://man.openbsd.org/setproctitle.3#CAVEATS
> 



Re: Remove some unnecessary setproctitle(3) format strings

2022-09-27 Thread Martijn van Duren
The caveats section talks about "user-supplied data". These string are
constant and don't contain any '%'. Most other daemons in base use the
setproctitle("title"); format as well.

On Tue, 2022-09-27 at 08:07 +0100, Stuart Henderson wrote:
> These programs seem OK as-is, they are following the advice in
> https://man.openbsd.org/setproctitle.3#CAVEATS
> 
> On 2022/09/26 18:06, Josiah Frentsos wrote:
> > Index: sbin/dhcpleased/engine.c
> > ===
> > RCS file: /cvs/src/sbin/dhcpleased/engine.c,v
> > retrieving revision 1.38
> > diff -u -p -r1.38 engine.c
> > --- sbin/dhcpleased/engine.c5 May 2022 14:44:59 -   1.38
> > +++ sbin/dhcpleased/engine.c26 Sep 2022 21:43:28 -
> > @@ -197,7 +197,7 @@ engine(int debug, int verbose)
> > if (unveil(NULL, NULL) == -1)
> > fatal("unveil");
> >  
> > -   setproctitle("%s", "engine");
> > +   setproctitle("engine");
> > log_procinit("engine");
> >  
> > if (setgroups(1, &pw->pw_gid) ||
> > Index: sbin/dhcpleased/frontend.c
> > ===
> > RCS file: /cvs/src/sbin/dhcpleased/frontend.c,v
> > retrieving revision 1.30
> > diff -u -p -r1.30 frontend.c
> > --- sbin/dhcpleased/frontend.c  14 Jul 2022 15:23:09 -  1.30
> > +++ sbin/dhcpleased/frontend.c  26 Sep 2022 21:43:29 -
> > @@ -151,7 +151,7 @@ frontend(int debug, int verbose)
> > if (unveil(NULL, NULL) == -1)
> > fatal("unveil");
> >  
> > -   setproctitle("%s", "frontend");
> > +   setproctitle("frontend");
> > log_procinit("frontend");
> >  
> > if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
> > Index: sbin/slaacd/engine.c
> > ===
> > RCS file: /cvs/src/sbin/slaacd/engine.c,v
> > retrieving revision 1.84
> > diff -u -p -r1.84 engine.c
> > --- sbin/slaacd/engine.c26 Aug 2022 00:02:08 -  1.84
> > +++ sbin/slaacd/engine.c26 Sep 2022 21:43:29 -
> > @@ -372,7 +372,7 @@ engine(int debug, int verbose)
> > if (unveil(NULL, NULL) == -1)
> > fatal("unveil");
> >  
> > -   setproctitle("%s", "engine");
> > +   setproctitle("engine");
> > log_procinit("engine");
> >  
> > if (setgroups(1, &pw->pw_gid) ||
> > Index: sbin/slaacd/frontend.c
> > ===
> > RCS file: /cvs/src/sbin/slaacd/frontend.c,v
> > retrieving revision 1.64
> > diff -u -p -r1.64 frontend.c
> > --- sbin/slaacd/frontend.c  12 Jul 2022 16:54:59 -  1.64
> > +++ sbin/slaacd/frontend.c  26 Sep 2022 21:43:29 -
> > @@ -153,7 +153,7 @@ frontend(int debug, int verbose)
> > if (unveil(NULL, NULL) == -1)
> > fatal("unveil");
> >  
> > -   setproctitle("%s", "frontend");
> > +   setproctitle("frontend");
> > log_procinit("frontend");
> >  
> > if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
> > Index: sbin/unwind/frontend.c
> > ===
> > RCS file: /cvs/src/sbin/unwind/frontend.c,v
> > retrieving revision 1.73
> > diff -u -p -r1.73 frontend.c
> > --- sbin/unwind/frontend.c  13 Mar 2022 15:14:01 -  1.73
> > +++ sbin/unwind/frontend.c  26 Sep 2022 21:43:30 -
> > @@ -207,7 +207,7 @@ frontend(int debug, int verbose)
> > if (chdir("/") == -1)
> > fatal("chdir(\"/\")");
> >  
> > -   setproctitle("%s", "frontend");
> > +   setproctitle("frontend");
> > log_procinit("frontend");
> >  
> > if (setgroups(1, &pw->pw_gid) ||
> > Index: sbin/unwind/resolver.c
> > ===
> > RCS file: /cvs/src/sbin/unwind/resolver.c,v
> > retrieving revision 1.155
> > diff -u -p -r1.155 resolver.c
> > --- sbin/unwind/resolver.c  12 Mar 2022 14:35:29 -  1.155
> > +++ sbin/unwind/resolver.c  26 Sep 2022 21:43:30 -
> > @@ -368,7 +368,7 @@ resolver(int debug, int verbose)
> > if ((pw = getpwnam(UNWIND_USER)) == NULL)
> > fatal("getpwnam");
> >  
> > -   setproctitle("%s", "resolver");
> > +   setproctitle("resolver");
> > log_procinit("resolver");
> >  
> > if (setgroups(1, &pw->pw_gid) ||
> > Index: usr.bin/ssh/sshd.c
> > ===
> > RCS file: /cvs/src/usr.bin/ssh/sshd.c,v
> > retrieving revision 1.591
> > diff -u -p -r1.591 sshd.c
> > --- usr.bin/ssh/sshd.c  17 Sep 2022 10:34:29 -  1.591
> > +++ usr.bin/ssh/sshd.c  26 Sep 2022 21:43:34 -
> > @@ -492,7 +492,7 @@ privsep_preauth(struct ssh *ssh)
> > set_log_handler(mm_log_handler, pmonitor);
> >  
> > privsep_preauth_child();
> > -   setproctitle("%s", "[net]");
> > +   setproctitle("[net]");
> > if (box != NULL)
> > ssh_sandbox_child(box);
> >  
> > @@ -1627,7 +

snmpd: allow NULL in appl_varbind_valid

2022-09-13 Thread Martijn van Duren
varbind was designed to allow both a ber NULL and a NULL pointer for
value. The ber NULL case is there for when it was received via a PDU.
The NULL pointer case can happen if application.c runs into a timeout
or when a backend runs into problems.

The NULL pointer case however was overlooked in appl_varbind_valid and
results in an "missing value" error, (needlessly) terminating the
connection to the backend.

Found the hard way by Mischa Peters while stress testing agentx support
for vmd.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.15
diff -u -p -r1.15 application.c
--- application.c   31 Aug 2022 09:19:22 -  1.15
+++ application.c   13 Sep 2022 09:59:19 -
@@ -1170,8 +1170,11 @@ appl_varbind_valid(struct appl_varbind *
int eomv = 0;
 
if (varbind->av_value == NULL) {
-   *errstr = "missing value";
-   return 0;
+   if (!null) {
+   *errstr = "missing value";
+   return 0;
+   }
+   return 1;
}
if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) {
switch (varbind->av_value->be_type) {



libagentx: NULL deref on varbinds after connection reset

2022-09-13 Thread Martijn van Duren
When a connection is reset while we still have an outstanding request,
the connection from the request to the rest of the structure is removed,
so we don't send any old data over the new connection.

However, the current code dereferences axc at a couple of places before
we check it for NULL.

Found the hard way by Mischa Peters while stress testing agentx support
for vmd.

OK?

martijn@

Index: agentx.c
===
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.16
diff -u -p -r1.16 agentx.c
--- agentx.c29 Aug 2022 12:17:24 -  1.16
+++ agentx.c13 Sep 2022 09:51:02 -
@@ -2575,8 +2575,8 @@ static void
 agentx_get_finalize(struct agentx_get *axg)
 {
struct agentx_context *axc = axg->axg_axc;
-   struct agentx_session *axs = axc->axc_axs;
-   struct agentx *ax = axs->axs_ax;
+   struct agentx_session *axs;
+   struct agentx *ax;
size_t i, j, nvarbind = 0;
uint16_t error = 0, index = 0;
struct ax_varbind *vbl;
@@ -2591,11 +2591,14 @@ agentx_get_finalize(struct agentx_get *a
}
}
 
-   if (axg->axg_axc == NULL) {
+   if (axc == NULL) {
agentx_get_free(axg);
return;
}
 
+   axs = axc->axc_axs;
+   ax = axs->axs_ax;
+
if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) {
agentx_log_axg_warn(axg, "Couldn't parse request");
agentx_get_free(axg);
@@ -2655,12 +2658,14 @@ agentx_get_free(struct agentx_get *axg)
 {
struct agentx_varbind *axv;
struct agentx_object *axo;
-   struct agentx *ax = axg->axg_axc->axc_axs->axs_ax;
+   struct agentx *ax;
struct agentx_varbind_index *index;
size_t i, j;
 
-   if (axg->axg_axc != NULL)
+   if (axg->axg_axc != NULL) {
+   ax = axg->axg_axc->axc_axs->axs_ax;
TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs);
+   }
 
for (i = 0; i < axg->axg_nvarbind; i++) {
axv = &(axg->axg_varbind[i]);
@@ -2701,6 +2706,11 @@ agentx_varbind_start(struct agentx_varbi
agentx_log_axg_fatalx(axv->axv_axg,
"%s: axv_initialized not set", __func__);
 #endif
+
+   if (axc == NULL) {
+   agentx_varbind_error_type(axv, AX_PDU_ERROR_PROCESSINGERROR, 1);
+   return;
+   }
 
bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid),
sizeof(axo_search.axo_oid));



vmd: Add support for agentx

2022-09-03 Thread Martijn van Duren
{ "agentx", AGENTX },
{ "allow",  ALLOW },
{ "boot",   BOOT },
{ "cdrom",  CDROM },
+   { "context",CONTEXT},
{ "delay",  DELAY },
{ "device", DEVICE },
{ "disable",DISABLE },
@@ -793,6 +838,7 @@ lookup(char *s)
{ "net",NET },
{ "owner",  OWNER },
{ "parallel",   PARALLEL },
+   { "path",   PATH },
{ "prefix", PREFIX },
{ "rdomain",RDOMAIN },
{ "size",   SIZE },
@@ -1148,6 +1194,10 @@ parse_config(const char *filename)
 
/* Set the default switch type */
(void)strlcpy(vsw_type, VMD_SWITCH_TYPE, sizeof(vsw_type));
+
+   env->vmd_cfg.cfg_agentx.ax_enabled = 0;
+   env->vmd_cfg.cfg_agentx.ax_context[0] = '\0';
+   env->vmd_cfg.cfg_agentx.ax_path[0] = '\0';
 
yyparse();
errors = file->errors;
Index: proc.h
===
RCS file: /cvs/src/usr.sbin/vmd/proc.h,v
retrieving revision 1.20
diff -u -p -r1.20 proc.h
--- proc.h  16 Jun 2021 16:55:02 -  1.20
+++ proc.h  3 Sep 2022 11:06:31 -
@@ -79,6 +79,7 @@ TAILQ_HEAD(ctl_connlist, ctl_conn);
 enum privsep_procid {
PROC_PARENT = 0,
PROC_CONTROL,
+   PROC_AGENTX,
PROC_VMM,
PROC_PRIV,
PROC_MAX,
Index: vm.conf.5
===
RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v
retrieving revision 1.58
diff -u -p -r1.58 vm.conf.5
--- vm.conf.5   4 Aug 2022 11:50:51 -   1.58
+++ vm.conf.5   3 Sep 2022 11:06:31 -
@@ -91,6 +91,19 @@ vm "vm1.example.com" {
 .Sh GLOBAL CONFIGURATION
 The following setting can be configured globally:
 .Bl -tag -width Ds
+.It Ic agentx Oo Ic context Ar context Oc Oo Ic path Ar path Oc
+Export vm metrics via an AgentX compatible
+.Pq snmp
+daemon by connecting to
+.Ar path .
+Metrics can be found under the vmMIB subtree
+.Pq mib-2.236 .
+If
+.Ar path
+is omitted it will default to
+.Pa /var/agentx/master .
+.Ar Context
+is the SNMPv3 context and can usually be omitted.
 .It Ic local prefix Ar address Ns Li / Ns Ar prefix
 Set the network prefix that is used to allocate subnets for
 local interfaces, see
Index: vm_agentx.c
===
RCS file: vm_agentx.c
diff -N vm_agentx.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ vm_agentx.c 3 Sep 2022 11:06:31 -
@@ -0,0 +1,537 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2022 Martijn van Duren 
+ *
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "proc.h"
+#include "vmd.h"
+
+struct conn {
+   struct event ev;
+   struct agentx *agentx;
+};
+
+void vm_agentx_run(struct privsep *, struct privsep_proc *, void *);
+int vm_agentx_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+void vm_agentx_configure(struct vmd_agentx *);
+static void vm_agentx_nofd(struct agentx *, void *, int);
+static void vm_agentx_tryconnect(int, short, void *);
+static void vm_agentx_read(int, short, void *);
+static void vm_agentx_flush_pending(void);
+static int vm_agentx_sortvir(const void *, const void *);
+static int vm_agentx_adminstate(int);
+static int vm_agentx_operstate(int);
+static void vm_agentx_vmHvSoftware(struct agentx_varbind *);
+static void vm_agentx_vmHvVersion(struct agentx_varbind *);
+static void vm_agentx_vmHvObjectID(struct agentx_varbind *);
+static void vm_agentx_vminfo(struct agentx_varbind *);
+
+static struct agentx_index *vmIndex;
+static struct agentx_object *vmNumber, *vmName, *vmUUID, *vmOSType;
+static struct agentx_object *vmAdminState, *vmOperState, *vmAutoStart;
+static str

snmpd(8): Fix searchrange end length in agentx

2022-08-30 Thread Martijn van Duren
I think this one speaks for itself.

OK?

martijn@

? obj
Index: application_agentx.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v
retrieving revision 1.2
diff -u -p -r1.2 application_agentx.c
--- application_agentx.c29 Aug 2022 18:10:48 -  1.2
+++ application_agentx.c30 Aug 2022 14:49:52 -
@@ -660,7 +660,7 @@ appl_agentx_getnext(struct appl_backend 
srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
srl[i].asr_stop.aoi_include = 0;
srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
-   for (j = 0; j < vb->av_oid.bo_n; j++)
+   for (j = 0; j < vb->av_oid_end.bo_n; j++)
srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
}
if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {



snmpd(8): Allow registering above subtree allocated region

2022-08-30 Thread Martijn van Duren
So right now we disallow allocation of a region if a sub-region has the
subtree flag set. This logic is inverted compared to it's intention,
which is that if you allocate a region with subtree it should check that
the entire region below it is available (application.c:242 should say
"subtree" instead of "region->subtree").

However, since the subtree flag is intended for backends that are
normally only initialized on startup and we now have blocklist, chances
for overlap increase and could possible disable bigger regions than
intended.

I think it's safe to remove this entire overlap check because of the
following reasoning:
- First the blocklist is loaded. This should prevent allocating
  anything underneath it and expects things to be loaded on top of
  it
- legacy (internal): This could have regions allocated over it and
  should have blocklist regions underneath it
- agentx (as native backend): Same as legacy
- agentx (dynamic applications): Only get their chance when
  everything already has been initialised and thus no risk of being
  inserted below a "subtree" region, because that's being captured
  by the first overlap check.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.12
diff -u -p -r1.12 application.c
--- application.c   29 Aug 2022 18:05:08 -  1.12
+++ application.c   30 Aug 2022 14:47:23 -
@@ -237,14 +237,6 @@ appl_region(struct appl_context *ctx, ui
region->ar_backend != backend)
goto overlap;
 
-   search.ar_oid = *oid;
-   region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
-   if (region != NULL && region->ar_subtree && 
-   region->ar_backend != backend && (
-   appl_region_cmp(&search, region) == 0 ||
-   appl_region_cmp(&search, region) == -2))
-   goto overlap;
-
if ((nregion = malloc(sizeof(*nregion))) == NULL) {
log_warn("%s: Can't register %s: Processing error",
backend->ab_name, oidbuf);



snmpd(8): Better determining searchrange end

2022-08-30 Thread Martijn van Duren
Doing overlapping regions is hard...

At application.c:1341 we currently assign region->ar_oid to oid.
However, with overlapping regions this can cause a recursion, because
region might be the parent of the previous region that would cause the
oid to traverse back and cause a loop.

This can easily be fixed by changing this line to vb->av_oid, but I also
found that the code doesn't produce the cleanest searchrange end,
adjacent regions might not be taken into account on a single run because
of the else statement on line 1363.

With the following registration it will result in:
blocklist: Registering 1.3.6.1.4.1.30155.1.9.129 context() priority(1) 
timeout(1.50s)
AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.1 context() 
priority(1) timeout(5.00s)
AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.2 context() 
priority(1) timeout(5.00s)
AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.5 context() 
priority(1) timeout(5.00s)
AgentX(556403008/718113859): Registering 1.3.6.1.4.1.30155.6 context() 
priority(1) timeout(5.00s)
...
GetNextRequest{333882979, 0, 0, {{1.3.6.1.4.1.30155.1.9.128.1.24.3:null}}}
AgentX(556403008/718113859): GetNextRequest{1517163039, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.128.1.24.3-1.3.6.1.4.1.30155.1.9.129:null}}}
AgentX(556403008/718113859): Response{1517163039, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.128.1.24.3:endOfMibView}}}
blocklist: GetNextRequest{1339241491, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.129(incl)-1.3.6.1.4.1.30155.1.9.130:null}}}
blocklist: Response{1339241491, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.129:endOfMibView}}}
AgentX(556403008/718113859): GetNextRequest{1545296030, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.130(incl)-1.3.6.1.4.1.30155.2:null}}}
AgentX(556403008/718113859): Response{1545296030, 0, 0, 
{{1.3.6.1.4.1.30155.1.10.1.0:2}}}

to
...
GetNextRequest{2078129483, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24:null}}}
blocklist: GetNextRequest{-1870096078, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24-1.3.6.1.4.1.30155.1.9.130:null}}}
blocklist: Response{-1870096078, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.129.1.1.1.192.168.153.0.24:endOfMibView}}}
AgentX(3175611302/2074212055): GetNextRequest{-867062125, 0, 0, 
{{1.3.6.1.4.1.30155.1.9.130(incl)-1.3.6.1.4.1.30155.3:null}}}
AgentX(3175611302/2074212055): Response{-867062125, 0, 0, 
{{1.3.6.1.4.1.30155.1.10.1.0:2}}}

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.12
diff -u -p -r1.12 application.c
--- application.c   29 Aug 2022 18:05:08 -  1.12
+++ application.c   30 Aug 2022 14:29:50 -
@@ -1287,7 +1287,7 @@ appl_varbind_backend(struct appl_varbind
struct appl_request_upstream *ureq = ivb->avi_request_upstream;
struct appl_region search, *region, *pregion;
struct appl_varbind *vb = &(ivb->avi_varbind);
-   struct ber_oid oid;
+   struct ber_oid oid, nextsibling;
int next, cmp;
 
next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ ||
@@ -1338,33 +1338,41 @@ appl_varbind_backend(struct appl_varbind
}
ivb->avi_region = region;
if (next) {
-   oid = region->ar_oid;
+   oid = vb->av_oid;
+   /*
+* For the searchrange end we only want contiguous regions.
+* This means directly connecting, or overlapping with the same
+* backend.
+*/
do {
pregion = region;
region = appl_region_next(ureq->aru_ctx, &oid, pregion);
-   if (region != NULL &&
-   appl_region_cmp(region, pregion) > 0)
-   oid = region->ar_oid;
-   else
+   if (region == NULL) {
+   oid = pregion->ar_oid;
ober_oid_nextsibling(&oid);
-   } while (region != NULL &&
-   region->ar_backend == pregion->ar_backend);
-   if (region == NULL) {
-   vb->av_oid_end = pregion->ar_oid;
-   if (pregion->ar_instance &&
-   vb->av_oid_end.bo_n < BER_MAX_OID_LEN)
-   vb->av_oid_end.bo_id[vb->av_oid_end.bo_n++] = 0;
-   else
-   ober_oid_nextsibling(&(vb->av_oid_end));
-   } else {
-   if (ober_oid_cmp(&(region->ar_oid),
-   &(ivb->avi_region->ar_oid)) == 2)
-   vb->av_oid_end = region->ar_oid;
-   else {
-   vb->av_oid_end = ivb->avi_region->ar_oid;
-   ober_oid_nextsibling(&(vb->av_oid_end));
+   break;
}
-   }
+   

snmpd(8): Minor logging cleanup

2022-08-29 Thread Martijn van Duren
Apparently I mistyped one AgentX as Agentx, and when moving sess_id to
uint32_t (in an early draft) I forgot to adjust the %d in two places.

OK?

martijn@

Index: application_agentx.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application_agentx.c,v
retrieving revision 1.1
diff -u -p -r1.1 application_agentx.c
--- application_agentx.c23 Aug 2022 08:56:20 -  1.1
+++ application_agentx.c29 Aug 2022 17:50:50 -
@@ -145,7 +145,7 @@ appl_agentx_listen(struct agentx_master 
bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
sizeof(master->axm_sun)) == -1 ||
listen(master->axm_fd, 5)) {
-   log_warn("Agentx: listen %s", master->axm_sun.sun_path);
+   log_warn("AgentX: listen %s", master->axm_sun.sun_path);
umask(mask);
return;
}
@@ -208,7 +208,7 @@ appl_agentx_accept(int masterfd, short e
appl_agentx_recv, conn);
event_add(&(conn->conn_rev), NULL);
event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
-   log_info("AgentX(%d): new connection", conn->conn_id);
+   log_info("AgentX(%"PRIu32"): new connection", conn->conn_id);
 
return;
  fail:
@@ -442,7 +442,7 @@ appl_agentx_open(struct appl_agentx_conn
if (asprintf(&(session->sess_backend.ab_name),
"AgentX(%"PRIu32"/%"PRIu32")",
conn->conn_id, session->sess_id) == -1) {
-   log_warn("AgentX(%d): asprintf: Open Failed",
+   log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
conn->conn_id);
goto fail;
}



snmpd(8): Allow overlapping region from same backend

2022-08-29 Thread Martijn van Duren
Right now we don't allow overlapping regions when the subtree flag is
set, . However I don't see a reason why a single backend can't
make an overlapping region with itself.

I would also like to use this feature when moving mib.c code into an
libagentx based backend.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.10
diff -u -p -r1.10 application.c
--- application.c   29 Aug 2022 13:25:18 -  1.10
+++ application.c   29 Aug 2022 17:39:15 -
@@ -223,12 +223,14 @@ appl_region(struct appl_context *ctx, ui
 * This allows us to keep control of certain regions like system.
 */
region = appl_region_find(ctx, oid);
-   if (region != NULL && region->ar_subtree)
+   if (region != NULL && region->ar_subtree &&
+   region->ar_backend != backend)
goto overlap;
 
search.ar_oid = *oid;
region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
-   if (region != NULL && region->ar_subtree && (
+   if (region != NULL && region->ar_subtree && 
+   region->ar_backend != backend && (
appl_region_cmp(&search, region) == 0 ||
appl_region_cmp(&search, region) == -2))
goto overlap;



snmpd(8): make sure oidbuf is properly initialized on overlapping regions

2022-08-29 Thread Martijn van Duren
I think the subject speaks for itself.

Not a really big problem, since non of the available software that we
currently have in base/ports have overlapping regions, but definitely
worth fixing.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.10
diff -u -p -r1.10 application.c
--- application.c   29 Aug 2022 13:25:18 -  1.10
+++ application.c   29 Aug 2022 17:30:44 -
@@ -218,6 +218,16 @@ appl_region(struct appl_context *ctx, ui
char oidbuf[1024], regionbuf[1024], subidbuf[11];
size_t i;
 
+   /* Don't use smi_oid2string, because appl_register can't use it */
+   oidbuf[0] = '\0';
+   for (i = 0; i < oid->bo_n; i++) {
+   if (i != 0)
+   strlcat(oidbuf, ".", sizeof(oidbuf));
+   snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
+   oid->bo_id[i]);
+   strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+   }
+
/*
 * Don't allow overlap when subtree flag is set.
 * This allows us to keep control of certain regions like system.
@@ -233,15 +243,6 @@ appl_region(struct appl_context *ctx, ui
appl_region_cmp(&search, region) == -2))
goto overlap;
 
-   /* Don't use smi_oid2string, because appl_register can't use it */
-   oidbuf[0] = '\0';
-   for (i = 0; i < oid->bo_n; i++) {
-   if (i != 0)
-   strlcat(oidbuf, ".", sizeof(oidbuf));
-   snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
-   oid->bo_id[i]);
-   strlcat(oidbuf, subidbuf, sizeof(oidbuf));
-   }
if ((nregion = malloc(sizeof(*nregion))) == NULL) {
log_warn("%s: Can't register %s: Processing error",
backend->ab_name, oidbuf);



snmpd(8): don't traverse back in tree

2022-07-22 Thread Martijn van Duren
When we have 2 overlapping regions within the same backend the current
code takes the OID of the parent region after the child region returned
an EOMV. This is of course wrong and creates an infinite loop.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.6
diff -u -p -r1.6 application.c
--- application.c   30 Jun 2022 11:28:36 -  1.6
+++ application.c   22 Jul 2022 15:07:53 -
@@ -1260,6 +1260,7 @@ appl_varbind_backend(struct appl_varbind
struct appl_request_upstream *ureq = ivb->avi_request_upstream;
struct appl_region search, *region, *pregion;
struct appl_varbind *vb = &(ivb->avi_varbind);
+   struct ber_oid oid;
int next, cmp;
 
next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ ||
@@ -1310,10 +1311,15 @@ appl_varbind_backend(struct appl_varbind
}
ivb->avi_region = region;
if (next) {
+   oid = region->ar_oid;
do {
pregion = region;
-   region = appl_region_next(ureq->aru_ctx,
-   &(region->ar_oid), pregion);
+   region = appl_region_next(ureq->aru_ctx, &oid, pregion);
+   if (region != NULL &&
+   appl_region_cmp(region, pregion) > 0)
+   oid = region->ar_oid;
+   else
+   ober_oid_nextsibling(&oid);
} while (region != NULL &&
region->ar_backend == pregion->ar_backend);
if (region == NULL) {



snmpd(8): restart requests where backend disappeared.

2022-07-22 Thread Martijn van Duren
appl_request_downstream_free gets called from 3 locations:
- appl_request_upstream_free: called from appl_request_upstream_reply
  and cleans up after a request has been answered, whether all the
  downstream requests have been completed or not (when timed out)
- appl_response: When a downstream reqeuest has been completed
- appl_close: When a downstream backend has been closed.

appl_request_upstream_free is already done with the upstream request
and appl_response already automatically starts the next iteration of
missing varbinds.

The problem arrises when appl_close is called (e.g. a backend
disappears unexpected) while there are still pending downstream
requests. In that case all references to the upstream request are
lost and we have a memory leak.

Diff below resets the pending varbinds to new and restarts the
parsing procedure.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.6
diff -u -p -r1.6 application.c
--- application.c   30 Jun 2022 11:28:36 -  1.6
+++ application.c   22 Jul 2022 14:59:00 -
@@ -716,6 +716,7 @@ void
 appl_request_downstream_free(struct appl_request_downstream *dreq)
 {
struct appl_varbind_internal *vb;
+   int retry = 0;
 
if (dreq == NULL)
return;
@@ -723,9 +724,16 @@ appl_request_downstream_free(struct appl
RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq);
evtimer_del(&(dreq->ard_timer));
 
-   for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next)
+   for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { 
vb->avi_request_downstream = NULL;
+   if (vb->avi_state == APPL_VBSTATE_PENDING) {
+   vb->avi_state = APPL_VBSTATE_NEW;
+   retry = 1;
+   }
+   }
 
+   if (retry)
+   appl_request_upstream_resolve(dreq->ard_request);
free(dreq);
 }
 



snmpd(8): honour searchrange end

2022-07-22 Thread Martijn van Duren
This is the snmpd(8) part of searchrange end issue mentioned in my
libagentx diff from yesterday.[0]

Since searchranges are an agentx specific thing I implemented two
cases:
1) Backends that support searchranges set ab_range to 1
   (application_agentx.c) and check the av_oid_end in
   appl_varbind_valid. If it fails here we generate a GENERR
   and disconnect the backend.
2) Backends that don't support searchranges set ab_range to 0 and
   are not checked for overflow in appl_varbind_valid, but instead
   treat the response as ENDOFMIBVIEW and continue searching at
   av_oid_end.

Since this is already the 3rd EOMV case added I decided to move to
a simple variable and do the reset-logic in a single place.

While here, also fix an indent in appl_close.

OK?

martijn@

[0] https://marc.info/?l=openbsd-tech&m=165843466721371&w=2

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.6
diff -u -p -r1.6 application.c
--- application.c   30 Jun 2022 11:28:36 -  1.6
+++ application.c   22 Jul 2022 13:13:43 -
@@ -126,7 +126,7 @@ void appl_request_downstream_send(struct
 void appl_request_downstream_timeout(int, short, void *);
 void appl_request_upstream_reply(struct appl_request_upstream *);
 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int,
-const char **);
+int, const char **);
 int appl_varbind_backend(struct appl_varbind_internal *);
 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
 void appl_report(struct snmp_message *, int32_t, struct ber_oid *,
@@ -541,7 +541,7 @@ appl_close(struct appl_backend *backend)
 
RB_FOREACH_SAFE(request, appl_requests,
&(backend->ab_requests), trequest)
-   appl_request_downstream_free(request);
+   appl_request_downstream_free(request);
 }
 
 struct appl_region *
@@ -1025,14 +1025,13 @@ appl_response(struct appl_backend *backe
 {
struct appl_request_downstream *dreq, search;
struct appl_request_upstream *ureq = NULL;
-   struct appl_region *nregion;
const char *errstr;
char oidbuf[1024];
enum snmp_pdutype pdutype;
struct appl_varbind *vb;
struct appl_varbind_internal *origvb = NULL;
int invalid = 0;
-   int next = 0;
+   int next = 0, eomv;
int32_t i;
 
appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist);
@@ -1055,7 +1054,7 @@ appl_response(struct appl_backend *backe
for (i = 1; vb != NULL; vb = vb->av_next, i++) {
 if (!appl_varbind_valid(vb, origvb == NULL ?
NULL : &(origvb->avi_varbind), next,
-error != APPL_ERROR_NOERROR, &errstr)) {
+error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) {
smi_oid2string(&(vb->av_oid), oidbuf,
sizeof(oidbuf), 0);
log_warnx("%s: %"PRIu32" %s: %s",
@@ -1068,35 +1067,36 @@ appl_response(struct appl_backend *backe
appl_varbind_error(origvb, error);
origvb->avi_state = APPL_VBSTATE_DONE;
origvb->avi_varbind.av_oid = vb->av_oid;
-   if (vb->av_value != NULL &&
+
+   eomv = vb->av_value != NULL &&
vb->av_value->be_class == BER_CLASS_CONTEXT &&
-   vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) {
-   nregion = appl_region_next(ureq->aru_ctx,
-   &(vb->av_oid), origvb->avi_region);
-   if (nregion != NULL) {
-   ober_free_elements(vb->av_value);
-   origvb->avi_varbind.av_oid =
-   nregion->ar_oid;
-   origvb->avi_varbind.av_include = 1;
-   vb->av_value = NULL;
-   origvb->avi_state = APPL_VBSTATE_NEW;
-   }
-   }
+   vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW;
+   /*
+* Treat results past av_oid_end for backends that
+* don't support searchranges as EOMV
+*/
+   eomv |= !backend->ab_range && next &&
+   ober_oid_cmp(&(vb->av_oid),
+   &(origvb->avi_varbind.av_oid_end)) > 0;
/* RFC 3584 section 4.2.2.1 */
if (ureq->aru_pduversion == SNMP_V1 &&
vb->av_value != NULL &&
vb->av_value->be_class == BER_CLASS_APPLICATION &&
vb->

libagentx: honour searchrange end

2022-07-21 Thread Martijn van Duren
When doing a getnext/getbulk request, agentx diverges from snmp that it
sends a searchrange, instead of a simple next. The end oid is currently
not correctly handled by both snmpd(8) and libagentx. If a backend has
two ranges with one or more other backends having a region claimed
in between snmpd(8) can skip over that other region.

To make it concrete. Moving most of mib.c into an libagentx based
application make it claim ipfRouteEntStatus and dot1dBaseNumPorts.
But the snmp tree (which resides in between) remains part of snmpd(8).
Doing a getnext on ipfRouteEntStatus.9 would make snmpd(8) skip to
dot1dBaseNumPorts.0, instead of snmpInPkts.0.

This diff should fix the libagentx side of things.

OK?

martijn@

Index: agentx.c
===
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.15
diff -u -p -r1.15 agentx.c
--- agentx.c19 Jul 2022 19:25:42 -  1.15
+++ agentx.c21 Jul 2022 20:14:04 -
@@ -2722,7 +2722,8 @@ agentx_varbind_start(struct agentx_varbi
 getnext:
while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN)
axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo);
-   if (axo == NULL) {
+   if (axo == NULL ||
+   ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) > 0) {
agentx_varbind_endofmibview(axv);
return;
}



libagentx: enable objects on dynamic index enabling

2022-07-17 Thread Martijn van Duren
Diff below is needed for migrating snmpd's mib.c to a libagentx based
environment. Specifically the OPENBSD-MEM-MIB:memIfTable, which relies
on IF-MIB:ifIndex.

The issue bubbles forth from the following behaviour:
In libagentx both an object and an index are enabled (started) based
when their respective region is started. The order in which these
regions are started is undetermined.
For agentx_object the agentx_object_start function is halted when not
all indices are in AX_CSTATE_OPEN.

The problem arises from the fact that agentx_index_finalize (the
function called when we know that the request to be opened can be
answered) is combined with a dynamic index (an index not registered
at the snmpd server, but determined by the libagentx based application):
The index is placed in the AX_CSTATE_OPEN state, but it returns
immediately, ignoring any objects that rely on it being opened.

The diff below replaces the return statement to a jump statement
to the final part of the function trying to open the functions.

OK?

martijn@

Index: agentx.c
===
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.14
diff -u -p -r1.14 agentx.c
--- agentx.c24 Oct 2021 18:03:27 -  1.14
+++ agentx.c17 Jul 2022 12:03:55 -
@@ -1660,7 +1660,7 @@ agentx_index_finalize(struct ax_pdu *pdu
 #endif
if (axi->axi_type == AXI_TYPE_DYNAMIC) {
axi->axi_cstate = AX_CSTATE_OPEN;
-   return 0;
+   goto objects_start;
}
 
resp = &(pdu->ap_payload.ap_response);
@@ -1717,6 +1717,7 @@ agentx_index_finalize(struct ax_pdu *pdu
if (axi->axi_dstate == AX_DSTATE_CLOSE)
return agentx_index_close(axi);
 
+ objects_start:
/* TODO Make use of range_subid register */
for (i = 0; i < axi->axi_objectlen; i++) {
if (axi->axi_object[i]->axo_dstate == AX_DSTATE_OPEN) {



Re: snmpd(8): clean up variable printing

2022-06-29 Thread Martijn van Duren
On Wed, 2022-01-19 at 16:23 +0100, Martijn van Duren wrote:
> The new code uses smi_print_element when debugging is enabled to trace
> calls. Unfortunately the current smi_print_element lacks in quite a few
> departments. This diff rewrites smi_print_element to be more concise
> than what we currently have, without moving into the more complex
> territory that snmp(1) has.
> 
> Unknown types are printed in a similar fashion to what tcpdump(8)'s
> snmp output does.
> 
> This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and
> ENDOFMIBVIEW) and distinguishing between different integer types.
> 
> I kept the current implementation under smi_print_element_legacy, so
> that we don't change the output of trap handlers. We should probably
> revisit that one at some point, but I don't think to go into that
> territory right now.
> 
> OK?
> 
> martijn@
> 
> p.s. I'm not particularly thrilled about the type hinting, but it's
> the cleanest that I could come up with without being too much of an
> eyesore or filling the screen up even further.
> 
ping

Index: smi.c
===
RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v
retrieving revision 1.30
diff -u -p -r1.30 smi.c
--- smi.c   21 Oct 2021 15:08:15 -  1.30
+++ smi.c   29 Jun 2022 11:44:26 -
@@ -46,6 +46,7 @@
 
 #include "snmpd.h"
 #include "mib.h"
+#include "application.h"
 
 #define MINIMUM(a, b)  (((a) < (b)) ? (a) : (b))
 
@@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r
 }
 #endif
 
+/* Keep around so trap handle scripts don't break */
 char *
-smi_print_element(struct ber_element *root)
+smi_print_element_legacy(struct ber_element *root)
 {
char*str = NULL, *buf, *p;
size_t   len, i;
@@ -520,6 +522,140 @@ smi_print_element(struct ber_element *ro
case BER_TYPE_SET:
default:
str = strdup("");
+   break;
+   }
+
+   return (str);
+
+ fail:
+   free(str);
+   return (NULL);
+}
+
+char *
+smi_print_element(struct ber_element *root)
+{
+   char*str = NULL, *buf, *p;
+   long longv;
+   struct ber_oid   o;
+   char strbuf[BUFSIZ];
+
+   switch (root->be_class) {
+   case BER_CLASS_UNIVERSAL:
+   switch (root->be_type) {
+   case BER_TYPE_INTEGER:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld", v) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OBJECT:
+   if (ober_get_oid(root, &o) == -1)
+   goto fail;
+   if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
+   sizeof(strbuf), 0)) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OCTETSTRING:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   p = reallocarray(NULL, 4, root->be_len + 1);
+   if (p == NULL)
+   goto fail;
+   strvisx(p, buf, root->be_len, VIS_NL);
+   if (asprintf(&str, "\"%s\"", p) == -1) {
+   free(p);
+   goto fail;
+   }
+   free(p);
+   break;
+   case BER_TYPE_NULL:
+   if (asprintf(&str, "null") == -1)
+   goto fail;
+   break;
+   default:
+   /* Should not happen in a valid SNMP packet */
+   if (asprintf(&str, "[U/%u]", root->be_type) == -1)
+   goto fail;
+   break;
+   }
+   break;
+   case BER_CLASS_APPLICATION:
+   switch (root->be_type) {
+   case SNMP_T_IPADDR:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   if (asprintf(&str, "%s",
+   inet_ntoa(*(struct in_addr *)buf)) == -1)
+   goto fail;
+   break;
+   case SNMP_T_COUNTER32:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld(c32)", v) == -1)
+

Re: snmpd(8): Add blocklist feature

2022-06-29 Thread Martijn van Duren
On Tue, 2022-06-28 at 12:33 +0200, Martijn van Duren wrote:
> On Tue, 2022-06-28 at 12:21 +0200, Martijn van Duren wrote:
> > On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote:
> > > Back in 2020 florian@ added the filter-pf-addresses keyword.
> > > Although useful, I always felt it was a bit too case-specific. The diff
> > > below adds a new blocklist feature/backend, which takes hold of an
> > > entire subtree, effectively removing it from the tree.
> > > 
> > > With this I've deprecated the filter-pf-address case and should
> > > probably be removed somewhere after 7.4. The filter-routes case can't
> > > be removed unfortunately, since its behaviour is not identical, and
> > > instead adds filters to the routing socket, preventing updates being
> > > pushed to snmpd(8).
> > > 
> > > Feedback/OK?
> > > 
> > > martijn@
> > > 
> > Also clean up after ourselves if appl_exception fails.
> > 
> *sigh* Missed a return.
> 
And also some cleanup during shutdown.

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile
--- Makefile19 Jan 2022 11:00:56 -  1.18
+++ Makefile29 Jun 2022 11:35:23 -
@@ -3,6 +3,7 @@
 PROG=  snmpd
 MAN=   snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
+   application_blocklist.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
pf.c proc.c usm.c traphandler.c util.c
 
Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.5
diff -u -p -r1.5 application.c
--- application.c   27 Jun 2022 10:31:17 -  1.5
+++ application.c   29 Jun 2022 11:35:23 -
@@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_
 void
 appl_init(void)
 {
+   appl_blocklist_init();
appl_legacy_init();
 }
 
@@ -156,6 +157,7 @@ appl_shutdown(void)
 {
struct appl_context *ctx, *tctx;
 
+   appl_blocklist_shutdown();
appl_legacy_shutdown();
 
TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
Index: application.h
===
RCS file: /cvs/src/usr.sbin/snmpd/application.h,v
retrieving revision 1.1
diff -u -p -r1.1 application.h
--- application.h   19 Jan 2022 10:59:35 -  1.1
+++ application.h   29 Jun 2022 11:35:23 -
@@ -133,3 +133,7 @@ struct ber_element *appl_exception(enum 
 /* application_legacy.c */
 voidappl_legacy_init(void);
 voidappl_legacy_shutdown(void);
+
+/* application_blocklist.c */
+voidappl_blocklist_init(void);
+voidappl_blocklist_shutdown(void);
Index: application_blocklist.c
===
RCS file: application_blocklist.c
diff -N application_blocklist.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ application_blocklist.c 29 Jun 2022 11:35:23 -
@@ -0,0 +1,141 @@
+/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */
+
+/*
+ * Copyright (c) 2022 Martijn van Duren 
+ *
+ * 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 
+#include 
+
+#include "application.h"
+#include "snmpd.h"
+
+struct appl_varbind *appl_blocklist_response(size_t);
+void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t,
+const char *, struct appl_varbind *);
+
+struct appl_backend_functions appl_blocklist_functions = {
+   .ab_get = appl_blocklist_get,
+   .ab_getnext = appl_blocklist_getnext,
+   .ab_getbulk = NULL,
+};
+
+struct appl_backend appl_blocklist = {
+   .ab_name = "blocklist",
+   .ab_cookie = NULL,
+   .ab_retries = 0,
+   .ab_fn = &appl_blocklist_functions
+};
+
+static struct appl_varbind *response = NULL;
+static size_t responsesz = 0;
+

Re: snmpd(8): Add blocklist feature

2022-06-28 Thread Martijn van Duren
On Tue, 2022-06-28 at 12:21 +0200, Martijn van Duren wrote:
> On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote:
> > Back in 2020 florian@ added the filter-pf-addresses keyword.
> > Although useful, I always felt it was a bit too case-specific. The diff
> > below adds a new blocklist feature/backend, which takes hold of an
> > entire subtree, effectively removing it from the tree.
> > 
> > With this I've deprecated the filter-pf-address case and should
> > probably be removed somewhere after 7.4. The filter-routes case can't
> > be removed unfortunately, since its behaviour is not identical, and
> > instead adds filters to the routing socket, preventing updates being
> > pushed to snmpd(8).
> > 
> > Feedback/OK?
> > 
> > martijn@
> > 
> Also clean up after ourselves if appl_exception fails.
> 
*sigh* Missed a return.

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile
--- Makefile19 Jan 2022 11:00:56 -  1.18
+++ Makefile28 Jun 2022 10:32:40 -
@@ -3,6 +3,7 @@
 PROG=  snmpd
 MAN=   snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
+   application_blocklist.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
pf.c proc.c usm.c traphandler.c util.c
 
Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.5
diff -u -p -r1.5 application.c
--- application.c   27 Jun 2022 10:31:17 -  1.5
+++ application.c   28 Jun 2022 10:32:40 -
@@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_
 void
 appl_init(void)
 {
+   appl_blocklist_init();
appl_legacy_init();
 }
 
Index: application.h
===
RCS file: /cvs/src/usr.sbin/snmpd/application.h,v
retrieving revision 1.1
diff -u -p -r1.1 application.h
--- application.h   19 Jan 2022 10:59:35 -  1.1
+++ application.h   28 Jun 2022 10:32:40 -
@@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum 
 /* application_legacy.c */
 voidappl_legacy_init(void);
 voidappl_legacy_shutdown(void);
+
+/* application_blocklist.c */
+voidappl_blocklist_init(void);
Index: application_blocklist.c
===
RCS file: application_blocklist.c
diff -N application_blocklist.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ application_blocklist.c 28 Jun 2022 10:32:40 -
@@ -0,0 +1,134 @@
+/*     $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */
+
+/*
+ * Copyright (c) 2022 Martijn van Duren 
+ *
+ * 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 
+#include 
+
+#include "application.h"
+#include "snmpd.h"
+
+struct appl_varbind *appl_blocklist_response(size_t);
+void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t,
+const char *, struct appl_varbind *);
+
+struct appl_backend_functions appl_blocklist_functions = {
+   .ab_get = appl_blocklist_get,
+   .ab_getnext = appl_blocklist_getnext,
+   .ab_getbulk = NULL,
+};
+
+struct appl_backend appl_blocklist = {
+   .ab_name = "blocklist",
+   .ab_cookie = NULL,
+   .ab_retries = 0,
+   .ab_fn = &appl_blocklist_functions
+};
+
+static struct appl_varbind *response = NULL;
+static size_t responsesz = 0;
+
+struct appl_varbind *
+appl_blocklist_response(size_t nvarbind)
+{
+   struct appl_varbind *tmp;
+   size_t i;
+
+   if (responsesz < nvarbind) {
+   if ((tmp = recallocarray(response, responsesz, nvarbind,
+   sizeof(*response))) == NULL) {
+   log_warn(NULL);
+   return NULL;
+   }
+   responsesz = nvarbind;
+   response = tmp;
+   }
+   for

Re: snmpd(8): Add blocklist feature

2022-06-28 Thread Martijn van Duren
On Tue, 2022-06-28 at 10:21 +0200, Martijn van Duren wrote:
> Back in 2020 florian@ added the filter-pf-addresses keyword.
> Although useful, I always felt it was a bit too case-specific. The diff
> below adds a new blocklist feature/backend, which takes hold of an
> entire subtree, effectively removing it from the tree.
> 
> With this I've deprecated the filter-pf-address case and should
> probably be removed somewhere after 7.4. The filter-routes case can't
> be removed unfortunately, since its behaviour is not identical, and
> instead adds filters to the routing socket, preventing updates being
> pushed to snmpd(8).
> 
> Feedback/OK?
> 
> martijn@
> 
Also clean up after ourselves if appl_exception fails.

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile
--- Makefile19 Jan 2022 11:00:56 -  1.18
+++ Makefile28 Jun 2022 10:20:23 -
@@ -3,6 +3,7 @@
 PROG=  snmpd
 MAN=   snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
+   application_blocklist.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
pf.c proc.c usm.c traphandler.c util.c
 
Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.5
diff -u -p -r1.5 application.c
--- application.c   27 Jun 2022 10:31:17 -  1.5
+++ application.c   28 Jun 2022 10:20:23 -
@@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_
 void
 appl_init(void)
 {
+   appl_blocklist_init();
appl_legacy_init();
 }
 
Index: application.h
===
RCS file: /cvs/src/usr.sbin/snmpd/application.h,v
retrieving revision 1.1
diff -u -p -r1.1 application.h
--- application.h   19 Jan 2022 10:59:35 -  1.1
+++ application.h   28 Jun 2022 10:20:23 -
@@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum 
 /* application_legacy.c */
 voidappl_legacy_init(void);
 voidappl_legacy_shutdown(void);
+
+/* application_blocklist.c */
+voidappl_blocklist_init(void);
Index: application_blocklist.c
===
RCS file: application_blocklist.c
diff -N application_blocklist.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ application_blocklist.c 28 Jun 2022 10:20:23 -
@@ -0,0 +1,132 @@
+/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */
+
+/*
+ * Copyright (c) 2022 Martijn van Duren 
+ *
+ * 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 
+#include 
+
+#include "application.h"
+#include "snmpd.h"
+
+struct appl_varbind *appl_blocklist_response(size_t);
+void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t,
+const char *, struct appl_varbind *);
+
+struct appl_backend_functions appl_blocklist_functions = {
+   .ab_get = appl_blocklist_get,
+   .ab_getnext = appl_blocklist_getnext,
+   .ab_getbulk = NULL,
+};
+
+struct appl_backend appl_blocklist = {
+   .ab_name = "blocklist",
+   .ab_cookie = NULL,
+   .ab_retries = 0,
+   .ab_fn = &appl_blocklist_functions
+};
+
+static struct appl_varbind *response = NULL;
+static size_t responsesz = 0;
+
+struct appl_varbind *
+appl_blocklist_response(size_t nvarbind)
+{
+   struct appl_varbind *tmp;
+   size_t i;
+
+   if (responsesz < nvarbind) {
+   if ((tmp = recallocarray(response, responsesz, nvarbind,
+   sizeof(*response))) == NULL) {
+   log_warn(NULL);
+   return NULL;
+   }
+   responsesz = nvarbind;
+   response = tmp;
+   }
+   for (i = 0; i < nvarbind; i++)
+   response[i].av_next = i + 1 == nvarbind ?
+   NULL : &(response[i + 1]);
+   return response;
+}
+
+void

snmpd(8): Allow for symbolic OID names in snmpd.conf

2022-06-28 Thread Martijn van Duren
When playing with the blocklist I noticed that the oid directive only
supports numeric OIDs. So if florian wants to use filter-pf-table in the
future he'd have to set:
blocklist 1.3.6.1.4.1.30155.1.9.129
which is pure insanity, if:
blocklist pfTblAddrTable
could be an option.

Diff below changes the "oid" parse.y definition from ober_string2oid to
smi_string2oid, which allows for the locally known symbolic names.
This also helps out with existing statements ("system oid",
"trap handle oid", and "trap receiver", and "oid").

While here, rename sysoid to oid, since it's not used by the system
oid anymore and do a little KNF.

OK?

martijn@

Index: parse.y
===
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.73
diff -u -p -r1.73 parse.y
--- parse.y 21 Nov 2021 13:33:53 -  1.73
+++ parse.y 28 Jun 2022 09:02:36 -
@@ -666,21 +666,20 @@ optwrite  : READONLY  { $$ = 
0; }
;
 
 oid: STRING{
-   struct ber_oid  *sysoid;
-   if ((sysoid =
-   calloc(1, sizeof(*sysoid))) == NULL) {
+   struct ber_oid  *oid;
+   if ((oid = calloc(1, sizeof(*oid))) == NULL) {
yyerror("calloc");
free($1);
YYERROR;
}
-   if (ober_string2oid($1, sysoid) == -1) {
+   if (smi_string2oid($1, oid) == -1) {
yyerror("invalid OID: %s", $1);
-   free(sysoid);
+   free(oid);
free($1);
YYERROR;
}
free($1);
-   $$ = sysoid;
+   $$ = oid;
}
;
 



snmpd(8): Add blocklist feature

2022-06-28 Thread Martijn van Duren
Back in 2020 florian@ added the filter-pf-addresses keyword.
Although useful, I always felt it was a bit too case-specific. The diff
below adds a new blocklist feature/backend, which takes hold of an
entire subtree, effectively removing it from the tree.

With this I've deprecated the filter-pf-address case and should
probably be removed somewhere after 7.4. The filter-routes case can't
be removed unfortunately, since its behaviour is not identical, and
instead adds filters to the routing socket, preventing updates being
pushed to snmpd(8).

Feedback/OK?

martijn@

Index: Makefile
===
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile
--- Makefile19 Jan 2022 11:00:56 -  1.18
+++ Makefile28 Jun 2022 08:18:17 -
@@ -3,6 +3,7 @@
 PROG=  snmpd
 MAN=   snmpd.8 snmpd.conf.5
 SRCS=  parse.y log.c snmpe.c application.c application_legacy.c \
+   application_blocklist.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
pf.c proc.c usm.c traphandler.c util.c
 
Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.5
diff -u -p -r1.5 application.c
--- application.c   27 Jun 2022 10:31:17 -  1.5
+++ application.c   28 Jun 2022 08:18:17 -
@@ -148,6 +148,7 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_
 void
 appl_init(void)
 {
+   appl_blocklist_init();
appl_legacy_init();
 }
 
Index: application.h
===
RCS file: /cvs/src/usr.sbin/snmpd/application.h,v
retrieving revision 1.1
diff -u -p -r1.1 application.h
--- application.h   19 Jan 2022 10:59:35 -  1.1
+++ application.h   28 Jun 2022 08:18:17 -
@@ -133,3 +133,6 @@ struct ber_element *appl_exception(enum 
 /* application_legacy.c */
 voidappl_legacy_init(void);
 voidappl_legacy_shutdown(void);
+
+/* application_blocklist.c */
+voidappl_blocklist_init(void);
Index: application_blocklist.c
===
RCS file: application_blocklist.c
diff -N application_blocklist.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ application_blocklist.c 28 Jun 2022 08:18:17 -
@@ -0,0 +1,122 @@
+/* $OpenBSD: application.c,v 1.5 2022/06/27 10:31:17 martijn Exp $ */
+
+/*
+ * Copyright (c) 2022 Martijn van Duren 
+ *
+ * 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 
+#include 
+
+#include "application.h"
+#include "snmpd.h"
+
+struct appl_varbind *appl_blocklist_response(size_t);
+void appl_blocklist_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_blocklist_getnext(struct appl_backend *, int32_t, int32_t,
+const char *, struct appl_varbind *);
+
+struct appl_backend_functions appl_blocklist_functions = {
+   .ab_get = appl_blocklist_get,
+   .ab_getnext = appl_blocklist_getnext,
+   .ab_getbulk = NULL,
+};
+
+struct appl_backend appl_blocklist = {
+   .ab_name = "blocklist",
+   .ab_cookie = NULL,
+   .ab_retries = 0,
+   .ab_fn = &appl_blocklist_functions
+};
+
+static struct appl_varbind *response = NULL;
+static size_t responsesz = 0;
+
+struct appl_varbind *
+appl_blocklist_response(size_t nvarbind)
+{
+   struct appl_varbind *tmp;
+   size_t i;
+
+   if (responsesz < nvarbind) {
+   if ((tmp = recallocarray(response, responsesz, nvarbind,
+   sizeof(*response))) == NULL) {
+   log_warn(NULL);
+   return NULL;
+   }
+   responsesz = nvarbind;
+   response = tmp;
+   }
+   for (i = 0; i < nvarbind; i++)
+   response[i].av_next = i + 1 == nvarbind ?
+   NULL : &(response[i + 1]);
+   return response;
+}
+
+void
+appl_blocklist_init(void)
+{
+   extern struct snmpd *snmpd_env;
+   size_t i;
+
+   for (i = 0; i < snmpd_env->sc_nblocklist; i++)
+   appl_register(NULL,

snmpd(8): Add rudimentary AgentX support

2022-06-27 Thread Martijn van Duren
13 @@ appl_response(struct appl_backend *backe
log_warnx("Invalid error index");
invalid = 1;
}
+/* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */
+#if PEDANTIC
if (error == APPL_ERROR_NOERROR && index != 0) {
log_warnx("error index with no error");
invalid = 1;
}
+#endif
if (vb == NULL && origvb != NULL) {
log_warnx("%s: Request %"PRIu32" returned less varbinds then "
"requested", backend->ab_name, requestid);
Index: usr.sbin/snmpd.application.agentx/application.h
===
RCS file: /cvs/src/usr.sbin/snmpd/application.h,v
retrieving revision 1.1
diff -u -p -r1.1 application.h
--- usr.sbin/snmpd.application.agentx/application.h 19 Jan 2022 10:59:35 
-  1.1
+++ usr.sbin/snmpd.application.agentx/application.h 27 Jun 2022 11:29:36 
-
@@ -117,6 +117,7 @@ struct appl_backend {
RB_HEAD(appl_requests, appl_request_downstream) ab_requests;
 };
 
+void appl(void);
 void appl_init(void);
 void appl_shutdown(void);
 enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid 
*,
@@ -133,3 +134,8 @@ struct ber_element *appl_exception(enum 
 /* application_legacy.c */
 voidappl_legacy_init(void);
 voidappl_legacy_shutdown(void);
+
+/* application_agentx.c */
+voidappl_agentx(void);
+voidappl_agentx_init(void);
+voidappl_agentx_shutdown(void);
Index: usr.sbin/snmpd.application.agentx/application_agentx.c
===
RCS file: usr.sbin/snmpd.application.agentx/application_agentx.c
diff -N usr.sbin/snmpd.application.agentx/application_agentx.c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ usr.sbin/snmpd.application.agentx/application_agentx.c  27 Jun 2022 
11:29:36 -
@@ -0,0 +1,842 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2021 Martijn van Duren 
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "application.h"
+#include "ax.h"
+#include "log.h"
+#include "smi.h"
+#include "snmp.h"
+#include "snmpd.h"
+
+#define AGENTX_DEFAULTTIMEOUT 5
+
+struct appl_agentx {
+   uint32_t ax_id;
+   struct ax *ax_ax;
+   struct event ax_ev;
+
+   struct appl_agentx_session *ax_session; /* List of sessions */
+   RB_ENTRY(appl_agentx) ax_entry;
+};
+
+struct appl_agentx_session {
+   struct appl_agentx *axs_ax;
+   uint32_t axs_id;
+   enum ax_byte_order axs_byteorder;
+   uint8_t axs_timeout;
+   struct ax_oid axs_oid;
+   struct ax_ostring axs_descr;
+   struct appl_backend axs_backend;
+
+   RB_ENTRY(appl_agentx_session) axs_entry;
+   struct appl_agentx_session *axs_next;
+};
+
+void appl_agentx_listen(struct agentx_master *);
+void appl_agentx_accept(int, short, void *);
+void appl_agentx_free(struct appl_agentx *);
+void appl_agentx_read(int, short, void *);
+void appl_agentx_open(struct appl_agentx *, struct ax_pdu *);
+void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
+void appl_agentx_session_free(struct appl_agentx_session *);
+void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
+struct appl_varbind *);
+void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
+ssize_t appl_agentx_send(struct appl_agentx_session *);
+struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
+struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
+struct ax_ostring *appl_agentx_string2ostring(const char *,
+struct ax_ostring *);
+int appl_agentx_cmp(struct 

snmpd(8): Application.c properly initialize oidbuf for appl_region

2022-06-27 Thread Martijn van Duren
When registering a region in appl_region (through appl_register) we
fill oidbuf with strlcat, but we don't start from a clean state and
might have garbage prepended.

This oidbuf is only used on error-conditions, so it's unlikely to
trigger with the current code. Diff below properly initializes it.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.3
diff -u -p -r1.3 application.c
--- application.c   22 Feb 2022 15:59:13 -  1.3
+++ application.c   27 Jun 2022 10:10:37 -
@@ -224,6 +224,7 @@ appl_region(struct appl_context *ctx, ui
goto overlap;
 
/* Don't use smi_oid2string, because appl_register can't use it */
+   oidbuf[0] = '\0';
for (i = 0; i < oid->bo_n; i++) {
if (i != 0)
strlcat(oidbuf, ".", sizeof(oidbuf));



Re: syslogd(8): Add hostname parsing support

2022-01-30 Thread Martijn van Duren
On Wed, 2022-01-26 at 09:18 -0700, Theo de Raadt wrote:
> > However, as things stand interpretation can be broken with the base
> > tools. I can't fix garbage input.
> 
> Your proposal builds a mechanism which encourages making decisions based
> upon parsing garbage input.

So let's just focus on my original diff and let that one stand on its
own.

Say I have a sender with the following line:
sender$ tail -2 /etc/syslog.conf
!doas
*.* @100.64.2.2

and a receiver with:
receiver$ tail -6 /etc/syslog.conf 
+100.64.2.3
!doas
*.* /tmp/prog_doas

!sender
*.* /tmp/prog_sender

receiver runs syslogd with -U100.64.2.2

When running syslogd on sender without any flags and doing a simple
`doas ls`:
receiver$ tail /tmp/prog_*
==> /tmp/prog_doas <==
Jan 30 15:03:51 100.64.2.3 doas: martijn ran command ls as root from 
/home/martijn

==> /tmp/prog_sender <==
receiver$ 

When running syslogd on sender with -h and doing a simple `doas ls`:
receiver$ tail /tmp/prog_*
==> /tmp/prog_doas <==

==> /tmp/prog_sender <==
Jan 30 15:08:08 100.64.2.3 sender doas: martijn ran command ls as root from 
/home/martijn
receiver$ 

With my diff applied and without -h:
receiver$ tail /tmp/prog_*
==> /tmp/prog_doas <==
Jan 30 15:11:55 100.64.2.3 doas: martijn ran command ls as root from 
/home/martijn

==> /tmp/prog_sender <==
receiver$ 

With my diff applied and with -h:
receiver$ tail /tmp/prog_*
==> /tmp/prog_doas <==
Jan 30 15:12:47 100.64.2.3 doas: martijn ran command ls as root from 
/home/martijn

==> /tmp/prog_sender <==
receiver$ 

So no new mechanism is introduced, but I am trying to make sure that
existing mechanisms work more consistent with what we already offer
out of the box in base.

martijn@

Index: parsemsg.c
===
RCS file: /cvs/src/usr.sbin/syslogd/parsemsg.c,v
retrieving revision 1.1
diff -u -p -r1.1 parsemsg.c
--- parsemsg.c  13 Jan 2022 10:34:07 -  1.1
+++ parsemsg.c  30 Jan 2022 14:20:36 -
@@ -17,8 +17,14 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include 
+
+#include 
+#include 
+
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -29,27 +35,42 @@
 
 size_t parsemsg_timestamp_bsd(const char *, char *);
 size_t parsemsg_timestamp_v1(const char *, char *);
+size_t parsemsg_hostname(const char *, char *);
 size_t parsemsg_prog(const char *, char *);
 
 struct msg *
 parsemsg(const char *msgstr, struct msg *msg)
 {
-   size_t n;
+   size_t timelen, proglen;
+   const char *hostname;
 
msg->m_pri = -1;
msgstr += parsemsg_priority(msgstr, &msg->m_pri);
if (msg->m_pri &~ (LOG_FACMASK|LOG_PRIMASK))
msg->m_pri = -1;
 
-   if ((n = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0)
-   n = parsemsg_timestamp_v1(msgstr, msg->m_timestamp);
-   msgstr += n;
+   if ((timelen = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0)
+   timelen = parsemsg_timestamp_v1(msgstr, msg->m_timestamp);
+   msgstr += timelen;
 
-   while (isspace(msgstr[0]))
+   while (isspace((unsigned char)msgstr[0]))
msgstr++;
 
-   parsemsg_prog(msgstr, msg->m_prog);
+   hostname = msgstr;
+   msgstr += parsemsg_hostname(msgstr, msg->m_hostname);
+ 
+   while (isspace((unsigned char)msgstr[0]))
+   msgstr++;
 
+   proglen = parsemsg_prog(msgstr, msg->m_prog);
+
+   /*
+* Without timestamp and tag, assume hostname as part of message.
+*/
+   if (!timelen && !proglen) {
+   msg->m_hostname[0] = '\0';
+   msgstr = hostname;
+   }
strlcpy(msg->m_msg, msgstr, sizeof(msg->m_msg));
 
return msg;
@@ -169,6 +190,47 @@ parsemsg_timestamp_v1(const char *msgstr
return msg - msgstr;
 }
 
+/*
+ * Parse the ip address or hostname according to inet_pton and res_hnok and
+ * return the length of the hostname including the trailing space if available.
+ */
+size_t
+parsemsg_hostname(const char *msgstr, char *hostname)
+{
+   size_t len;
+   struct in_addr buf4;
+   struct in6_addr buf6;
+
+   if (msgstr[0] == '-' && (msgstr[1] == ' ' || msgstr[1] == '\0')) {
+   hostname[0] = '\0';
+   if (msgstr[1] == '\0')
+   return 1;
+   return 2;
+   }
+
+   if ((len = strcspn(msgstr, " ")) > HOST_NAME_MAX)
+   return 0;
+   strlcpy(hostname, msgstr, len + 1);
+   if (msgstr[len] == ' ')
+   len++;
+
+   if (inet_pton(AF_INET, hostname, &buf4) == 1 ||
+   inet_pton(AF_INET6, hostname, &buf6) == 1) {
+   return len;
+   }
+
+   if (res_hnok(hostname) == 0) {
+   hostname[0] = '\0';
+   return 0;
+   }
+   

Re: snmpd(8): clean up variable printing

2022-01-28 Thread Martijn van Duren
On Wed, 2022-01-19 at 16:23 +0100, Martijn van Duren wrote:
> The new code uses smi_print_element when debugging is enabled to trace
> calls. Unfortunately the current smi_print_element lacks in quite a few
> departments. This diff rewrites smi_print_element to be more concise
> than what we currently have, without moving into the more complex
> territory that snmp(1) has.
> 
> Unknown types are printed in a similar fashion to what tcpdump(8)'s
> snmp output does.
> 
> This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and
> ENDOFMIBVIEW) and distinguishing between different integer types.
> 
> I kept the current implementation under smi_print_element_legacy, so
> that we don't change the output of trap handlers. We should probably
> revisit that one at some point, but I don't think to go into that
> territory right now.
> 
> OK?
> 
> martijn@
> 
> p.s. I'm not particularly thrilled about the type hinting, but it's
> the cleanest that I could come up with without being too much of an
> eyesore or filling the screen up even further.

Found a missing break after the BER_TYPE_NULL case.

OK?

martijn@

Index: smi.c
===
RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v
retrieving revision 1.30
diff -u -p -r1.30 smi.c
--- smi.c   21 Oct 2021 15:08:15 -  1.30
+++ smi.c   29 Jan 2022 07:28:15 -
@@ -46,6 +46,7 @@
 
 #include "snmpd.h"
 #include "mib.h"
+#include "application.h"
 
 #define MINIMUM(a, b)  (((a) < (b)) ? (a) : (b))
 
@@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r
 }
 #endif
 
+/* Keep around so trap handle scripts don't break */
 char *
-smi_print_element(struct ber_element *root)
+smi_print_element_legacy(struct ber_element *root)
 {
char*str = NULL, *buf, *p;
size_t   len, i;
@@ -520,6 +522,140 @@ smi_print_element(struct ber_element *ro
case BER_TYPE_SET:
default:
str = strdup("");
+   break;
+   }
+
+   return (str);
+
+ fail:
+   free(str);
+   return (NULL);
+}
+
+char *
+smi_print_element(struct ber_element *root)
+{
+   char*str = NULL, *buf, *p;
+   long longv;
+   struct ber_oid   o;
+   char strbuf[BUFSIZ];
+
+   switch (root->be_class) {
+   case BER_CLASS_UNIVERSAL:
+   switch (root->be_type) {
+   case BER_TYPE_INTEGER:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld", v) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OBJECT:
+   if (ober_get_oid(root, &o) == -1)
+   goto fail;
+   if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
+   sizeof(strbuf), 0)) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OCTETSTRING:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   p = reallocarray(NULL, 4, root->be_len + 1);
+   if (p == NULL)
+   goto fail;
+   strvisx(p, buf, root->be_len, VIS_NL);
+   if (asprintf(&str, "\"%s\"", p) == -1) {
+   free(p);
+   goto fail;
+   }
+   free(p);
+   break;
+   case BER_TYPE_NULL:
+   if (asprintf(&str, "null") == -1)
+   goto fail;
+   break;
+   default:
+   /* Should not happen in a valid SNMP packet */
+   if (asprintf(&str, "[U/%u]", root->be_type) == -1)
+   goto fail;
+   break;
+   }
+   break;
+   case BER_CLASS_APPLICATION:
+   switch (root->be_type) {
+   case SNMP_T_IPADDR:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   if (asprintf(&str, "%s",
+   inet_ntoa(*(struct in_addr *)buf)) == -1)
+   goto fail;
+   break;
+   case SNMP_T_COUNTER32:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "

Re: syslogd(8): Add hostname parsing support

2022-01-25 Thread Martijn van Duren
Thanks for looking into this.

On Sat, 2022-01-22 at 10:05 -0700, Theo de Raadt wrote:
> > Note that this only adds the parsing, the rest of the current behaviour
> > of stays the same. I have another diff in the pipeline for allowing the
> > hostname in the message.
> 
> I object to this process.
> 
> You want to add parsing code as a fait accompli. With no justification.
> Then later on, spring on us the code that uses it.

Sorry if my phrasing was off, but I do think the diff stands on its own
and I thought I made that case in the first paragraph. Even though I
include the mentioned follow up diffs below, let me try to rephrase my
case for it as a stand alone diff.

Right now we have 3 elements where syslog.conf can filter on: priority,
hostname, and progname. hostname currently always comes from the input
source:
- sendsyslog(2), /dev/klog, unix socket, vlogmsg(), and markit() use
  LocalHostName
- UDP/TCP/TLS print reverse lookup or numerical if -n is specified

My previous parsemsg() diff didn't change any behaviour, but makes it
more clear what's going on. When a message is handled through
printline() it goes through parsemsg(). Here we first check for an
optional priority and timestamp. parsemsg_priority() is the original
priority parsing code and parsemsg_timestamp_{bsd,v1} are the original
code lifted from logmsg. After that we continue to parsemsg_prog, which
is also lifted from logmsg(). In other words, at no point do we look
for a hostname in a message.
So if someone were to set -h in syslogd or use any other syslog server
that includes a hostname in the message and would relay it to a
syslogd(8) it would first parse priority/timestamp (if available) and
then continue to interpret the "first word" as progname. So a message
like: "<13>Jan 25 20:35:44 myhost myprog: mymsg" send over localhost
would have myhost as progname and localhost (or 127.0.0.1 if -n is
used) as hostname. So if someone would have "!myprog" in their
syslog.conf it would not match (while it should) and someone who
would have "!myhost" in their syslog.conf would match, while it
shouldn't.

What my diff does is based mainly on syslog(3) (and happens to match
what I've mostly seen in the wild) by saying that a progname should end
in a ':' or a '['. This is also what NetBSD does. By this minor change
we can reliably determine what the progname is and by that merit
determine what the hostname is.

The hostname in the original diff is stored in struct msg and not looked
at again, effectively replacing the hostname with the address found by
the input source. This behaviour should be identical to FreeBSD, which
has the -H flag to preserve the hostname in the message. To make this
explicit, the example message previously would be written out as:
"<13>Jan 25 20:35:44 localhost myprog: mymsg" instead of the current
"<13>Jan 25 20:35:44 localhost myhost myprog: mymsg"
and "+localhost" would continue to keep working as always.

>   What if we disagree
> with the code that uses it?  Will you delete this parsing code which
> nothing uses?

Fixing the "!progname" case should be sufficient reason on its own and
no other.
> 
> > - Timestamp: is easy to interpret, since it's a strict format.
> >   No changes here.
> 
> I believe "timestamp missing" is not strictly permitted.  But this was
> common for a while, and in OpenBSD it is the default message format.
> This is a due to the desire to make syslog_r(3) be signal/re-entrant
> safe on top of sendsyslog(2).  Then there is no good way of creating a
> timestamp string in the sender libc context.  A timestamp is easily
> re-inserted by the receiving syslogd.
> 
> > +   for (i = 0; i < HOST_NAME_MAX; i++) {
> 
> Unlike MAXHOSTNAMELEN/NI_MAXHOST, HOST_NAME_MAX storage does not include
> a NUL.  You might have the loop right.  Be careful.

I've defined it as "char m_hostname[HOST_NAME_MAX + 1];", so we have room
for the NUL.
> 
> > +* fqdn: [[:alnum:]-.]
> 
> That is not totally correct.
> 
> hostnames very often also contain '_' in the middle positions, early
> RFCs said no, but around 1990-ish Vixie in particular had to face
> reality..  I was involved in that conversation, it seems so long ago.
> 
> Your pattern is also incorrect in other ways, such as ".." is not legal,
> hostnames cannot start or end with '-' or '-', etc.  The current accepted
> rules are encoded in the undocumented libc function __res_hnok in
> lib/libc/net/res_comp.c

I wasn't aware of res_hnok. I see that it's used in other applications as
well. I changed it to use strcspn to look for the terminating space and
try inet_pton and res_hnok to check if it's valid. This makes the code a
lot cleaner. Thanks.
> 
> I don't know if false-identification of broken hostnames is bad or not
> I guess it depends what will happen with this information later on [ie.
> the part of your proposal that is being kept a secret].

The same thing that already happens. Writing it out to log-files and
using it to match on "+hostna

syslogd(8): Add hostname parsing support

2022-01-22 Thread Martijn van Duren
Currently syslogd(8) doesn't support hostname parsing for incoming
messages. This means that if a sender adds a hostname to a message it
will be interpreted as progname. Additionally, when a message is being
relayed, or there's some form of NATting taking place the originator of
the message will be completely lost.

The diff below adds hostname parsing and is already OK bluhm@, but since
I wanted to give other people a chance to yell at me before committing.
If nobody objects I'll commit it next weekend.
Note that this only adds the parsing, the rest of the current behaviour
of stays the same. I have another diff in the pipeline for allowing the
hostname in the message.

The definitions below are mostly in line with what {Net,Free}BSD do.

The BSD syslog protocol is rather loose and in quite a few locations
open for interpretation, so some definitions need to be hammered out:
- Timestamp: is easy to interpret, since it's a strict format.
  No changes here.
- Hostname: ip{,6} address, RFC1034 label, fqdn and is max
  HOST_NAME_MAX long.
- progname: alphanumeric, '-', '.', '_', ending in a ':' or '[' and is
  max 255 characters long.
  This changes in that a progname must end with ':' or '['.

I left the program name parsing as similar as I could, but make it
always distinguishable from the hostname. The ending in ':' or '[' is
part of syslog(3) and what I've always seen in the wild (ymmv). The only
exception to this rule is when progname (or ident as syslog(3) calls it)
fills up the message and ':' can't be placed. However, in that case it
won't fit in syslogd(8)'s progname definition of 255 characters.

According to syslog(3)'s guts, progname can be omitted if both ident
and LOG_PID have been omitted during openlog(3), and __progname
isn't set (no clue how that could happen op OpenBSD).
If this somehow miraculously happens the whole message is just the
"message" parameter of the syslog(3) call.

Because progname is now always identifiable, we know if we have a
hostname if progname is set and found on the first or second position
after timestamp/priority.

If progname is completely omitted we're a bit in no-mans-land. So here
we have to make a best effort. Currently we never set the hostname when
forwarding a message, unless syslogd is specified with '-h'. However,
this is not a sane default since it hinders relaying messages and bluhm@
and I already agree that we should make -h default and make -h a noop.

Going from syslog(3), if no timestamp is given we have just the message
plus (not quite) optional progname/ident. So my proposal is that
hostname should be parsed if either timestamp or progname is found.

Finally, the syslogd regress with rsyslog now fails, because we send
a message from the guts of the perl framework without timestamp or
progname, but rsyslog adds a timestamp when forwarding, but no other
changes are made. This test can be re-enabled when we allow using the
hostname from the message, or someone is brave enough to go into these
perl guts.

Additional OK? Comment?

martijn@

Index: usr.sbin/syslogd/parsemsg.c
===
RCS file: /cvs/src/usr.sbin/syslogd/parsemsg.c,v
retrieving revision 1.1
diff -u -p -r1.1 parsemsg.c
--- usr.sbin/syslogd/parsemsg.c 13 Jan 2022 10:34:07 -  1.1
+++ usr.sbin/syslogd/parsemsg.c 22 Jan 2022 14:52:04 -
@@ -17,6 +17,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include 
+
+#include 
+#include 
+
 #include 
 #include 
 #include 
@@ -29,27 +34,42 @@
 
 size_t parsemsg_timestamp_bsd(const char *, char *);
 size_t parsemsg_timestamp_v1(const char *, char *);
+size_t parsemsg_hostname(const char *, char *);
 size_t parsemsg_prog(const char *, char *);
 
 struct msg *
 parsemsg(const char *msgstr, struct msg *msg)
 {
-   size_t n;
+   size_t timelen, proglen;
+   const char *hostname;
 
msg->m_pri = -1;
msgstr += parsemsg_priority(msgstr, &msg->m_pri);
if (msg->m_pri &~ (LOG_FACMASK|LOG_PRIMASK))
msg->m_pri = -1;
 
-   if ((n = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0)
-   n = parsemsg_timestamp_v1(msgstr, msg->m_timestamp);
-   msgstr += n;
+   if ((timelen = parsemsg_timestamp_bsd(msgstr, msg->m_timestamp)) == 0)
+   timelen = parsemsg_timestamp_v1(msgstr, msg->m_timestamp);
+   msgstr += timelen;
+
+   while (isspace((unsigned char)msgstr[0]))
+   msgstr++;
 
-   while (isspace(msgstr[0]))
+   hostname = msgstr;
+   msgstr += parsemsg_hostname(msgstr, msg->m_hostname);
+ 
+   while (isspace((unsigned char)msgstr[0]))
msgstr++;
 
-   parsemsg_prog(msgstr, msg->m_prog);
+   proglen = parsemsg_prog(msgstr, msg->m_prog);
 
+   /*
+* Without timestamp and tag, assume hostname as part of message.
+*/
+   if (!timelen && !proglen) {
+   msg->m_hostname[0] = '\0';
+   

Re: snmpd(8): fix exceptions in mps.c

2022-01-20 Thread Martijn van Duren
Disregard this one for now. If o_get returns -1 it indicates an error,
so it should indicate this to the upper layers. However, the old code
can't handle this and I kept this code as is so that we can have some
time to let the dust settle around the new code (and easily switch back
if needed). Changing it to something more in line with the intended
behaviour of the current code, but still incorrect is not really an
improvement.

I'll revisit this one once more people have been exposed to the new
code.

On Thu, 2022-01-20 at 22:08 +0100, Martijn van Duren wrote:
> When hitting an error case in mps_get{,next}req, mps assumes that no OID
> has been linked to the root element. However, in both the get as well as
> the getnext case it's already set when entering the mib.c code, so going
> the fail goto path will result in the intended OID/exception pair being
> appended to the prior attached OID, instead of directly to the sequence
> (OID,OID,EXCEPTION).
> 
> This behaviour could already be triggered in the original codepath, so
> it's not a new issue introduced with the new application.c. And even
> though I would like to get rid of this code, I think it's worth fixing
> while we still have it around.
> 
> Easiest way to reproduce is to just return -1 in mib_getsys and do
> get syscontact.0 or getnext syscontact.
> 
> OK?
> 
> martijn@
> 
> Index: mps.c
> ===
> RCS file: /cvs/src/usr.sbin/snmpd/mps.c,v
> retrieving revision 1.29
> diff -u -p -r1.29 mps.c
> --- mps.c 30 Jun 2020 17:11:49 -  1.29
> +++ mps.c 20 Jan 2022 21:03:17 -
> @@ -150,7 +150,9 @@ fail:
>   return (-1);
>  
>   /* Set SNMPv2 extended error response. */
> - elm = ober_add_oid(elm, o);
> + if (root->be_sub != NULL)
> + ober_free_elements(ober_unlink_elements(root));
> + elm = ober_add_oid(root, o);
>   elm = ober_add_null(elm);
>   ober_set_header(elm, BER_CLASS_CONTEXT, error_type);
>   return (0);
> @@ -178,15 +180,16 @@ mps_setreq(struct snmp_message *msg, str
>  
>  int
>  mps_getnextreq(struct snmp_message *msg, struct ber_element *root,
> -struct ber_oid *o)
> +struct ber_oid *o0)
>  {
>   struct oid  *next = NULL;
>   struct ber_element  *ber = root;
>   struct oid   key, *value;
>   int  ret;
> - struct ber_oid   no;
> - unsigned longerror_type = 0;/* noSuchObject */
> + struct ber_oid   no, so, *o = &so;
> + unsigned longerror_type = 2;/* EndOfMibView */
>  
> + so = *o0;
>   if (o->bo_n > BER_MAX_OID_LEN)
>   return (-1);
>   bzero(&key, sizeof(key));
> @@ -259,7 +262,9 @@ fail:
>   return (-1);
>  
>   /* Set SNMPv2 extended error response. */
> - ber = ober_add_oid(ber, o);
> + if (root->be_sub != NULL)
> + ober_free_elements(ober_unlink_elements(root));
> + ber = ober_add_oid(root, o0);
>   ber = ober_add_null(ber);
>   ober_set_header(ber, BER_CLASS_CONTEXT, error_type);
>   return (0);
> 



application.c be more paranoid for misbehaving backends

2022-01-20 Thread Martijn van Duren
There's a missing NULL check in appl_response(). This should only happenwhen a 
backend is misbehaving, so I only managed to find this because
I'm actively bashing it right now. This should make us a little more
future-proof. Code further down the path already has similar NULL checks
against this variable.

OK?

martijn@

Index: application.c
===
RCS file: /cvs/src/usr.sbin/snmpd/application.c,v
retrieving revision 1.1
diff -u -p -r1.1 application.c
--- application.c   19 Jan 2022 10:59:35 -  1.1
+++ application.c   20 Jan 2022 21:52:41 -
@@ -1056,7 +1056,8 @@ appl_response(struct appl_backend *backe
appl_varbind_error(origvb, error);
origvb->avi_state = APPL_VBSTATE_DONE;
origvb->avi_varbind.av_oid = vb->av_oid;
-   if (vb->av_value->be_class == BER_CLASS_CONTEXT &&
+   if (vb->av_value != NULL &&
+   vb->av_value->be_class == BER_CLASS_CONTEXT &&
vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) {
nregion = appl_region_next(ureq->aru_ctx,
&(vb->av_oid), origvb->avi_region);



snmpd(8): fix exceptions in mps.c

2022-01-20 Thread Martijn van Duren
When hitting an error case in mps_get{,next}req, mps assumes that no OID
has been linked to the root element. However, in both the get as well as
the getnext case it's already set when entering the mib.c code, so going
the fail goto path will result in the intended OID/exception pair being
appended to the prior attached OID, instead of directly to the sequence
(OID,OID,EXCEPTION).

This behaviour could already be triggered in the original codepath, so
it's not a new issue introduced with the new application.c. And even
though I would like to get rid of this code, I think it's worth fixing
while we still have it around.

Easiest way to reproduce is to just return -1 in mib_getsys and do
get syscontact.0 or getnext syscontact.

OK?

martijn@

Index: mps.c
===
RCS file: /cvs/src/usr.sbin/snmpd/mps.c,v
retrieving revision 1.29
diff -u -p -r1.29 mps.c
--- mps.c   30 Jun 2020 17:11:49 -  1.29
+++ mps.c   20 Jan 2022 21:03:17 -
@@ -150,7 +150,9 @@ fail:
return (-1);
 
/* Set SNMPv2 extended error response. */
-   elm = ober_add_oid(elm, o);
+   if (root->be_sub != NULL)
+   ober_free_elements(ober_unlink_elements(root));
+   elm = ober_add_oid(root, o);
elm = ober_add_null(elm);
ober_set_header(elm, BER_CLASS_CONTEXT, error_type);
return (0);
@@ -178,15 +180,16 @@ mps_setreq(struct snmp_message *msg, str
 
 int
 mps_getnextreq(struct snmp_message *msg, struct ber_element *root,
-struct ber_oid *o)
+struct ber_oid *o0)
 {
struct oid  *next = NULL;
struct ber_element  *ber = root;
struct oid   key, *value;
int  ret;
-   struct ber_oid   no;
-   unsigned longerror_type = 0;/* noSuchObject */
+   struct ber_oid   no, so, *o = &so;
+   unsigned longerror_type = 2;/* EndOfMibView */
 
+   so = *o0;
if (o->bo_n > BER_MAX_OID_LEN)
return (-1);
bzero(&key, sizeof(key));
@@ -259,7 +262,9 @@ fail:
return (-1);
 
/* Set SNMPv2 extended error response. */
-   ber = ober_add_oid(ber, o);
+   if (root->be_sub != NULL)
+   ober_free_elements(ober_unlink_elements(root));
+   ber = ober_add_oid(root, o0);
ber = ober_add_null(ber);
ober_set_header(ber, BER_CLASS_CONTEXT, error_type);
return (0);



Re: ober_get_writebuf return correct length

2022-01-20 Thread Martijn van Duren
Forgot to mention, I checked all the instances of ober_get_writebuf I
could find and they either don't use it for the actual length, or ber
has been freshly initialised just before. So there's no problem here in
the known consumers.

On Thu, 2022-01-20 at 18:50 +0100, Martijn van Duren wrote:
> While reading through ber.c I noticed that ober_get_writebuf can return
> the wrong length when called multiple times on the same ber instance.
> 
> This is because ober_get_writebuf uses br_wend to calculate the length,
> while ober_write_elements uses that to determine the size of the buffer.
> ober_write_elements uses br_wptr to determine how much has been written.
> So use that pointer instead.
> 
> $ cat test.c
> #include 
> #include 
> #include 
> 
> int
> main(int argc, char *argv[])
> {
> struct ber_element *root;
> struct ber ber;
> void *buf;
> 
> bzero(&ber, sizeof(ber));
> 
> root = ober_printf_elements(NULL, "{}");
> printf("%zd\n", ober_write_elements(&ber, root));
> printf("%zd\n", ober_get_writebuf(&ber, &buf));
> 
> ober_free_elements(root);
> root = ober_printf_elements(NULL, "{d}", (int)1);
> printf("%zd\n", ober_write_elements(&ber, root));
> printf("%zd\n", ober_get_writebuf(&ber, &buf));
> 
> ober_free_elements(root);
> root = ober_printf_elements(NULL, "{}");
> printf("%zd\n", ober_write_elements(&ber, root));
> printf("%zd\n", ober_get_writebuf(&ber, &buf));
> }
> $ CFLAGS='-lutil' make test && ./test
> cc -lutil   -o test test.c 
> 2
> 2
> 5
> 5
> 2
> 5
> 
> OK?
> 
> martijn@
> 
> Index: ber.c
> ===
> RCS file: /cvs/src/lib/libutil/ber.c,v
> retrieving revision 1.23
> diff -u -p -r1.23 ber.c
> --- ber.c 21 Oct 2021 08:17:33 -  1.23
> +++ ber.c 20 Jan 2022 17:48:27 -
> @@ -831,7 +831,7 @@ ober_get_writebuf(struct ber *b, void **
>   if (b->br_wbuf == NULL)
>   return -1;
>   *buf = b->br_wbuf;
> - return (b->br_wend - b->br_wbuf);
> + return (b->br_wptr - b->br_wbuf);
>  }
>  
>  /*
> 



ober_get_writebuf return correct length

2022-01-20 Thread Martijn van Duren
While reading through ber.c I noticed that ober_get_writebuf can return
the wrong length when called multiple times on the same ber instance.

This is because ober_get_writebuf uses br_wend to calculate the length,
while ober_write_elements uses that to determine the size of the buffer.
ober_write_elements uses br_wptr to determine how much has been written.
So use that pointer instead.

$ cat test.c
#include 
#include 
#include 

int
main(int argc, char *argv[])
{
struct ber_element *root;
struct ber ber;
void *buf;

bzero(&ber, sizeof(ber));

root = ober_printf_elements(NULL, "{}");
printf("%zd\n", ober_write_elements(&ber, root));
printf("%zd\n", ober_get_writebuf(&ber, &buf));

ober_free_elements(root);
root = ober_printf_elements(NULL, "{d}", (int)1);
printf("%zd\n", ober_write_elements(&ber, root));
printf("%zd\n", ober_get_writebuf(&ber, &buf));

ober_free_elements(root);
root = ober_printf_elements(NULL, "{}");
printf("%zd\n", ober_write_elements(&ber, root));
printf("%zd\n", ober_get_writebuf(&ber, &buf));
}
$ CFLAGS='-lutil' make test && ./test
cc -lutil   -o test test.c 
2
2
5
5
2
5

OK?

martijn@

Index: ber.c
===
RCS file: /cvs/src/lib/libutil/ber.c,v
retrieving revision 1.23
diff -u -p -r1.23 ber.c
--- ber.c   21 Oct 2021 08:17:33 -  1.23
+++ ber.c   20 Jan 2022 17:48:27 -
@@ -831,7 +831,7 @@ ober_get_writebuf(struct ber *b, void **
if (b->br_wbuf == NULL)
return -1;
*buf = b->br_wbuf;
-   return (b->br_wend - b->br_wbuf);
+   return (b->br_wptr - b->br_wbuf);
 }
 
 /*



remove snmpe.c transactionid

2022-01-20 Thread Martijn van Duren
This was from a sequence of early attempts to work towards a new
application layer. I can give more reasoning behind it, but the bottom
line is that it's currently dead weight.

OK to remove this code again?

martijn@

Index: snmpd.h
===
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.102
diff -u -p -r1.102 snmpd.h
--- snmpd.h 19 Jan 2022 10:25:04 -  1.102
+++ snmpd.h 20 Jan 2022 13:38:28 -
@@ -403,8 +403,6 @@ struct snmp_message {
u_int8_t sm_data[READ_BUF_SIZE];
size_t   sm_datalen;
 
-   uint32_t sm_transactionid;
-
u_intsm_version;
 
/* V1, V2c */
@@ -441,11 +439,7 @@ struct snmp_message {
 
struct ber_element  *sm_varbind;
struct ber_element  *sm_varbindresp;
-
-   RB_ENTRY(snmp_message)   sm_entry;
 };
-RB_HEAD(snmp_messages, snmp_message);
-extern struct snmp_messages snmp_messages;
 
 /* Defined in SNMPv2-MIB.txt (RFC 3418) */
 struct snmp_stats {
@@ -644,8 +638,6 @@ void snmpe(struct privsep *, struct pr
 voidsnmpe_shutdown(void);
 voidsnmpe_dispatchmsg(struct snmp_message *);
 voidsnmpe_response(struct snmp_message *);
-int snmp_messagecmp(struct snmp_message *, struct snmp_message *);
-RB_PROTOTYPE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)
 
 /* trap.c */
 voidtrap_init(void);
Index: snmpe.c
===
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.82
diff -u -p -r1.82 snmpe.c
--- snmpe.c 19 Jan 2022 11:00:56 -  1.82
+++ snmpe.c 20 Jan 2022 13:38:28 -
@@ -58,8 +58,6 @@ intsnmpe_encode(struct snmp_message *)
 struct imsgev  *iev_parent;
 static const struct timevalsnmpe_tcp_timeout = { 10, 0 }; /* 10s */
 
-struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages);
-
 static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT }
 };
@@ -246,11 +244,6 @@ snmpe_parse(struct snmp_message *msg)
 
msg->sm_errstr = "invalid message";
 
-   do {
-   msg->sm_transactionid = arc4random();
-   } while (msg->sm_transactionid == 0 ||
-   RB_INSERT(snmp_messages, &snmp_messages, msg) != NULL);
-
if (ober_scanf_elements(root, "{ie", &ver, &a) != 0)
goto parsefail;
 
@@ -910,8 +903,6 @@ snmpe_response(struct snmp_message *msg)
 void
 snmp_msgfree(struct snmp_message *msg)
 {
-   if (msg->sm_transactionid != 0)
-   RB_REMOVE(snmp_messages, &snmp_messages, msg);
event_del(&msg->sm_sockev);
ober_free(&msg->sm_ber);
if (msg->sm_req != NULL)
@@ -974,12 +965,3 @@ snmpe_encode(struct snmp_message *msg)
 #endif
return 0;
 }
-
-int
-snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2)
-{
-   return (m1->sm_transactionid < m2->sm_transactionid ? -1 :
-   m1->sm_transactionid > m2->sm_transactionid);
-}
-
-RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)



snmpd(8): clean up variable printing

2022-01-19 Thread Martijn van Duren
The new code uses smi_print_element when debugging is enabled to trace
calls. Unfortunately the current smi_print_element lacks in quite a few
departments. This diff rewrites smi_print_element to be more concise
than what we currently have, without moving into the more complex
territory that snmp(1) has.

Unknown types are printed in a similar fashion to what tcpdump(8)'s
snmp output does.

This change helps mostly with exceptions (NOSUCH{OBJECT,INSTANCE} and
ENDOFMIBVIEW) and distinguishing between different integer types.

I kept the current implementation under smi_print_element_legacy, so
that we don't change the output of trap handlers. We should probably
revisit that one at some point, but I don't think to go into that
territory right now.

OK?

martijn@

p.s. I'm not particularly thrilled about the type hinting, but it's
the cleanest that I could come up with without being too much of an
eyesore or filling the screen up even further.

Index: smi.c
===
RCS file: /cvs/src/usr.sbin/snmpd/smi.c,v
retrieving revision 1.30
diff -u -p -r1.30 smi.c
--- smi.c   21 Oct 2021 15:08:15 -  1.30
+++ smi.c   19 Jan 2022 15:21:20 -
@@ -46,6 +46,7 @@
 
 #include "snmpd.h"
 #include "mib.h"
+#include "application.h"
 
 #define MINIMUM(a, b)  (((a) < (b)) ? (a) : (b))
 
@@ -461,8 +462,9 @@ smi_debug_elements(struct ber_element *r
 }
 #endif
 
+/* Keep around so trap handle scripts don't break */
 char *
-smi_print_element(struct ber_element *root)
+smi_print_element_legacy(struct ber_element *root)
 {
char*str = NULL, *buf, *p;
size_t   len, i;
@@ -520,6 +522,139 @@ smi_print_element(struct ber_element *ro
case BER_TYPE_SET:
default:
str = strdup("");
+   break;
+   }
+
+   return (str);
+
+ fail:
+   free(str);
+   return (NULL);
+}
+
+char *
+smi_print_element(struct ber_element *root)
+{
+   char*str = NULL, *buf, *p;
+   long longv;
+   struct ber_oid   o;
+   char strbuf[BUFSIZ];
+
+   switch (root->be_class) {
+   case BER_CLASS_UNIVERSAL:
+   switch (root->be_type) {
+   case BER_TYPE_INTEGER:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld", v) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OBJECT:
+   if (ober_get_oid(root, &o) == -1)
+   goto fail;
+   if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
+   sizeof(strbuf), 0)) == -1)
+   goto fail;
+   break;
+   case BER_TYPE_OCTETSTRING:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   p = reallocarray(NULL, 4, root->be_len + 1);
+   if (p == NULL)
+   goto fail;
+   strvisx(p, buf, root->be_len, VIS_NL);
+   if (asprintf(&str, "\"%s\"", p) == -1) {
+   free(p);
+   goto fail;
+   }
+   free(p);
+   break;
+   case BER_TYPE_NULL:
+   if (asprintf(&str, "null") == -1)
+   goto fail;
+   default:
+   /* Should not happen in a valid SNMP packet */
+   if (asprintf(&str, "[U/%u]", root->be_type) == -1)
+   goto fail;
+   break;
+   }
+   break;
+   case BER_CLASS_APPLICATION:
+   switch (root->be_type) {
+   case SNMP_T_IPADDR:
+   if (ober_get_string(root, &buf) == -1)
+   goto fail;
+   if (asprintf(&str, "%s",
+   inet_ntoa(*(struct in_addr *)buf)) == -1)
+   goto fail;
+   break;
+   case SNMP_T_COUNTER32:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld(c32)", v) == -1)
+   goto fail;
+   break;
+   case SNMP_T_GAUGE32:
+   if (ober_get_integer(root, &v) == -1)
+   goto fail;
+   if (asprintf(&str, "%lld(g32)", v) == -1)
+   goto fail;
+   break;
+   case SNMP_T_TIMETICKS:
+   if (ober_get_integer(root, &v) == -1)
+   

Re: sed(1): enable regression tests and correct pattern space assumptions

2022-01-10 Thread Martijn van Duren
On Mon, 2022-01-10 at 08:21 -0700, Todd C. Miller wrote:
> On Mon, 10 Jan 2022 15:23:42 +0100, Martijn van Duren wrote:
> 
> > The lputs case is fairly straight forward and I'd like to get an OK
> > for that part.
> 
> I agree that fixing lputs() to honor psl is the best approach.
> Wouldn't the diff be simpler if you left the handling of 's' as-is
> but make the loop invariant l > 0 and decrement l accordingly?
> Using len instead of l is probably a little easier to read.
> 
>  - todd

sure

Index: process.c
===
RCS file: /cvs/src/usr.bin/sed/process.c,v
retrieving revision 1.34
diff -u -p -r1.34 process.c
--- process.c   14 Nov 2018 10:59:33 -  1.34
+++ process.c   10 Jan 2022 15:52:26 -
@@ -60,7 +60,7 @@ static SPACE HS, PS, SS;
 
 static inline int   applies(struct s_command *);
 static void flush_appends(void);
-static void lputs(char *);
+static void lputs(char *, size_t);
 static inline int   regexec_e(regex_t *, const char *, int, int, size_t,
 size_t);
 static void regsub(SPACE *, char *, char *);
@@ -158,7 +158,7 @@ redirect:
(void)fprintf(outfile, "%s", cp->t);
break;
case 'l':
-   lputs(ps);
+   lputs(ps, psl);
break;
case 'n':
if (!nflag && !pd)
@@ -478,14 +478,14 @@ flush_appends(void)
 }
 
 static void
-lputs(char *s)
+lputs(char *s, size_t len)
 {
int count;
extern int termwidth;
const char *escapes;
char *p;
 
-   for (count = 0; *s; ++s) {
+   for (count = 0; len > 0; len--, s++) {
if (count >= termwidth) {
(void)fprintf(outfile, "\\\n");
count = 0;
@@ -501,7 +501,7 @@ lputs(char *s)
} else {
escapes = "\\\a\b\f\r\t\v";
(void)fputc('\\', outfile);
-   if ((p = strchr(escapes, *s))) {
+   if ((p = strchr(escapes, *s)) && *s != '\0') {
(void)fputc("\\abfrtv"[p - escapes], outfile);
count += 2;
} else {



Re: sed(1): enable regression tests and correct pattern space assumptions

2022-01-10 Thread Martijn van Duren
On Mon, 2022-01-10 at 00:25 -0600, user wrote:
> The commandD1 regression test can be enabled without modifying sed's source 
> code, it is no longer failing. The commandl1, commandl2, and commandc1 tests 
> fail because the 'c' and 'D' commands assume that setting the correct length 
> of the pattern space is sufficient and do not correct the pattern space. 
> 
> The pattern space is cleared in the 'c' command by setting ps to a null byte 
> and a null byte is copied to the pattern space in the 'D' command to prevent 
> stray bytes from being printed by other commands. Specifically, the 
> substitute function in pattern.c:408 calls cspace(&SS, s++, 1, APPEND) if s 
> doesn't point to a null byte. If the pattern space isn't cleared by the 'c' 
> command this will be executed and cause the substitute space to be advanced 
> by 1 byte and causes an incorrect output (commandc1 test). If the pattern 
> space isn't cleared in the 'c' command lputs (called by the l command) will 
> also print the pattern space without checking the pattern space length 
> (commandl2 test), giving an incorrect output. In the 'D' command, the 
> original memmove did not copy the null byte at the end of the bytes that were 
> moved, causing a lputs call following the command to give an incorrect output 
> (commandl1 test).
> 
> Not sure if the diff is correct since it was generated using got diff.

Thanks for the detailed analysis. However, I think we should fix the
cases where SPACE.len is not honoured...

The lputs case is fairly straight forward and I'd like to get an OK
for that part.

The substitute case I haven't convinced myself just yet that it's
correct. But (all) regress passes and I want to put it out here in
case someone with more spare brain cycles than me wants to dive into it.

martijn@

Index: process.c
===
RCS file: /cvs/src/usr.bin/sed/process.c,v
retrieving revision 1.34
diff -u -p -r1.34 process.c
--- process.c   14 Nov 2018 10:59:33 -  1.34
+++ process.c   10 Jan 2022 14:20:56 -
@@ -60,7 +60,7 @@ static SPACE HS, PS, SS;
 
 static inline int   applies(struct s_command *);
 static void flush_appends(void);
-static void lputs(char *);
+static void lputs(char *, size_t);
 static inline int   regexec_e(regex_t *, const char *, int, int, size_t,
 size_t);
 static void regsub(SPACE *, char *, char *);
@@ -158,7 +158,7 @@ redirect:
(void)fprintf(outfile, "%s", cp->t);
break;
case 'l':
-   lputs(ps);
+   lputs(ps, psl);
break;
case 'n':
if (!nflag && !pd)
@@ -390,11 +390,11 @@ substitute(struct s_command *cp)
 * and at the end of the line, terminate.
 */
if (match[0].rm_so == match[0].rm_eo) {
-   if (*s == '\0' || *s == '\n')
+   if (slen == 0 || *s == '\0' || *s == '\n')
slen = -1;
else
slen--;
-   if (*s != '\0') {
+   if (slen >= 0 && *s != '\0') {
cspace(&SS, s++, 1, APPEND);
le++;
}
@@ -478,34 +478,35 @@ flush_appends(void)
 }
 
 static void
-lputs(char *s)
+lputs(char *s, size_t l)
 {
int count;
extern int termwidth;
const char *escapes;
+   size_t i;
char *p;
 
-   for (count = 0; *s; ++s) {
+   for (i = 0, count = 0; i < l; i++) {
if (count >= termwidth) {
(void)fprintf(outfile, "\\\n");
count = 0;
}
-   if (isascii((unsigned char)*s) && isprint((unsigned char)*s)
-   && *s != '\\') {
-   (void)fputc(*s, outfile);
+   if (isascii((unsigned char)s[i]) && isprint((unsigned char)s[i])
+   && s[i] != '\\') {
+   (void)fputc(s[i], outfile);
count++;
-   } else if (*s == '\n') {
+   } else if (s[i] == '\n') {
(void)fputc('$', outfile);
(void)fputc('\n', outfile);
count = 0;
} else {
escapes = "\\\a\b\f\r\t\v";
(void)fputc('\\', outfile);
-   if ((p = strchr(escapes, *s))) {
+   if ((p = strchr(escapes, s[i])) && s[i] != '\0') {
(void)fputc("\\abfrtv"[p - escapes], outfile);
count += 2;
} else {
- 

Re: rev(1): pull MB_CUR_MAX out of the hot loop

2022-01-08 Thread Martijn van Duren
I fully agree with your reasoning and also prefer this one over the
previous two diff.

OK martijn@

On Sun, 2022-01-09 at 00:45 +0100, Ingo Schwarze wrote:
> Hi,
> 
> Martijn van Duren wrote on Sat, Jan 08, 2022 at 08:30:20AM +0100:
> 
> > Why not go for the following diff?
> > It has a comparable speed increase, but without the added complexity
> > of a second inner loop.
> 
> Actually, i like the idea of not duplicating the loop, in cases where
> that is possible without a noticeable performance cost.
> 
> I admit i OK'ed the original UTF-8 diff almost six years ago,
> but looking at the code again, i now dislike how it is testing
> every byte twice for no apparent reason.  Even worse, i regard
> the lack of I/O error checking on write operations as a bug.
> Note that it does make sense to proceed to the next file on *read*
> errors, whereas a *write* error should better be fatal.
> 
> For those reason, and because i prefer versions of isu8cont()
> that do not inspect the locale, i propose the following patch
> instead.
> 
> As an aside, i tried using fwrite(3) instead of the manual
> putchar(*u) loop, but that is almost exactly as slow as repeatedly
> calling MB_CUR_MAX, probably due to either locking or orientation
> setting or some aspect of iov handling or buffering or more than
> one of those; i refrained from profiling it.
> 
>  - 8< - schnipp - >8 - 8< - schnapp - >8 -
> 
> Index: rev.c
> ===
> RCS file: /cvs/src/usr.bin/rev/rev.c,v
> retrieving revision 1.13
> diff -u -p -r1.13 rev.c
> --- rev.c 10 Apr 2016 17:06:52 -  1.13
> +++ rev.c 8 Jan 2022 23:19:46 -
> @@ -46,13 +46,14 @@ void usage(void);
>  int
>  main(int argc, char *argv[])
>  {
> - char *filename, *p = NULL, *t, *u;
> + char *filename, *p = NULL, *t, *te, *u;
>   FILE *fp;
>   ssize_t len;
>   size_t ps = 0;
> - int ch, rval;
> + int ch, multibyte, rval;
>  
>   setlocale(LC_CTYPE, "");
> + multibyte = MB_CUR_MAX > 1;
>  
>   if (pledge("stdio rpath", NULL) == -1)
>   err(1, "pledge");
> @@ -83,14 +84,16 @@ main(int argc, char *argv[])
>   if (p[len - 1] == '\n')
>   --len;
>   for (t = p + len - 1; t >= p; --t) {
> - if (isu8cont(*t))
> - continue;
> - u = t;
> - do {
> - putchar(*u);
> - } while (isu8cont(*(++u)));
> + te = t;
> + if (multibyte)
> + while (t > p && isu8cont(*t))
> + --t;
> + for (u = t; u <= te; ++u)
> + if (putchar(*u) == EOF)
> + err(1, "stdout");
>   }
> - putchar('\n');
> + if (putchar('\n') == EOF)
> + err(1, "stdout");
>   }
>   if (ferror(fp)) {
>   warn("%s", filename);
> @@ -104,7 +107,7 @@ main(int argc, char *argv[])
>  int
>  isu8cont(unsigned char c)
>  {
> - return MB_CUR_MAX > 1 && (c & (0x80 | 0x40)) == 0x80;
> + return (c & (0x80 | 0x40)) == 0x80;
>  }
>  
>  void
> 
>  - 8< - schnipp - >8 - 8< - schnapp - >8 -
> 
> The rest of this mail contains test reports.
> These test reports use two test programs:
> 
>  - 8< - schnipp - >8 - 8< - schnapp - >8 -
> 
> /* makeutf8.c */
> 
> #include 
> #include 
> #include 
> #include 
> #include 
> 
> int
> main(void)
> {
>   FILE*in, *out;
>   char*line = NULL;
>   size_t   linesize = 0;
>   unsigned int wc, wcl = 0;
> 
>   if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
>   err(1, "setlocale");
> 
>   if ((in = fopen("/usr/src/gnu/usr.bin/perl/lib/unicore/"
>   "UnicodeData.txt", "r")) == NULL)
>   err(1, "in");
>   if ((out = fopen("utf8.txt", "w")) == NULL)
>   err(1, "out");
> 
>   whil

Re: rev(1): pull MB_CUR_MAX out of the hot loop

2022-01-07 Thread Martijn van Duren
On Fri, 2022-01-07 at 15:00 -0600, Scott Cheloha wrote:
> On Fri, Jan 07, 2022 at 01:43:24PM -0600, Scott Cheloha wrote:
> > 
> > [...]
> > 
> > Like this?
> > 
> > [...]
> 
> Updated: make the for-loop update expressions match.
> 
Why not go for the following diff?
It has a comparable speed increase, but without the added complexity of
a second inner loop.

Either diff is fine by me though, so if you want to go ahead with your
diff: OK martijn@

$ export LC_ALL=C
$ for i in $(jot 10); do time rev /usr/share/dict/words > /dev/null; done
0m00.17s real 0m00.16s user 0m00.02s system
0m00.17s real 0m00.17s user 0m00.01s system
0m00.17s real 0m00.17s user 0m00.01s system
0m00.17s real 0m00.18s user 0m00.00s system
0m00.17s real 0m00.17s user 0m00.01s system
0m00.20s real 0m00.16s user 0m00.03s system
0m00.23s real 0m00.16s user 0m00.01s system
0m00.17s real 0m00.18s user 0m00.00s system
0m00.17s real 0m00.21s user 0m00.00s system
0m00.17s real 0m00.16s user 0m00.01s system
$ for i in $(jot 10); do time ./obj/rev.scott /usr/share/dict/words > 
/dev/null; done
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.02s system
0m00.06s real 0m00.04s user 0m00.02s system
0m00.05s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.05s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.03s user 0m00.01s system
0m00.04s real 0m00.05s user 0m00.00s system
$ for i in $(jot 10); do time ./obj/rev.martijn /usr/share/dict/words > 
/dev/null; done
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.03s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.06s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.05s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.01s system
$ export LC_ALL=en_US.UTF-8
$ for i in $(jot 10); do time rev /usr/share/dict/words > /dev/null; done
0m00.17s real 0m00.17s user 0m00.00s system
0m00.17s real 0m00.18s user 0m00.00s system
0m00.17s real 0m00.17s user 0m00.00s system
0m00.17s real 0m00.18s user 0m00.01s system
0m00.17s real 0m00.17s user 0m00.01s system
0m00.17s real 0m00.18s user 0m00.00s system
0m00.17s real 0m00.17s user 0m00.00s system
0m00.17s real 0m00.17s user 0m00.02s system
0m00.17s real 0m00.17s user 0m00.01s system
0m00.17s real 0m00.18s user 0m00.00s system
$ for i in $(jot 10); do time ./obj/rev.scott /usr/share/dict/words > 
/dev/null; done
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.05s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.00s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.01s system
0m00.04s real 0m00.04s user 0m00.00s system
$ for i in $(jot 10); do time ./obj/rev.martijn /usr/share/dict/words > 
/dev/null; done
0m00.04s real 0m00.04s user 0m00.01s system
0m00.05s real 0m00.04s user 0m00.00s system
0m00.05s real 0m00.05s user 0m00.00s system
0m00.05s real 0m00.03s user 0m00.02s system
0m00.05s real 0m00.05s user 0m00.00s system
0m00.05s real 0m00.05s user 0m00.00s system
0m00.05s real 0m00.05s user 0m00.00s system
0m00.05s real 0m00.04s user 0m00.01s system
0m00.05s real 0m00.04s user 0m00.01s system
0m00.05s real 0m00.05s user 0m00.00s system

martijn@

ps. I don't have your fancy nanotime. Where can I find that?

Index: rev.c
===
RCS file: /cvs/src/usr.bin/rev/rev.c,v
retrieving revision 1.13
diff -u -p -r1.13 rev.c
--- rev.c   10 Apr 2016 17:06:52 -  1.13
+++ rev.c   8 Jan 2022 07:09:19 -
@@ -43,6 +43,8 @@
 int isu8cont(unsigned char);
 void usage(void);
 
+int multibyte;
+
 int
 main(int argc, char *argv[])
 {
@@ -53,6 +55,7 @@ main(int argc, char *argv[])
int ch, rval;
 
setlocale(LC_CTYPE, "");
+   multibyte = MB_CUR_MAX > 1;
 
if (pledge("stdio rpath", NULL

Re: snmpd(8): New application layer - step towards agentx support

2022-01-05 Thread Martijn van Duren
Problem found: The code was compiled on -stable, which I apparently
misread. There's changes in libutil in current that this diff needs.

Pending Joel's results: Anyone else wanting to chime in?

On Mon, 2022-01-03 at 15:09 +0100, Joel Carnat wrote:
> Hello,
> 
> I have just patched my snmpd from -current ; everything else is 
> 7.0-stable. I'm not sure what happens but I use the same snmpd.conf and 
> connects to snmpd from another machine using
> 
> # snmpwalk -v 3 -a SHA -A "changeme" -l authPriv -u telegraf \
> -x AES -X "changeme" server
> 
> But using the patched snmpd, I get the following error:
> mib_2 = No Such Object available on this agent at this OID. Using the 
> 7.0 version, it works perfectly.
> 
> I can send full snmpd logs if you think it's usefull.
> 
> Regards,
> Joel C.
> 
> On 1/3/22 13:57, Martijn van Duren wrote:
> > On Sun, 2021-11-21 at 14:58 +0100, Martijn van Duren wrote:
> > > On Sun, 2021-11-14 at 14:35 +, Stuart Henderson wrote:
> > > > On 2021/11/14 11:49, Martijn van Duren wrote:
> > > > > sthen@ found an issue when using this diff with netsnmp tools.
> > > > > 
> > > > > The problem was that I put the requestID in the msgID, resulting
> > > > > in a mismatch upon receiving the reply. The reason that snmp(1)
> > > > > works is because msgID and requestID are the same.
> > > > > Diff below fixes things.
> > > > 
> > > > This version works for me, and the runtime increase with librenms
> > > > fetches and polls (which use a mixture of get/bulkwalk) is acceptable
> > > > (10% or so).
> > > > 
> > > Anyone else put this through a test? I want to move forward with this.
> > > 
> > > martijn@
> > > 
> > 2 month ping.
> > So far I only have gotten test results from sthen@.
> > Should I just put this in or is someone planning to actually look into
> > the code?
> > 
> > martijn@



Re: snmpd(8): New application layer - step towards agentx support

2022-01-03 Thread Martijn van Duren
On Mon, 2022-01-03 at 15:09 +0100, Joel Carnat wrote:
> Hello,
> 
> I have just patched my snmpd from -current ; everything else is 
> 7.0-stable. I'm not sure what happens but I use the same snmpd.conf and 
> connects to snmpd from another machine using
> 
> # snmpwalk -v 3 -a SHA -A "changeme" -l authPriv -u telegraf \
> -x AES -X "changeme" server
> 
> But using the patched snmpd, I get the following error:
> mib_2 = No Such Object available on this agent at this OID. Using the 
> 7.0 version, it works perfectly.
> 
> I can send full snmpd logs if you think it's usefull.

Thanks for testing, unfortunately I can't reproduce your error:
martijn$ snmpwalk -v3 -a SHA -A test1234 -l authpriv -u testsha1 -x AES -X 
test1234 127.0.0.1 | head
SNMPv2-MIB::sysDescr.0 = STRING: OpenBSD martijn.office.cloudvps.nl 7.0 
GENERIC.MP#213 amd64
SNMPv2-MIB::sysObjectID.0 = OID: OPENBSD-BASE-MIB::openBSDDefaultObjectID
SNMPv2-MIB::sysUpTime.0 = Timeticks: (344136) 0:57:21.36
SNMPv2-MIB::sysContact.0 = STRING: Martijn van Duren
SNMPv2-MIB::sysName.0 = STRING: martijn
SNMPv2-MIB::sysLocation.0 = STRING: 
SNMPv2-MIB::sysORLastChange.0 = Timeticks: (0) 0:00:00.00
SNMPv2-MIB::sysORIndex.1 = INTEGER: 1
SNMPv2-MIB::sysORIndex.2 = INTEGER: 2
SNMPv2-MIB::sysORIndex.3 = INTEGER: 3

Also, your report doesn't make sense to me: snmpwalk is from net-snmp,
while the name mib_2 is used (incorrectly) by snmp(1). And "No Such
Object" should not happen on getnext requests (which is what a walk
does): Something must have gone really weird to end up in such an
error path.

Could you please provide a minimal proof of concept snmpd.conf with
full client command and output to allow me to reproduce this?

> 
> Regards,
> Joel C.
> 
> On 1/3/22 13:57, Martijn van Duren wrote:
> > On Sun, 2021-11-21 at 14:58 +0100, Martijn van Duren wrote:
> > > On Sun, 2021-11-14 at 14:35 +, Stuart Henderson wrote:
> > > > On 2021/11/14 11:49, Martijn van Duren wrote:
> > > > > sthen@ found an issue when using this diff with netsnmp tools.
> > > > > 
> > > > > The problem was that I put the requestID in the msgID, resulting
> > > > > in a mismatch upon receiving the reply. The reason that snmp(1)
> > > > > works is because msgID and requestID are the same.
> > > > > Diff below fixes things.
> > > > 
> > > > This version works for me, and the runtime increase with librenms
> > > > fetches and polls (which use a mixture of get/bulkwalk) is acceptable
> > > > (10% or so).
> > > > 
> > > Anyone else put this through a test? I want to move forward with this.
> > > 
> > > martijn@
> > > 
> > 2 month ping.
> > So far I only have gotten test results from sthen@.
> > Should I just put this in or is someone planning to actually look into
> > the code?
> > 
> > martijn@



Re: ldap search vs ldapsearch

2022-01-03 Thread Martijn van Duren
On Mon, 2021-11-08 at 09:51 +0100, Martijn van Duren wrote:
> On Sat, 2021-11-06 at 03:11 -0400, Allan Streib wrote:
> > On OpenBSD 7.0-release, comparing the output of OpenLDAP's
> > ldapsearch(1) to ldap(1) search, the ldap(1) search output is
> > missing the last attribute of each directory entry.
> > 
> > e.g. from a directory I am working on at work:
> > 
> > $ ldapsearch -LLL -x -H ldapi://%2fvar%2frun%2fldapi -b 
> > dc=ise,dc=luddy,dc=indiana,dc=edu '(objectClass=organizationalUnit)'
> > dn: ou=people,dc=ise,dc=luddy,dc=indiana,dc=edu
> > objectClass: organizationalUnit
> > ou: people
> > description: ISE Systems Users
> > 
> > dn: ou=groups,dc=ise,dc=luddy,dc=indiana,dc=edu
> > objectClass: organizationalUnit
> > ou: groups
> > description: ISE Systems Groups
> > 
> > 
> > Note that these are missing the "description" attribute:
> > 
> > $ ldap search -H ldapi://%2fvar%2frun%2fldapi -b 
> > dc=ise,dc=luddy,dc=indiana,dc=edu '(objectClass=organizationalUnit)'
> > dn: ou=people,dc=ise,dc=luddy,dc=indiana,dc=edu
> > objectClass: organizationalUnit
> > ou: people
> > 
> > dn: ou=groups,dc=ise,dc=luddy,dc=indiana,dc=edu
> > objectClass: organizationalUnit
> > ou: groups
> > 
> > Allan
> > 
> Thanks for the detailed report.
> This edgecase got overlooked when moving to a stricter ASN.1 parsing for
> ober_scanf_elements, which resulted in a premature exit of the loop.
> Diff below should fix it.
> 
> This diff also applies to libexec/login_ldap and usr.sbin/ypldap.
> 
> OK?
> 
> martijn@

Almost forgot about this one.

Index: aldap.c
===
RCS file: /cvs/src/usr.bin/ldap/aldap.c,v
retrieving revision 1.9
diff -u -p -r1.9 aldap.c
--- aldap.c 24 Oct 2019 12:39:26 -  1.9
+++ aldap.c 8 Nov 2021 08:50:12 -
@@ -580,15 +580,15 @@ int
 aldap_first_attr(struct aldap_message *msg, char **outkey,
 struct aldap_stringset **outvalues)
 {
-   struct ber_element *b, *c;
+   struct ber_element *b;
char *key;
struct aldap_stringset *ret;
 
if (msg->body.search.attrs == NULL)
goto fail;
 
-   if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}e",
-   &key, &b, &c) != 0)
+   if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}",
+   &key, &b) != 0)
goto fail;
 
msg->body.search.iter = msg->body.search.attrs->be_next;
@@ -610,7 +610,7 @@ int
 aldap_next_attr(struct aldap_message *msg, char **outkey,
 struct aldap_stringset **outvalues)
 {
-   struct ber_element *a, *b;
+   struct ber_element *a;
char *key;
struct aldap_stringset *ret;
 
@@ -622,8 +622,7 @@ aldap_next_attr(struct aldap_message *ms
if (ober_get_eoc(msg->body.search.iter) == 0)
goto notfound;
 
-   if (ober_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b)
-   != 0)
+   if (ober_scanf_elements(msg->body.search.iter, "{s(e)}", &key, &a) != 0)
goto fail;
 
msg->body.search.iter = msg->body.search.iter->be_next;



  1   2   3   4   5   6   7   >