Index: head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt =================================================================== --- head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 328159) +++ head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 328160) @@ -1,570 +1,579 @@ -- -- Copyright (c) 2001-2003 -- Fraunhofer Institute for Open Communication Systems (FhG Fokus). -- All rights reserved. -- -- Author: Harti Brandt -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $Begemot: bsnmp/snmpd/BEGEMOT-SNMPD.txt,v 1.23 2004/08/06 08:47:08 brandt Exp $ -- -- Begemot Private SNMPd MIB. -- BEGEMOT-SNMPD-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32, Unsigned32, IpAddress FROM SNMPv2-SMI TEXTUAL-CONVENTION, TruthValue, RowStatus FROM SNMPv2-TC MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF begemot FROM BEGEMOT-MIB; begemotSnmpd MODULE-IDENTITY - LAST-UPDATED "200212040000Z" + LAST-UPDATED "201801190000Z" ORGANIZATION "Fraunhofer FOKUS, CATS" CONTACT-INFO " Hartmut Brandt Postal: Fraunhofer Institute for Open Communication Systems Kaiserin-Augusta-Allee 31 10589 Berlin Germany Fax: +49 30 3463 7352 E-mail: harti@freebsd.org" DESCRIPTION "The MIB module for the Begemot SNMP daemon." ::= { begemot 1 } begemotSnmpdObjects OBJECT IDENTIFIER ::= { begemotSnmpd 1 } begemotSnmpdDefs OBJECT IDENTIFIER ::= { begemotSnmpd 2 } -- -------------------------------------------------------------------------- SectionName ::= TEXTUAL-CONVENTION DISPLAY-HINT "14a" STATUS current DESCRIPTION "Name of a loadable module. Should consist of alphanumeric characers only, the first character must be a letter." SYNTAX OCTET STRING (SIZE(1..14)) -- -------------------------------------------------------------------------- -- -- Agent types -- begemotSnmpdAgent OBJECT IDENTIFIER ::= { begemotSnmpdDefs 1 } begemotSnmpdAgentFreeBSD OBJECT-IDENTITY STATUS current DESCRIPTION "Identifies the agent as running on FreeBSD." ::= { begemotSnmpdAgent 1 } -- -------------------------------------------------------------------------- -- -- The Config Group -- begemotSnmpdConfig OBJECT IDENTIFIER ::= { begemotSnmpdObjects 1 } begemotSnmpdTransmitBuffer OBJECT-TYPE SYNTAX INTEGER (484..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The size of the receive buffer in bytes. Larger messages are dropped by SNMPd." DEFVAL { 2048 } ::= { begemotSnmpdConfig 1 } begemotSnmpdReceiveBuffer OBJECT-TYPE SYNTAX INTEGER (484..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The size of the transmit buffer in bytes. Larger messages cannot be sent by the SNMPd." DEFVAL { 2048 } ::= { begemotSnmpdConfig 2 } begemotSnmpdCommunityDisable OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "Disables all access to the CommunityTable from SNMP. Once set it cannot be cleared." DEFVAL { false } ::= { begemotSnmpdConfig 3 } begemotSnmpdTrap1Addr OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-write STATUS current DESCRIPTION "The trap sink for v1 traps." ::= { begemotSnmpdConfig 4 } begemotSnmpdVersionEnable OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "The SNMP versions that the agent processes. The following bits are defined: 0x00000001 - SNMPv1 0x00000002 - SNMPv2c 0x00000004 - SNMPv3" DEFVAL { 3 } ::= { begemotSnmpdConfig 5 } -- -- Trap destinations -- begemotTrapSinkTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotTrapSinkEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with destinations for standard traps." INDEX { begemotTrapSinkAddr, begemotTrapSinkPort } ::= { begemotSnmpdObjects 2 } begemotTrapSinkEntry OBJECT-TYPE SYNTAX BegemotTrapSinkEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Entry describes one trap destination." INDEX { begemotTrapSinkAddr, begemotTrapSinkPort } ::= { begemotTrapSinkTable 1 } BegemotTrapSinkEntry ::= SEQUENCE { begemotTrapSinkAddr IpAddress, begemotTrapSinkPort INTEGER, begemotTrapSinkStatus RowStatus } begemotTrapSinkAddr OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS not-accessible STATUS current DESCRIPTION "Destination IP address of the manager station where to send traps." ::= { begemotTrapSinkEntry 1 } begemotTrapSinkPort OBJECT-TYPE SYNTAX INTEGER (1..65535) MAX-ACCESS not-accessible STATUS current DESCRIPTION "Destination UDP port of the manager station where to send traps." ::= { begemotTrapSinkEntry 2 } begemotTrapSinkStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "Used to create/activate/destroy the entry." ::= { begemotTrapSinkEntry 3 } -- -- SNMP port table -- begemotSnmpdPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with descriptions of UDP ports to listen on for SNMP messages." ::= { begemotSnmpdObjects 4 } begemotSnmpdPortEntry OBJECT-TYPE SYNTAX BegemotSnmpdPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with descriptions of UDP ports to listen on for SNMP messages." INDEX { begemotSnmpdPortAddress, begemotSnmpdPortPort } ::= { begemotSnmpdPortTable 1 } BegemotSnmpdPortEntry ::= SEQUENCE { begemotSnmpdPortAddress IpAddress, begemotSnmpdPortPort INTEGER, begemotSnmpdPortStatus INTEGER } begemotSnmpdPortAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS not-accessible STATUS current DESCRIPTION "The IP address to bind to." ::= { begemotSnmpdPortEntry 1 } begemotSnmpdPortPort OBJECT-TYPE SYNTAX INTEGER (1..65535) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The UDP port to listen on for SNMP messages." ::= { begemotSnmpdPortEntry 2 } begemotSnmpdPortStatus OBJECT-TYPE SYNTAX INTEGER { valid(1), invalid(2) } MAX-ACCESS read-create STATUS current DESCRIPTION "Set status to 1 to create entry, set it to 2 to delete it." ::= { begemotSnmpdPortEntry 3 } --- --- Community table --- begemotSnmpdCommunityTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdCommunityEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with the community strings for access control." ::= { begemotSnmpdObjects 5 } begemotSnmpdCommunityEntry OBJECT-TYPE SYNTAX BegemotSnmpdCommunityEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with the community strings for access control. When begemotSnmpdCommDisable is true, this table disappears." INDEX { begemotSnmpdCommunityModule, begemotSnmpdCommunityIndex } ::= { begemotSnmpdCommunityTable 1 } BegemotSnmpdCommunityEntry ::= SEQUENCE { begemotSnmpdCommunityModule SectionName, begemotSnmpdCommunityIndex Unsigned32, begemotSnmpdCommunityString OCTET STRING, - begemotSnmpdCommunityDescr OCTET STRING + begemotSnmpdCommunityDescr OCTET STRING, + begemotSnmpdCommunityPermission INTEGER } begemotSnmpdCommunityModule OBJECT-TYPE SYNTAX SectionName MAX-ACCESS not-accessible STATUS current DESCRIPTION "Index of the module that has registered this community. For global communities this is the empty string." ::= { begemotSnmpdCommunityEntry 1 } begemotSnmpdCommunityIndex OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The numerical index of the community (private to the module)." ::= { begemotSnmpdCommunityEntry 2 } begemotSnmpdCommunityString OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-write STATUS current DESCRIPTION "The string for access to SNMPd." ::= { begemotSnmpdCommunityEntry 3 } begemotSnmpdCommunityDescr OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "A description what this community is good for." ::= { begemotSnmpdCommunityEntry 4 } + +begemotSnmpdCommunityPermission OBJECT-TYPE + SYNTAX INTEGER (1..4294967295) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The numerical value of access rights granted to the community." + ::= { begemotSnmpdCommunityEntry 5 } -- -- Module table -- begemotSnmpdModuleTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdModuleEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table describing all the currently loaded dynamic modules. Writing to this table loads and unloads modules." ::= { begemotSnmpdObjects 6 } begemotSnmpdModuleEntry OBJECT-TYPE SYNTAX BegemotSnmpdModuleEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table entry describing a loadable module." INDEX { begemotSnmpdModuleSection } ::= { begemotSnmpdModuleTable 1 } BegemotSnmpdModuleEntry ::= SEQUENCE { begemotSnmpdModuleSection SectionName, begemotSnmpdModulePath OCTET STRING, begemotSnmpdModuleComment OCTET STRING } begemotSnmpdModuleSection OBJECT-TYPE SYNTAX SectionName MAX-ACCESS not-accessible STATUS current DESCRIPTION "The string used for matching configuration file sections and indexes the module table." ::= { begemotSnmpdModuleEntry 1 } begemotSnmpdModulePath OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-create STATUS current DESCRIPTION "The path name of the module. Set to empty string to unload a module. The path of an existing module may not be changed." ::= { begemotSnmpdModuleEntry 2 } begemotSnmpdModuleComment OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "A comment describing this module." ::= { begemotSnmpdModuleEntry 3 } -- -------------------------------------------------------------------------- -- -- The STATISTICS Group -- begemotSnmpdStats OBJECT IDENTIFIER ::= { begemotSnmpdObjects 7 } begemotSnmpdStatsNoRxBufs OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of times a receive buffer could not be allocated for a packet." ::= { begemotSnmpdStats 1 } begemotSnmpdStatsNoTxBufs OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of times a transmit buffer could not be allocated for a packet." ::= { begemotSnmpdStats 2 } begemotSnmpdStatsInTooLongPkts OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets received that were longer than the receive buffer. These packets are dropped." ::= { begemotSnmpdStats 3 } begemotSnmpdStatsInBadPduTypes OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets received with a bad type field." ::= { begemotSnmpdStats 4 } -- -- The Debug Group -- begemotSnmpdDebug OBJECT IDENTIFIER ::= { begemotSnmpdObjects 8 } begemotSnmpdDebugDumpPdus OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "Dump PDUs to log file if true." DEFVAL { false } ::= { begemotSnmpdDebug 1 } begemotSnmpdDebugSnmpTrace OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-write STATUS current DESCRIPTION "Tracing flags for the SNMP library. These flags have the following meaning: 0x00000001 trace GET operator 0x00000002 trace GETNEXT operator 0x00000004 trace SET operator 0x00000008 trace dependency processing 0x00000010 trace node finding 0x10000000 log ASN1 errors 0x20000000 log SNMP errors Individual values can be or-ed together." DEFVAL { 0 } ::= { begemotSnmpdDebug 2 } begemotSnmpdDebugSyslogPri OBJECT-TYPE SYNTAX INTEGER (0..8) MAX-ACCESS read-write STATUS current DESCRIPTION "Events with this or higher priority should not be logged." DEFVAL { 7 } -- don't log debug messages ::= { begemotSnmpdDebug 3 } -- -- Local port table -- begemotSnmpdLocalPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdLocalPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with descriptions of local (unix domain) ports to listen on for SNMP messages." ::= { begemotSnmpdObjects 9 } begemotSnmpdLocalPortEntry OBJECT-TYPE SYNTAX BegemotSnmpdLocalPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with descriptions of local ports to listen on for SNMP messages." INDEX { begemotSnmpdLocalPortPath } ::= { begemotSnmpdLocalPortTable 1 } BegemotSnmpdLocalPortEntry ::= SEQUENCE { begemotSnmpdLocalPortPath OCTET STRING, begemotSnmpdLocalPortStatus INTEGER, begemotSnmpdLocalPortType INTEGER } begemotSnmpdLocalPortPath OBJECT-TYPE SYNTAX OCTET STRING (SIZE(1..104)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The path name to create and listen on." ::= { begemotSnmpdLocalPortEntry 1 } begemotSnmpdLocalPortStatus OBJECT-TYPE SYNTAX INTEGER { valid(1), invalid(2) } MAX-ACCESS read-create STATUS current DESCRIPTION "Set status to 1 to create entry, set it to 2 to delete it." ::= { begemotSnmpdLocalPortEntry 2 } begemotSnmpdLocalPortType OBJECT-TYPE SYNTAX INTEGER { dgram-unpriv(1), dgram-priv(2), stream-unpriv(3), stream-priv(4) } MAX-ACCESS read-create STATUS current DESCRIPTION "Type of the port. If the type is unpriv SET operations are allowed from all clients if the community matches. For priv SET operations are allowed only from peers with uid zero. If the daemon cannot determine the peer uid it disallows the SET operation for -priv ports." ::= { begemotSnmpdLocalPortEntry 3 } -- -- Transport mapping table -- begemotSnmpdTransportMappings OBJECT IDENTIFIER ::= { begemotSnmpdObjects 10 } begemotSnmpdTransportTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdTransportEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table containing all the currently loaded transport mappings." ::= { begemotSnmpdTransportMappings 1 } begemotSnmpdTransportEntry OBJECT-TYPE SYNTAX BegemotSnmpdTransportEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with the transport mappings." INDEX { begemotSnmpdTransportName } ::= { begemotSnmpdTransportTable 1 } BegemotSnmpdTransportEntry ::= SEQUENCE { begemotSnmpdTransportName OCTET STRING, begemotSnmpdTransportStatus INTEGER, begemotSnmpdTransportOid OBJECT IDENTIFIER } begemotSnmpdTransportName OBJECT-TYPE SYNTAX OCTET STRING (SIZE(1..256)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The name of the mapping." ::= { begemotSnmpdTransportEntry 1 } begemotSnmpdTransportStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-only STATUS current DESCRIPTION "Used to create/activate/destroy the entry." ::= { begemotSnmpdTransportEntry 2 } begemotSnmpdTransportOid OBJECT-TYPE SYNTAX OBJECT IDENTIFIER MAX-ACCESS read-only STATUS current DESCRIPTION "A pointer to the group with the transport-dependend stuff." ::= { begemotSnmpdTransportEntry 3 } -- -- XXX These should go into their own MIB -- begemotSnmpdTransUdp OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 2 } begemotSnmpdTransLsock OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 3 } END Index: head/contrib/bsnmp/snmpd/action.c =================================================================== --- head/contrib/bsnmp/snmpd/action.c (revision 328159) +++ head/contrib/bsnmp/snmpd/action.c (revision 328160) @@ -1,1224 +1,1270 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * Copyright (c) 2004-2006 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: action.c 517 2006-10-31 08:52:04Z brandt_h $ * * Variable access for SNMPd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" static const struct asn_oid oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable; #ifdef __FreeBSD__ static const struct asn_oid oid_freeBSDVersion = OIDX_freeBSDVersion; #endif /* * Get an integer value from the KERN sysctl subtree. */ static char * act_getkernint(int id) { int mib[2]; size_t len; u_long value; char *string; mib[0] = CTL_KERN; mib[1] = id; len = sizeof(value); if (sysctl(mib, 2, &value, &len, NULL, 0) != 0) return (NULL); if ((string = malloc(20)) == NULL) return (NULL); sprintf(string, "%lu", value); return (string); } /* * Initialize global variables of the system group. */ int init_actvals(void) { struct utsname uts; char *hostid; size_t len; #ifdef __FreeBSD__ char *rel, *p, *end; u_long num; #endif if (uname(&uts) == -1) return (-1); if ((systemg.name = strdup(uts.nodename)) == NULL) return (-1); if ((hostid = act_getkernint(KERN_HOSTID)) == NULL) return (-1); len = strlen(uts.nodename) + 1; len += strlen(hostid) + 1; len += strlen(uts.sysname) + 1; len += strlen(uts.release) + 1; if ((systemg.descr = malloc(len)) == NULL) { free(hostid); return (-1); } sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname, uts.release); #ifdef __FreeBSD__ /* * Construct a FreeBSD oid */ systemg.object_id = oid_freeBSDVersion; rel = uts.release; while ((p = strsep(&rel, ".")) != NULL && systemg.object_id.len < ASN_MAXOIDLEN) { systemg.object_id.subs[systemg.object_id.len] = 0; if (*p != '\0') { num = strtoul(p, &end, 10); if (end == p) break; systemg.object_id.subs[systemg.object_id.len] = num; } systemg.object_id.len++; } #endif free(hostid); return (0); } /* * Initialize global variables of the snmpEngine group. */ int init_snmpd_engine(void) { char *hostid; snmpd_engine.engine_boots = 1; snmpd_engine.engine_time = 1; snmpd_engine.max_msg_size = 1500; /* XXX */ snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80; snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16; snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8; snmpd_engine.engine_id[3] = OID_freeBSD & 0xff; snmpd_engine.engine_id[4] = 128; snmpd_engine.engine_len = 5; if ((hostid = act_getkernint(KERN_HOSTID)) == NULL) return (-1); if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) { memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len); snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ; } else { memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, hostid, strlen(hostid)); snmpd_engine.engine_len += strlen(hostid); } free(hostid); return (0); } int set_snmpd_engine(void) { FILE *fp; uint32_t i; uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2]; if (engine_file[0] == '\0') return (-1); cptr = myengine; for (i = 0; i < snmpd_engine.engine_len; i++) cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]); *cptr++ = '\n'; *cptr++ = '\0'; if ((fp = fopen(engine_file, "r+")) != NULL) { if (fgets(engine, sizeof(engine) - 1, fp) == NULL || fscanf(fp, "%u", &snmpd_engine.engine_boots) <= 0) { fclose(fp); goto save_boots; } fclose(fp); if (strcmp(myengine, engine) != 0) snmpd_engine.engine_boots = 1; else snmpd_engine.engine_boots++; } else if (errno != ENOENT) return (-1); save_boots: if ((fp = fopen(engine_file, "w+")) == NULL) return (-1); fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots); fclose(fp); return (0); } void update_snmpd_engine_time(void) { uint64_t etime; etime = (get_ticks() - start_tick) / 100ULL; if (etime < INT32_MAX) snmpd_engine.engine_time = etime; else { start_tick = get_ticks(); (void)set_snmpd_engine(); snmpd_engine.engine_time = start_tick; } } /************************************************************* * * System group */ int op_system_group(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: switch (which) { case LEAF_sysDescr: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); return (string_save(value, ctx, -1, &systemg.descr)); case LEAF_sysObjectId: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); return (oid_save(value, ctx, &systemg.object_id)); case LEAF_sysContact: return (string_save(value, ctx, -1, &systemg.contact)); case LEAF_sysName: return (string_save(value, ctx, -1, &systemg.name)); case LEAF_sysLocation: return (string_save(value, ctx, -1, &systemg.location)); } return (SNMP_ERR_NO_CREATION); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_sysDescr: string_rollback(ctx, &systemg.descr); return (SNMP_ERR_NOERROR); case LEAF_sysObjectId: oid_rollback(ctx, &systemg.object_id); return (SNMP_ERR_NOERROR); case LEAF_sysContact: string_rollback(ctx, &systemg.contact); return (SNMP_ERR_NOERROR); case LEAF_sysName: string_rollback(ctx, &systemg.name); return (SNMP_ERR_NOERROR); case LEAF_sysLocation: string_rollback(ctx, &systemg.location); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_sysDescr: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysObjectId: oid_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysContact: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysName: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysLocation: string_commit(ctx); return (SNMP_ERR_NOERROR); } abort(); } /* * Come here for GET. */ switch (which) { case LEAF_sysDescr: return (string_get(value, systemg.descr, -1)); case LEAF_sysObjectId: return (oid_get(value, &systemg.object_id)); case LEAF_sysUpTime: value->v.uint32 = get_ticks() - start_tick; break; case LEAF_sysContact: return (string_get(value, systemg.contact, -1)); case LEAF_sysName: return (string_get(value, systemg.name, -1)); case LEAF_sysLocation: return (string_get(value, systemg.location, -1)); case LEAF_sysServices: value->v.integer = systemg.services; break; case LEAF_sysORLastChange: value->v.uint32 = systemg.or_last_change; break; } return (SNMP_ERR_NOERROR); } /************************************************************* * * Debug group */ int op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: value->v.integer = TRUTH_MK(debug.dump_pdus); break; case LEAF_begemotSnmpdDebugSnmpTrace: value->v.uint32 = snmp_trace; break; case LEAF_begemotSnmpdDebugSyslogPri: value->v.integer = debug.logpri; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = debug.dump_pdus; debug.dump_pdus = TRUTH_GET(value->v.integer); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSnmpTrace: ctx->scratch->int1 = snmp_trace; snmp_trace = value->v.uint32; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: if (value->v.integer < 0 || value->v.integer > 8) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = debug.logpri; debug.logpri = (u_int)value->v.integer; return (SNMP_ERR_NOERROR); } return (SNMP_ERR_NO_CREATION); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: debug.dump_pdus = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSnmpTrace: snmp_trace = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: debug.logpri = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: case LEAF_begemotSnmpdDebugSnmpTrace: return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: if (debug.logpri == 0) setlogmask(0); else setlogmask(LOG_UPTO(debug.logpri - 1)); return (SNMP_ERR_NOERROR); } abort(); } abort(); } /************************************************************* * * OR Table */ int op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct objres *objres; switch (op) { case SNMP_OP_GETNEXT: if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.subs[sub] = objres->index; value->var.len = sub + 1; break; case SNMP_OP_GET: if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } /* * Come here for GET, GETNEXT. */ switch (value->var.subs[sub - 1]) { case LEAF_sysORID: value->v.oid = objres->oid; break; case LEAF_sysORDescr: return (string_get(value, objres->descr, -1)); case LEAF_sysORUpTime: value->v.uint32 = objres->uptime; break; } return (SNMP_ERR_NOERROR); } /************************************************************* * * mib-2 snmp */ int op_snmp(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_snmpInPkts: value->v.uint32 = snmpd_stats.inPkts; break; case LEAF_snmpInBadVersions: value->v.uint32 = snmpd_stats.inBadVersions; break; case LEAF_snmpInBadCommunityNames: value->v.uint32 = snmpd_stats.inBadCommunityNames; break; case LEAF_snmpInBadCommunityUses: value->v.uint32 = snmpd_stats.inBadCommunityUses; break; case LEAF_snmpInASNParseErrs: value->v.uint32 = snmpd_stats.inASNParseErrs; break; case LEAF_snmpEnableAuthenTraps: value->v.integer = TRUTH_MK(snmpd.auth_traps); break; case LEAF_snmpSilentDrops: value->v.uint32 = snmpd_stats.silentDrops; break; case LEAF_snmpProxyDrops: value->v.uint32 = snmpd_stats.proxyDrops; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = value->v.integer; snmpd.auth_traps = TRUTH_GET(value->v.integer); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: snmpd.auth_traps = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: return (SNMP_ERR_NOERROR); } abort(); } abort(); } /************************************************************* * * SNMPd statistics group */ int op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_begemotSnmpdStatsNoRxBufs: value->v.uint32 = snmpd_stats.noRxbuf; break; case LEAF_begemotSnmpdStatsNoTxBufs: value->v.uint32 = snmpd_stats.noTxbuf; break; case LEAF_begemotSnmpdStatsInTooLongPkts: value->v.uint32 = snmpd_stats.inTooLong; break; case LEAF_begemotSnmpdStatsInBadPduTypes: value->v.uint32 = snmpd_stats.inBadPduTypes; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: case SNMP_OP_GETNEXT: abort(); } abort(); } /* * SNMPd configuration scalars */ int op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: value->v.integer = snmpd.txbuf; break; case LEAF_begemotSnmpdReceiveBuffer: value->v.integer = snmpd.rxbuf; break; case LEAF_begemotSnmpdCommunityDisable: value->v.integer = TRUTH_MK(snmpd.comm_dis); break; case LEAF_begemotSnmpdTrap1Addr: return (ip_get(value, snmpd.trap1addr)); case LEAF_begemotSnmpdVersionEnable: value->v.uint32 = snmpd.version_enable; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: ctx->scratch->int1 = snmpd.txbuf; if (value->v.integer < 484 || value->v.integer > 65535) return (SNMP_ERR_WRONG_VALUE); snmpd.txbuf = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdReceiveBuffer: ctx->scratch->int1 = snmpd.rxbuf; if (value->v.integer < 484 || value->v.integer > 65535) return (SNMP_ERR_WRONG_VALUE); snmpd.rxbuf = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdCommunityDisable: ctx->scratch->int1 = snmpd.comm_dis; if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); if (TRUTH_GET(value->v.integer)) { snmpd.comm_dis = 1; } else { if (snmpd.comm_dis) return (SNMP_ERR_WRONG_VALUE); } return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: return (ip_save(value, ctx, snmpd.trap1addr)); case LEAF_begemotSnmpdVersionEnable: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); ctx->scratch->int1 = snmpd.version_enable; if (value->v.uint32 == 0 || (value->v.uint32 & ~VERS_ENABLE_ALL)) return (SNMP_ERR_WRONG_VALUE); snmpd.version_enable = value->v.uint32; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: snmpd.rxbuf = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdReceiveBuffer: snmpd.txbuf = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdCommunityDisable: snmpd.comm_dis = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: ip_rollback(ctx, snmpd.trap1addr); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdVersionEnable: snmpd.version_enable = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: case LEAF_begemotSnmpdReceiveBuffer: case LEAF_begemotSnmpdCommunityDisable: return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: ip_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdVersionEnable: return (SNMP_ERR_NOERROR); } abort(); } abort(); } /* * The community table */ int op_community(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { - asn_subid_t which = value->var.subs[sub - 1]; + struct asn_oid index; struct community *c; + asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: if ((community != COMM_INITIALIZE && snmpd.comm_dis) || (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &c->index); break; case SNMP_OP_GET: if ((community != COMM_INITIALIZE && snmpd.comm_dis) || (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: - if ((community != COMM_INITIALIZE && snmpd.comm_dis) || - (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) - return (SNMP_ERR_NO_CREATION); - if (which != LEAF_begemotSnmpdCommunityString) + if (community != COMM_INITIALIZE && snmpd.comm_dis) return (SNMP_ERR_NOT_WRITEABLE); - return (string_save(value, ctx, -1, &c->string)); + index.len = 2; + index.subs[0] = 0; + index.subs[1] = value->var.subs[value->var.len - 1]; + switch (which) { + case LEAF_begemotSnmpdCommunityString: + /* check that given string is unique */ + TAILQ_FOREACH(c, &community_list, link) { + if (!asn_compare_oid(&index, &c->index)) + continue; + if (c->string != NULL && strcmp(c->string, + value->v.octetstring.octets) == 0) + return (SNMP_ERR_WRONG_VALUE); + } + case LEAF_begemotSnmpdCommunityPermission: + break; + default: + return (SNMP_ERR_NOT_WRITEABLE); + } + if ((c = FIND_OBJECT_OID(&community_list, &value->var, + sub)) == NULL) { + /* create new community and use user sepcified index */ + c = comm_define_ordered(COMM_READ, "SNMP Custom Community", + &index, NULL, NULL); + if (c == NULL) + return (SNMP_ERR_NO_CREATION); + } + switch (which) { + case LEAF_begemotSnmpdCommunityString: + return (string_save(value, ctx, -1, &c->string)); + case LEAF_begemotSnmpdCommunityPermission: + if (value->v.integer != COMM_READ && + value->v.integer != COMM_WRITE) + return (SNMP_ERR_WRONG_VALUE); + c->private = value->v.integer; + break; + default: + return (SNMP_ERR_NOT_WRITEABLE); + } + return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if (which == LEAF_begemotSnmpdCommunityString) { if ((c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) string_free(ctx); else string_rollback(ctx, &c->string); return (SNMP_ERR_NOERROR); } + if (which == LEAF_begemotSnmpdCommunityPermission) + return (SNMP_ERR_NOERROR); abort(); case SNMP_OP_COMMIT: if (which == LEAF_begemotSnmpdCommunityString) { if ((c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) string_free(ctx); else string_commit(ctx); return (SNMP_ERR_NOERROR); } + if (which == LEAF_begemotSnmpdCommunityPermission) + return (SNMP_ERR_NOERROR); abort(); default: abort(); } switch (which) { case LEAF_begemotSnmpdCommunityString: return (string_get(value, c->string, -1)); case LEAF_begemotSnmpdCommunityDescr: return (string_get(value, c->descr, -1)); + + case LEAF_begemotSnmpdCommunityPermission: + value->v.integer = c->private; + return (SNMP_ERR_NOERROR); + default: + return (SNMP_ERR_NOT_WRITEABLE); } abort(); } /* * Module table. */ struct module_dep { struct snmp_dependency dep; u_char section[LM_SECTION_MAX + 1]; u_char *path; struct lmodule *m; }; static int dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct module_dep *mdep = (struct module_dep *)(void *)dep; switch (op) { case SNMP_DEPOP_COMMIT: if (mdep->path == NULL) { /* unload - find the module */ TAILQ_FOREACH(mdep->m, &lmodules, link) if (strcmp(mdep->m->section, mdep->section) == 0) break; if (mdep->m == NULL) /* no such module - that's ok */ return (SNMP_ERR_NOERROR); /* handle unloading in the finalizer */ return (SNMP_ERR_NOERROR); } /* load */ if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) { /* could not load */ return (SNMP_ERR_RES_UNAVAIL); } /* start in finalizer */ return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (mdep->path == NULL) { /* rollback unload - the finalizer takes care */ return (SNMP_ERR_NOERROR); } /* rollback load */ lm_unload(mdep->m); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if (mdep->path == NULL) { if (mdep->m != NULL && ctx->code == SNMP_RET_OK) lm_unload(mdep->m); } else { if (mdep->m != NULL && ctx->code == SNMP_RET_OK && community != COMM_INITIALIZE) lm_start(mdep->m); free(mdep->path); } return (SNMP_ERR_NOERROR); } abort(); } int op_modules(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct lmodule *m; u_char *section, *ptr; size_t seclen; struct module_dep *mdep; struct asn_oid idx; switch (op) { case SNMP_OP_GETNEXT: if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &m->index); break; case SNMP_OP_GET: if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: m = FIND_OBJECT_OID(&lmodules, &value->var, sub); if (which != LEAF_begemotSnmpdModulePath) { if (m == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); } /* the errors in the next few statements can only happen when * m is NULL, hence the NO_CREATION error. */ if (index_decode(&value->var, sub, iidx, §ion, &seclen)) return (SNMP_ERR_NO_CREATION); /* check the section name */ if (seclen > LM_SECTION_MAX || seclen == 0) { free(section); return (SNMP_ERR_NO_CREATION); } for (ptr = section; ptr < section + seclen; ptr++) if (!isascii(*ptr) || !isalnum(*ptr)) { free(section); return (SNMP_ERR_NO_CREATION); } if (!isalpha(section[0])) { free(section); return (SNMP_ERR_NO_CREATION); } /* check the path */ for (ptr = value->v.octetstring.octets; ptr < value->v.octetstring.octets + value->v.octetstring.len; ptr++) { if (*ptr == '\0') { free(section); return (SNMP_ERR_WRONG_VALUE); } } if (m == NULL) { if (value->v.octetstring.len == 0) { free(section); return (SNMP_ERR_INCONS_VALUE); } } else { if (value->v.octetstring.len != 0) { free(section); return (SNMP_ERR_INCONS_VALUE); } } asn_slice_oid(&idx, &value->var, sub, value->var.len); /* so far, so good */ mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx, &oid_begemotSnmpdModuleTable, &idx, sizeof(*mdep), dep_modules); if (mdep == NULL) { free(section); return (SNMP_ERR_RES_UNAVAIL); } if (mdep->section[0] != '\0') { /* two writes to the same entry - bad */ free(section); return (SNMP_ERR_INCONS_VALUE); } strncpy(mdep->section, section, seclen); mdep->section[seclen] = '\0'; free(section); if (value->v.octetstring.len == 0) mdep->path = NULL; else { if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL) return (SNMP_ERR_RES_UNAVAIL); strncpy(mdep->path, value->v.octetstring.octets, value->v.octetstring.len); mdep->path[value->v.octetstring.len] = '\0'; } ctx->scratch->ptr1 = mdep; return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: abort(); } switch (which) { case LEAF_begemotSnmpdModulePath: return (string_get(value, m->path, -1)); case LEAF_begemotSnmpdModuleComment: return (string_get(value, m->config->comment, -1)); } abort(); } int op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_snmpSetSerialNo: value->v.integer = snmp_serial_no; break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_snmpSetSerialNo: if (value->v.integer != snmp_serial_no) return (SNMP_ERR_INCONS_VALUE); break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (snmp_serial_no++ == 2147483647) snmp_serial_no = 0; return (SNMP_ERR_NOERROR); } abort(); } /* * SNMP Engine */ int op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); switch (which) { case LEAF_snmpEngineID: if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id, snmpd_engine.engine_len); ctx->scratch->int1 = snmpd_engine.engine_len; snmpd_engine.engine_len = value->v.octetstring.len; memcpy(snmpd_engine.engine_id, value->v.octetstring.octets, value->v.octetstring.len); break; case LEAF_snmpEngineMaxMessageSize: ctx->scratch->int1 = snmpd_engine.max_msg_size; snmpd_engine.max_msg_size = value->v.integer; break; default: return (SNMP_ERR_NOT_WRITEABLE); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_snmpEngineID: snmpd_engine.engine_len = ctx->scratch->int1; memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1, snmpd_engine.engine_len); free(ctx->scratch->ptr1); break; case LEAF_snmpEngineMaxMessageSize: snmpd_engine.max_msg_size = ctx->scratch->int1; break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (which == LEAF_snmpEngineID) { if (set_snmpd_engine() < 0) { snmpd_engine.engine_len = ctx->scratch->int1; memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1, ctx->scratch->int1); } free(ctx->scratch->ptr1); } return (SNMP_ERR_NOERROR); } switch (which) { case LEAF_snmpEngineID: return (string_get(value, snmpd_engine.engine_id, snmpd_engine.engine_len)); case LEAF_snmpEngineBoots: value->v.integer = snmpd_engine.engine_boots; break; case LEAF_snmpEngineTime: update_snmpd_engine_time(); value->v.integer = snmpd_engine.engine_time; break; case LEAF_snmpEngineMaxMessageSize: value->v.integer = snmpd_engine.max_msg_size; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } /* * Transport table */ int op_transport_table(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct transport *t; u_char *tname, *ptr; size_t tnamelen; switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: t = FIND_OBJECT_OID(&transport_list, &value->var, sub); if (which != LEAF_begemotSnmpdTransportStatus) { if (t == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); } /* the errors in the next few statements can only happen when * t is NULL, hence the NO_CREATION error. */ if (index_decode(&value->var, sub, iidx, &tname, &tnamelen)) return (SNMP_ERR_NO_CREATION); /* check the section name */ if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) { free(tname); return (SNMP_ERR_NO_CREATION); } for (ptr = tname; ptr < tname + tnamelen; ptr++) { if (!isascii(*ptr) || !isalnum(*ptr)) { free(tname); return (SNMP_ERR_NO_CREATION); } } /* for now */ return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: abort(); } switch (which) { case LEAF_begemotSnmpdTransportStatus: value->v.integer = 1; break; case LEAF_begemotSnmpdTransportOid: memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id)); break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmpd/main.c =================================================================== --- head/contrib/bsnmp/snmpd/main.c (revision 328159) +++ head/contrib/bsnmp/snmpd/main.c (revision 328160) @@ -1,3090 +1,3111 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $ * * SNMPd main stuff. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCPWRAPPERS #include #include #endif #include "support.h" #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" #define PATH_PID "/var/run/%s.pid" #define PATH_CONFIG "/etc/%s.config" #define PATH_ENGINE "/var/%s.engine" uint64_t this_tick; /* start of processing of current packet (absolute) */ uint64_t start_tick; /* start of processing */ struct systemg systemg = { NULL, { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }}, NULL, NULL, NULL, 64 + 8 + 4, 0 }; struct debug debug = { 0, /* dump_pdus */ LOG_DEBUG, /* log_pri */ 0, /* evdebug */ }; struct snmpd snmpd = { 2048, /* txbuf */ 2048, /* rxbuf */ 0, /* comm_dis */ 0, /* auth_traps */ {0, 0, 0, 0}, /* trap1addr */ VERS_ENABLE_ALL,/* version_enable */ }; struct snmpd_stats snmpd_stats; struct snmpd_usmstat snmpd_usmstats; /* snmpEngine */ struct snmp_engine snmpd_engine; /* snmpSerialNo */ int32_t snmp_serial_no; struct snmpd_target_stats snmpd_target_stats; /* search path for config files */ const char *syspath = PATH_SYSCONFIG; /* list of all loaded modules */ struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules); /* list of loaded modules during start-up in the order they were loaded */ static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start); /* list of all known communities */ struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list); /* list of all known USM users */ static struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist); /* A list of all VACM users configured, including v1, v2c and v3 */ static struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist); /* A list of all VACM groups */ static struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist); static struct vacm_group vacm_default_group = { .groupname = "", }; /* The list of configured access entries */ static struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist); /* The list of configured views */ static struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist); /* The list of configured contexts */ static struct vacm_contextlist vacm_contextlist = SLIST_HEAD_INITIALIZER(vacm_contextlist); /* list of all installed object resources */ struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list); /* community value generator */ static u_int next_community_index = 1; /* list of all known ranges */ struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list); /* identifier generator */ u_int next_idrange = 1; /* list of all current timers */ struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list); /* list of file descriptors */ struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list); /* program arguments */ static char **progargs; static int nprogargs; /* current community */ u_int community; static struct community *comm; /* current USM user */ struct usm_user *usm_user; /* file names */ static char config_file[MAXPATHLEN + 1]; static char pid_file[MAXPATHLEN + 1]; char engine_file[MAXPATHLEN + 1]; #ifndef USE_LIBBEGEMOT /* event context */ static evContext evctx; #endif /* signal mask */ static sigset_t blocked_sigs; /* signal handling */ static int work; #define WORK_DOINFO 0x0001 #define WORK_RECONFIG 0x0002 /* oids */ static const struct asn_oid oid_snmpMIB = OIDX_snmpMIB, oid_begemotSnmpd = OIDX_begemotSnmpd, oid_coldStart = OIDX_coldStart, oid_authenticationFailure = OIDX_authenticationFailure; const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }}; const struct asn_oid oid_usmUnknownEngineIDs = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}}; const struct asn_oid oid_usmNotInTimeWindows = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}}; /* request id generator for traps */ u_int trap_reqid; /* help text */ static const char usgtxt[] = "\ Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\ Open Communication Systems (FhG Fokus). All rights reserved.\n\ Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\ usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\ [-l prefix] [-m variable=value] [-p file]\n\ options:\n\ -d don't daemonize\n\ -h print this info\n\ -c file specify configuration file\n\ -D options debugging options\n\ -e file specify engine id file\n\ -I path system include path\n\ -l prefix default basename for pid and config file\n\ -m var=val define variable\n\ -p file specify pid file\n\ "; /* hosts_access(3) request */ #ifdef USE_TCPWRAPPERS static struct request_info req; #endif /* transports */ extern const struct transport_def udp_trans; extern const struct transport_def lsock_trans; struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list); /* forward declarations */ static void snmp_printf_func(const char *fmt, ...); static void snmp_error_func(const char *err, ...); static void snmp_debug_func(const char *err, ...); static void asn_error_func(const struct asn_buf *b, const char *err, ...); /* * Allocate rx/tx buffer. We allocate one byte more for rx. */ void * buf_alloc(int tx) { void *buf; if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) { syslog(LOG_CRIT, "cannot allocate buffer"); if (tx) snmpd_stats.noTxbuf++; else snmpd_stats.noRxbuf++; return (NULL); } return (buf); } /* * Return the buffer size. */ size_t buf_size(int tx) { return (tx ? snmpd.txbuf : snmpd.rxbuf); } /* * Prepare a PDU for output */ void snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen, const char *dest) { struct asn_buf resp_b; enum snmp_code code; resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; if ((code = snmp_pdu_encode(pdu, &resp_b)) != SNMP_CODE_OK) { syslog(LOG_ERR, "cannot encode message (code=%d)", code); abort(); } if (debug.dump_pdus) { snmp_printf("%s <- ", dest); snmp_pdu_dump(pdu); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); } /* * Check USM PDU header credentials against local SNMP Engine & users. */ static enum snmp_code snmp_pdu_auth_user(struct snmp_pdu *pdu) { usm_user = NULL; /* un-authenticated snmpEngineId discovery */ if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) { pdu->engine.engine_len = snmpd_engine.engine_len; memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); update_snmpd_engine_time(); pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if ((usm_user = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name)) == NULL || usm_user->status != 1 /* active */) return (SNMP_CODE_BADUSER); if (usm_user->user_engine_len != snmpd_engine.engine_len || memcmp(usm_user->user_engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_BADENGINE); pdu->user.priv_proto = usm_user->suser.priv_proto; memcpy(pdu->user.priv_key, usm_user->suser.priv_key, sizeof(pdu->user.priv_key)); /* authenticated snmpEngineId discovery */ if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { update_snmpd_engine_time(); pdu->user.auth_proto = usm_user->suser.auth_proto; memcpy(pdu->user.auth_key, usm_user->suser.auth_key, sizeof(pdu->user.auth_key)); if (pdu->engine.engine_boots == 0 && pdu->engine.engine_time == 0) { update_snmpd_engine_time(); pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if (pdu->engine.engine_boots != snmpd_engine.engine_boots || abs(pdu->engine.engine_time - snmpd_engine.engine_time) > SNMP_TIME_WINDOW) return (SNMP_CODE_NOTINTIME); } if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) || ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 && usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) || ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 && usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV)) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } /* * Check whether access to each of var bindings in the PDU is allowed based * on the user credentials against the configured User groups & VACM views. */ enum snmp_code snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip) { const char *uname; int32_t suboid, smodel; uint32_t i; struct vacm_user *vuser; struct vacm_access *acl; struct vacm_context *vacmctx; struct vacm_view *view; /* * At least a default context exists if the snmpd_vacm(3) module is * running. */ if (SLIST_EMPTY(&vacm_contextlist) || (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0) return (SNMP_CODE_OK); switch (pdu->version) { case SNMP_V1: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv1; break; case SNMP_V2c: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv2c; break; case SNMP_V3: uname = pdu->user.sec_name; if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); /* Compare the PDU context engine id against the agent's */ if (pdu->context_engine_len != snmpd_engine.engine_len || memcmp(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_FAILED); break; default: abort(); } SLIST_FOREACH(vuser, &vacm_userlist, vvu) if (strcmp(uname, vuser->secname) == 0 && vuser->sec_model == smodel) break; if (vuser == NULL || vuser->group == NULL) return (SNMP_CODE_FAILED); /* XXX: shteryana - recheck */ TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) { if (acl->group != vuser->group) continue; SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl) if (memcmp(vacmctx->ctxname, acl->ctx_prefix, acl->ctx_match) == 0) goto match; } return (SNMP_CODE_FAILED); match: switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_GETBULK: if ((view = acl->read_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_SET: if ((view = acl->write_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_TRAP: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: if ((view = acl->notify_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_RESPONSE: /* NOTREACHED */ return (SNMP_CODE_FAILED); default: abort(); } for (i = 0; i < pdu->nbindings; i++) { /* XXX - view->mask*/ suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var); if ((!suboid && !view->exclude) || (suboid && view->exclude)) { *ip = i + 1; return (SNMP_CODE_FAILED); } } return (SNMP_CODE_OK); } /* * SNMP input. Start: decode the PDU, find the user or community. */ enum snmpd_input_err snmp_input_start(const u_char *buf, size_t len, const char *source, struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen) { struct asn_buf b; enum snmp_code code; enum snmpd_input_err ret; int sret; /* update uptime */ this_tick = get_ticks(); b.asn_cptr = buf; b.asn_len = len; ret = SNMPD_INPUT_OK; /* look whether we have enough bytes for the entire PDU. */ switch (sret = snmp_pdu_snoop(&b)) { case 0: return (SNMPD_INPUT_TRUNC); case -1: snmpd_stats.inASNParseErrs++; return (SNMPD_INPUT_FAILED); } b.asn_len = *pdulen = (size_t)sret; memset(pdu, 0, sizeof(*pdu)); if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK) goto decoded; if (pdu->version == SNMP_V3) { if (pdu->security_model != SNMP_SECMODEL_USM) { code = SNMP_CODE_FAILED; goto decoded; } if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK) goto decoded; if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK) goto decoded; } code = snmp_pdu_decode_scoped(&b, pdu, ip); decoded: snmpd_stats.inPkts++; switch (code) { case SNMP_CODE_FAILED: snmpd_stats.inASNParseErrs++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADVERS: bad_vers: snmpd_stats.inBadVersions++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADLEN: if (pdu->type == SNMP_OP_SET) ret = SNMPD_INPUT_VALBADLEN; break; case SNMP_CODE_OORANGE: if (pdu->type == SNMP_OP_SET) ret = SNMPD_INPUT_VALRANGE; break; case SNMP_CODE_BADENC: if (pdu->type == SNMP_OP_SET) ret = SNMPD_INPUT_VALBADENC; break; case SNMP_CODE_BADSECLEVEL: snmpd_usmstats.unsupported_seclevels++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_NOTINTIME: snmpd_usmstats.not_in_time_windows++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADUSER: snmpd_usmstats.unknown_users++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADENGINE: snmpd_usmstats.unknown_engine_ids++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADDIGEST: snmpd_usmstats.wrong_digests++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_EDECRYPT: snmpd_usmstats.decrypt_errors++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_OK: switch (pdu->version) { case SNMP_V1: if (!(snmpd.version_enable & VERS_ENABLE_V1)) goto bad_vers; break; case SNMP_V2c: if (!(snmpd.version_enable & VERS_ENABLE_V2C)) goto bad_vers; break; case SNMP_V3: if (!(snmpd.version_enable & VERS_ENABLE_V3)) goto bad_vers; break; case SNMP_Verr: goto bad_vers; } break; } if (debug.dump_pdus) { snmp_printf("%s -> ", source); snmp_pdu_dump(pdu); } /* * Look, whether we know the community or user */ if (pdu->version != SNMP_V3) { TAILQ_FOREACH(comm, &community_list, link) if (comm->string != NULL && strcmp(comm->string, pdu->community) == 0) break; if (comm == NULL) { snmpd_stats.inBadCommunityNames++; snmp_pdu_free(pdu); if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); ret = SNMPD_INPUT_BAD_COMM; } else community = comm->value; } else if (pdu->nbindings == 0) { /* RFC 3414 - snmpEngineID Discovery */ if (strlen(pdu->user.sec_name) == 0) { asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), &oid_usmUnknownEngineIDs); pdu->context_engine_len = snmpd_engine.engine_len; memcpy(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len); } else if (pdu->engine.engine_boots == 0 && pdu->engine.engine_time == 0) { asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), &oid_usmNotInTimeWindows); update_snmpd_engine_time(); pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; } } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH && (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) { snmpd_usmstats.not_in_time_windows++; ret = SNMPD_INPUT_FAILED; } if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK) ret = SNMPD_INPUT_FAILED; return (ret); } /* * Will return only _OK or _FAILED */ enum snmpd_input_err snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen, u_char *sndbuf, size_t *sndlen, const char *source, enum snmpd_input_err ierr, int32_t ivar, void *data) { struct snmp_pdu resp; struct asn_buf resp_b, pdu_b; enum snmp_ret ret; resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; pdu_b.asn_cptr = rcvbuf; pdu_b.asn_len = rcvlen; if (ierr != SNMPD_INPUT_OK) { /* error decoding the input of a SET */ if (pdu->version == SNMP_V1) pdu->error_status = SNMP_ERR_BADVALUE; else if (ierr == SNMPD_INPUT_VALBADLEN) pdu->error_status = SNMP_ERR_WRONG_LENGTH; else if (ierr == SNMPD_INPUT_VALRANGE) pdu->error_status = SNMP_ERR_WRONG_VALUE; else pdu->error_status = SNMP_ERR_WRONG_ENCODING; pdu->error_index = ivar; if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) { syslog(LOG_WARNING, "could not encode error response"); snmpd_stats.silentDrops++; return (SNMPD_INPUT_FAILED); } if (debug.dump_pdus) { snmp_printf("%s <- ", source); snmp_pdu_dump(pdu); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); return (SNMPD_INPUT_OK); } switch (pdu->type) { case SNMP_PDU_GET: ret = snmp_get(pdu, &resp_b, &resp, data); break; case SNMP_PDU_GETNEXT: ret = snmp_getnext(pdu, &resp_b, &resp, data); break; case SNMP_PDU_SET: ret = snmp_set(pdu, &resp_b, &resp, data); break; case SNMP_PDU_GETBULK: ret = snmp_getbulk(pdu, &resp_b, &resp, data); break; default: ret = SNMP_RET_IGN; break; } switch (ret) { case SNMP_RET_OK: /* normal return - send a response */ if (debug.dump_pdus) { snmp_printf("%s <- ", source); snmp_pdu_dump(&resp); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); snmp_pdu_free(&resp); return (SNMPD_INPUT_OK); case SNMP_RET_IGN: /* error - send nothing */ snmpd_stats.silentDrops++; return (SNMPD_INPUT_FAILED); case SNMP_RET_ERR: /* error - send error response. The snmp routine has * changed the error fields in the original message. */ resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) { syslog(LOG_WARNING, "could not encode error response"); snmpd_stats.silentDrops++; return (SNMPD_INPUT_FAILED); } else { if (debug.dump_pdus) { snmp_printf("%s <- ", source); snmp_pdu_dump(pdu); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); return (SNMPD_INPUT_OK); } } abort(); } /* * Insert a port into the right place in the transport's table of ports */ void trans_insert_port(struct transport *t, struct tport *port) { struct tport *p; port->transport = t; TAILQ_FOREACH(p, &t->table, link) { if (asn_compare_oid(&p->index, &port->index) > 0) { TAILQ_INSERT_BEFORE(p, port, link); return; } } TAILQ_INSERT_TAIL(&t->table, port, link); } /* * Remove a port from a transport's list */ void trans_remove_port(struct tport *port) { TAILQ_REMOVE(&port->transport->table, port, link); } /* * Find a port on a transport's list */ struct tport * trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub) { return (FIND_OBJECT_OID(&t->table, idx, sub)); } /* * Find next port on a transport's list */ struct tport * trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub) { return (NEXT_OBJECT_OID(&t->table, idx, sub)); } /* * Return first port */ struct tport * trans_first_port(struct transport *t) { return (TAILQ_FIRST(&t->table)); } /* * Iterate through all ports until a function returns a 0. */ struct tport * trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t), intptr_t arg) { struct tport *p; TAILQ_FOREACH(p, &t->table, link) if (func(p, arg) == 0) return (p); return (NULL); } /* * Register a transport */ int trans_register(const struct transport_def *def, struct transport **pp) { u_int i; char or_descr[256]; if ((*pp = malloc(sizeof(**pp))) == NULL) return (SNMP_ERR_GENERR); /* construct index */ (*pp)->index.len = strlen(def->name) + 1; (*pp)->index.subs[0] = strlen(def->name); for (i = 0; i < (*pp)->index.subs[0]; i++) (*pp)->index.subs[i + 1] = def->name[i]; (*pp)->vtab = def; if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) { free(*pp); return (SNMP_ERR_INCONS_VALUE); } /* register module */ snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name); if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) { free(*pp); return (SNMP_ERR_GENERR); } INSERT_OBJECT_OID((*pp), &transport_list); TAILQ_INIT(&(*pp)->table); return (SNMP_ERR_NOERROR); } /* * Unregister transport */ int trans_unregister(struct transport *t) { if (!TAILQ_EMPTY(&t->table)) return (SNMP_ERR_INCONS_VALUE); or_unregister(t->or_index); TAILQ_REMOVE(&transport_list, t, link); return (SNMP_ERR_NOERROR); } /* * File descriptor support */ #ifdef USE_LIBBEGEMOT static void input(int fd, int mask __unused, void *uap) #else static void input(evContext ctx __unused, void *uap, int fd, int mask __unused) #endif { struct fdesc *f = uap; (*f->func)(fd, f->udata); } void fd_suspend(void *p) { struct fdesc *f = p; #ifdef USE_LIBBEGEMOT if (f->id >= 0) { poll_unregister(f->id); f->id = -1; } #else if (evTestID(f->id)) { (void)evDeselectFD(evctx, f->id); evInitID(&f->id); } #endif } int fd_resume(void *p) { struct fdesc *f = p; int err; #ifdef USE_LIBBEGEMOT if (f->id >= 0) return (0); if ((f->id = poll_register(f->fd, input, f, POLL_IN)) < 0) { err = errno; syslog(LOG_ERR, "select fd %d: %m", f->fd); errno = err; return (-1); } #else if (evTestID(f->id)) return (0); if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) { err = errno; syslog(LOG_ERR, "select fd %d: %m", f->fd); errno = err; return (-1); } #endif return (0); } void * fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod) { struct fdesc *f; int err; if ((f = malloc(sizeof(struct fdesc))) == NULL) { err = errno; syslog(LOG_ERR, "fd_select: %m"); errno = err; return (NULL); } f->fd = fd; f->func = func; f->udata = udata; f->owner = mod; #ifdef USE_LIBBEGEMOT f->id = -1; #else evInitID(&f->id); #endif if (fd_resume(f)) { err = errno; free(f); errno = err; return (NULL); } LIST_INSERT_HEAD(&fdesc_list, f, link); return (f); } void fd_deselect(void *p) { struct fdesc *f = p; LIST_REMOVE(f, link); fd_suspend(f); free(f); } static void fd_flush(struct lmodule *mod) { struct fdesc *t, *t1; t = LIST_FIRST(&fdesc_list); while (t != NULL) { t1 = LIST_NEXT(t, link); if (t->owner == mod) fd_deselect(t); t = t1; } } /* * Consume a message from the input buffer */ static void snmp_input_consume(struct port_input *pi) { if (!pi->stream) { /* always consume everything */ pi->length = 0; return; } if (pi->consumed >= pi->length) { /* all bytes consumed */ pi->length = 0; return; } memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed); pi->length -= pi->consumed; } /* * Input from a socket */ int snmpd_input(struct port_input *pi, struct tport *tport) { u_char *sndbuf; size_t sndlen; struct snmp_pdu pdu; enum snmpd_input_err ierr, ferr; enum snmpd_proxy_err perr; ssize_t ret, slen; int32_t vi; #ifdef USE_TCPWRAPPERS char client[16]; #endif ret = tport->transport->vtab->recv(tport, pi); if (ret == -1) return (-1); #ifdef USE_TCPWRAPPERS /* * In case of AF_INET{6} peer, do hosts_access(5) check. */ if (pi->peer->sa_family != AF_LOCAL && inet_ntop(pi->peer->sa_family, &((const struct sockaddr_in *)(const void *)pi->peer)->sin_addr, client, sizeof(client)) != NULL) { request_set(&req, RQ_CLIENT_ADDR, client, 0); if (hosts_access(&req) == 0) { syslog(LOG_ERR, "refused connection from %.500s", eval_client(&req)); return (-1); } } else if (pi->peer->sa_family != AF_LOCAL) syslog(LOG_ERR, "inet_ntop(): %m"); #endif /* * Handle input */ ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi, &pi->consumed); if (ierr == SNMPD_INPUT_TRUNC) { /* need more bytes. This is ok only for streaming transports. * but only if we have not reached bufsiz yet. */ if (pi->stream) { if (pi->length == buf_size(0)) { snmpd_stats.silentDrops++; return (-1); } return (0); } snmpd_stats.silentDrops++; return (-1); } /* can't check for bad SET pdus here, because a proxy may have to * check the access first. We don't want to return an error response * to a proxy PDU with a wrong community */ if (ierr == SNMPD_INPUT_FAILED) { /* for streaming transports this is fatal */ if (pi->stream) return (-1); snmp_input_consume(pi); return (0); } if (ierr == SNMPD_INPUT_BAD_COMM) { snmp_input_consume(pi); return (0); } /* * If that is a module community and the module has a proxy function, * the hand it over to the module. */ if (comm != NULL && comm->owner != NULL && comm->owner->config->proxy != NULL) { perr = (*comm->owner->config->proxy)(&pdu, tport->transport, &tport->index, pi->peer, pi->peerlen, ierr, vi, !pi->cred || pi->priv); switch (perr) { case SNMPD_PROXY_OK: snmp_input_consume(pi); return (0); case SNMPD_PROXY_REJ: break; case SNMPD_PROXY_DROP: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.proxyDrops++; return (0); case SNMPD_PROXY_BADCOMM: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.inBadCommunityNames++; if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); case SNMPD_PROXY_BADCOMMUSE: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.inBadCommunityUses++; if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); } } /* * Check type */ if (pdu.type == SNMP_PDU_RESPONSE || pdu.type == SNMP_PDU_TRAP || pdu.type == SNMP_PDU_TRAP2) { snmpd_stats.silentDrops++; snmpd_stats.inBadPduTypes++; snmp_pdu_free(&pdu); snmp_input_consume(pi); return (0); } /* * Check community */ if (pdu.version < SNMP_V3 && ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) || - (community != COMM_WRITE && - (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) { + (comm != NULL && comm->private != COMM_WRITE && + (pdu.type == SNMP_PDU_SET || comm->private != COMM_READ)))) { snmpd_stats.inBadCommunityUses++; snmp_pdu_free(&pdu); snmp_input_consume(pi); if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); } /* * Execute it. */ if ((sndbuf = buf_alloc(1)) == NULL) { snmpd_stats.silentDrops++; snmp_pdu_free(&pdu); snmp_input_consume(pi); return (0); } ferr = snmp_input_finish(&pdu, pi->buf, pi->length, sndbuf, &sndlen, "SNMP", ierr, vi, NULL); if (ferr == SNMPD_INPUT_OK) { slen = tport->transport->vtab->send(tport, sndbuf, sndlen, pi->peer, pi->peerlen); if (slen == -1) syslog(LOG_ERR, "send*: %m"); else if ((size_t)slen != sndlen) syslog(LOG_ERR, "send*: short write %zu/%zu", sndlen, (size_t)slen); } snmp_pdu_free(&pdu); free(sndbuf); snmp_input_consume(pi); return (0); } /* * Send a PDU to a given port */ void snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu, const struct sockaddr *addr, socklen_t addrlen) { struct transport *trans = targ; struct tport *tp; u_char *sndbuf; size_t sndlen; ssize_t len; TAILQ_FOREACH(tp, &trans->table, link) if (asn_compare_oid(port, &tp->index) == 0) break; if (tp == 0) return; if ((sndbuf = buf_alloc(1)) == NULL) return; snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY"); len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen); if (len == -1) syslog(LOG_ERR, "sendto: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "sendto: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } /* * Close an input source */ void snmpd_input_close(struct port_input *pi) { if (pi->id != NULL) fd_deselect(pi->id); if (pi->fd >= 0) (void)close(pi->fd); if (pi->buf != NULL) free(pi->buf); } /* * Dump internal state. */ #ifdef USE_LIBBEGEMOT static void info_func(void) #else static void info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { struct lmodule *m; u_int i; char buf[10000]; syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid()); for (i = 0; i < tree_size; i++) { switch (tree[i].type) { case SNMP_NODE_LEAF: sprintf(buf, "LEAF: %s %s", tree[i].name, asn_oid2str(&tree[i].oid)); break; case SNMP_NODE_COLUMN: sprintf(buf, "COL: %s %s", tree[i].name, asn_oid2str(&tree[i].oid)); break; } syslog(LOG_DEBUG, "%s", buf); } TAILQ_FOREACH(m, &lmodules, link) if (m->config->dump) (*m->config->dump)(); } /* * Re-read configuration */ #ifdef USE_LIBBEGEMOT static void config_func(void) #else static void config_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { struct lmodule *m; if (read_config(config_file, NULL)) { syslog(LOG_ERR, "error reading config file '%s'", config_file); return; } TAILQ_FOREACH(m, &lmodules, link) if (m->config->config) (*m->config->config)(); } /* * On USR1 dump actual configuration. */ static void onusr1(int s __unused) { work |= WORK_DOINFO; } static void onhup(int s __unused) { work |= WORK_RECONFIG; } static void onterm(int s __unused) { /* allow clean-up */ exit(0); } static void init_sigs(void) { struct sigaction sa; sa.sa_handler = onusr1; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); if (sigaction(SIGUSR1, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } sa.sa_handler = onhup; if (sigaction(SIGHUP, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } sa.sa_handler = onterm; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGTERM, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } if (sigaction(SIGINT, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } } static void block_sigs(void) { sigset_t set; sigfillset(&set); if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) { syslog(LOG_ERR, "SIG_BLOCK: %m"); exit(1); } } static void unblock_sigs(void) { if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) { syslog(LOG_ERR, "SIG_SETMASK: %m"); exit(1); } } /* * Shut down */ static void term(void) { (void)unlink(pid_file); } static void trans_stop(void) { struct transport *t; TAILQ_FOREACH(t, &transport_list, link) (void)t->vtab->stop(1); } /* * Define a macro from the command line */ static void do_macro(char *arg) { char *eq; int err; if ((eq = strchr(arg, '=')) == NULL) err = define_macro(arg, ""); else { *eq++ = '\0'; err = define_macro(arg, eq); } if (err == -1) { syslog(LOG_ERR, "cannot save macro: %m"); exit(1); } } /* * Re-implement getsubopt from scratch, because the second argument is broken * and will not compile with WARNS=5. */ static int getsubopt1(char **arg, const char *const *options, char **valp, char **optp) { static const char *const delim = ",\t "; u_int i; char *ptr; *optp = NULL; /* skip leading junk */ for (ptr = *arg; *ptr != '\0'; ptr++) if (strchr(delim, *ptr) == NULL) break; if (*ptr == '\0') { *arg = ptr; return (-1); } *optp = ptr; /* find the end of the option */ while (*++ptr != '\0') if (strchr(delim, *ptr) != NULL || *ptr == '=') break; if (*ptr != '\0') { if (*ptr == '=') { *ptr++ = '\0'; *valp = ptr; while (*ptr != '\0' && strchr(delim, *ptr) == NULL) ptr++; if (*ptr != '\0') *ptr++ = '\0'; } else *ptr++ = '\0'; } *arg = ptr; for (i = 0; *options != NULL; options++, i++) if (strcmp(*optp, *options) == 0) return (i); return (-1); } int main(int argc, char *argv[]) { int opt; FILE *fp; int background = 1; struct tport *p; const char *prefix = "snmpd"; struct lmodule *m; char *value = NULL, *option; /* XXX */ struct transport *t; #define DBG_DUMP 0 #define DBG_EVENTS 1 #define DBG_TRACE 2 static const char *const debug_opts[] = { "dump", "events", "trace", NULL }; snmp_printf = snmp_printf_func; snmp_error = snmp_error_func; snmp_debug = snmp_debug_func; asn_error = asn_error_func; while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF) switch (opt) { case 'c': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'd': background = 0; break; case 'D': while (*optarg) { switch (getsubopt1(&optarg, debug_opts, &value, &option)) { case DBG_DUMP: debug.dump_pdus = 1; break; case DBG_EVENTS: debug.evdebug++; break; case DBG_TRACE: if (value == NULL) syslog(LOG_ERR, "no value for 'trace'"); else snmp_trace = strtoul(value, NULL, 0); break; case -1: if (suboptarg) syslog(LOG_ERR, "unknown debug flag '%s'", option); else syslog(LOG_ERR, "missing debug flag"); break; } } break; case 'e': strlcpy(engine_file, optarg, sizeof(engine_file)); break; case 'h': fprintf(stderr, "%s", usgtxt); exit(0); case 'I': syspath = optarg; break; case 'l': prefix = optarg; break; case 'm': do_macro(optarg); break; case 'p': strlcpy(pid_file, optarg, sizeof(pid_file)); break; } openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER); setlogmask(LOG_UPTO(debug.logpri - 1)); if (background && daemon(0, 0) < 0) { syslog(LOG_ERR, "daemon: %m"); exit(1); } argc -= optind; argv += optind; progargs = argv; nprogargs = argc; srandomdev(); snmp_serial_no = random(); #ifdef USE_TCPWRAPPERS /* * Initialize hosts_access(3) handler. */ request_init(&req, RQ_DAEMON, "snmpd", 0); sock_methods(&req); #endif /* * Initialize the tree. */ if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) { syslog(LOG_ERR, "%m"); exit(1); } memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE); tree_size = CTREE_SIZE; /* * Get standard communities */ - (void)comm_define(1, "SNMP read", NULL, NULL); - (void)comm_define(2, "SNMP write", NULL, NULL); + comm_define(COMM_READ, "SNMP read", NULL, NULL); + comm_define(COMM_WRITE, "SNMP write", NULL, NULL); community = COMM_INITIALIZE; trap_reqid = reqid_allocate(512, NULL); if (config_file[0] == '\0') snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix); init_actvals(); init_snmpd_engine(); this_tick = get_ticks(); start_tick = this_tick; /* start transports */ if (atexit(trans_stop) == -1) { syslog(LOG_ERR, "atexit failed: %m"); exit(1); } if (udp_trans.start() != SNMP_ERR_NOERROR) syslog(LOG_WARNING, "cannot start UDP transport"); if (lsock_trans.start() != SNMP_ERR_NOERROR) syslog(LOG_WARNING, "cannot start LSOCK transport"); #ifdef USE_LIBBEGEMOT if (debug.evdebug > 0) rpoll_trace = 1; #else if (evCreate(&evctx)) { syslog(LOG_ERR, "evCreate: %m"); exit(1); } if (debug.evdebug > 0) evSetDebug(evctx, 10, stderr); #endif if (engine_file[0] == '\0') snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix); if (read_config(config_file, NULL)) { syslog(LOG_ERR, "error in config file"); exit(1); } TAILQ_FOREACH(t, &transport_list, link) TAILQ_FOREACH(p, &t->table, link) t->vtab->init_port(p); init_sigs(); if (pid_file[0] == '\0') snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix); if ((fp = fopen(pid_file, "w")) != NULL) { fprintf(fp, "%u", getpid()); fclose(fp); if (atexit(term) == -1) { syslog(LOG_ERR, "atexit failed: %m"); (void)remove(pid_file); exit(0); } } if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.", NULL) == 0) { syslog(LOG_ERR, "cannot register SNMPv2 MIB"); exit(1); } if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.", NULL) == 0) { syslog(LOG_ERR, "cannot register begemotSnmpd MIB"); exit(1); } while ((m = TAILQ_FIRST(&modules_start)) != NULL) { m->flags &= ~LM_ONSTARTLIST; TAILQ_REMOVE(&modules_start, m, start); lm_start(m); } snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL); for (;;) { #ifndef USE_LIBBEGEMOT evEvent event; #endif struct lmodule *mod; TAILQ_FOREACH(mod, &lmodules, link) if (mod->config->idle != NULL) (*mod->config->idle)(); #ifndef USE_LIBBEGEMOT if (evGetNext(evctx, &event, EV_WAIT) == 0) { if (evDispatch(evctx, event)) syslog(LOG_ERR, "evDispatch: %m"); } else if (errno != EINTR) { syslog(LOG_ERR, "evGetNext: %m"); exit(1); } #else poll_dispatch(1); #endif if (work != 0) { block_sigs(); if (work & WORK_DOINFO) { #ifdef USE_LIBBEGEMOT info_func(); #else if (evWaitFor(evctx, &work, info_func, NULL, NULL) == -1) { syslog(LOG_ERR, "evWaitFor: %m"); exit(1); } #endif } if (work & WORK_RECONFIG) { #ifdef USE_LIBBEGEMOT config_func(); #else if (evWaitFor(evctx, &work, config_func, NULL, NULL) == -1) { syslog(LOG_ERR, "evWaitFor: %m"); exit(1); } #endif } work = 0; unblock_sigs(); #ifndef USE_LIBBEGEMOT if (evDo(evctx, &work) == -1) { syslog(LOG_ERR, "evDo: %m"); exit(1); } #endif } } return (0); } uint64_t get_ticks(void) { struct timeval tv; uint64_t ret; if (gettimeofday(&tv, NULL)) abort(); ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL; return (ret); } /* * Timer support */ /* * Trampoline for the non-repeatable timers. */ #ifdef USE_LIBBEGEMOT static void tfunc(int tid __unused, void *uap) #else static void tfunc(evContext ctx __unused, void *uap, struct timespec due __unused, struct timespec inter __unused) #endif { struct timer *tp = uap; LIST_REMOVE(tp, link); tp->func(tp->udata); free(tp); } /* * Trampoline for the repeatable timers. */ #ifdef USE_LIBBEGEMOT static void trfunc(int tid __unused, void *uap) #else static void trfunc(evContext ctx __unused, void *uap, struct timespec due __unused, struct timespec inter __unused) #endif { struct timer *tp = uap; tp->func(tp->udata); } /* * Start a one-shot timer */ void * timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod) { struct timer *tp; #ifndef USE_LIBBEGEMOT struct timespec due; #endif if ((tp = malloc(sizeof(struct timer))) == NULL) { syslog(LOG_CRIT, "out of memory for timer"); exit(1); } #ifndef USE_LIBBEGEMOT due = evAddTime(evNowTime(), evConsTime(ticks / 100, (ticks % 100) * 10000)); #endif tp->udata = udata; tp->owner = mod; tp->func = func; LIST_INSERT_HEAD(&timer_list, tp, link); #ifdef USE_LIBBEGEMOT if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #else if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id) == -1) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #endif return (tp); } /* * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument * is currently ignored and the initial number of ticks is set to the * repeat number of ticks. */ void * timer_start_repeat(u_int ticks __unused, u_int repeat_ticks, void (*func)(void *), void *udata, struct lmodule *mod) { struct timer *tp; #ifndef USE_LIBBEGEMOT struct timespec due; struct timespec inter; #endif if ((tp = malloc(sizeof(struct timer))) == NULL) { syslog(LOG_CRIT, "out of memory for timer"); exit(1); } #ifndef USE_LIBBEGEMOT due = evAddTime(evNowTime(), evConsTime(ticks / 100, (ticks % 100) * 10000)); inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000); #endif tp->udata = udata; tp->owner = mod; tp->func = func; LIST_INSERT_HEAD(&timer_list, tp, link); #ifdef USE_LIBBEGEMOT if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #else if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #endif return (tp); } /* * Stop a timer. */ void timer_stop(void *p) { struct timer *tp = p; LIST_REMOVE(tp, link); #ifdef USE_LIBBEGEMOT poll_stop_timer(tp->id); #else if (evClearTimer(evctx, tp->id) == -1) { syslog(LOG_ERR, "cannot stop timer: %m"); exit(1); } #endif free(p); } static void timer_flush(struct lmodule *mod) { struct timer *t, *t1; t = LIST_FIRST(&timer_list); while (t != NULL) { t1 = LIST_NEXT(t, link); if (t->owner == mod) timer_stop(t); t = t1; } } static void snmp_printf_func(const char *fmt, ...) { va_list ap; static char *pend = NULL; char *ret, *new; va_start(ap, fmt); vasprintf(&ret, fmt, ap); va_end(ap); if (ret == NULL) return; if (pend != NULL) { if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1)) == NULL) { free(ret); return; } pend = new; strcat(pend, ret); free(ret); } else pend = ret; while ((ret = strchr(pend, '\n')) != NULL) { *ret = '\0'; syslog(LOG_DEBUG, "%s", pend); if (strlen(ret + 1) == 0) { free(pend); pend = NULL; break; } strcpy(pend, ret + 1); } } static void snmp_error_func(const char *err, ...) { char errbuf[1000]; va_list ap; if (!(snmp_trace & LOG_SNMP_ERRORS)) return; va_start(ap, err); snprintf(errbuf, sizeof(errbuf), "SNMP: "); vsnprintf(errbuf + strlen(errbuf), sizeof(errbuf) - strlen(errbuf), err, ap); va_end(ap); syslog(LOG_ERR, "%s", errbuf); } static void snmp_debug_func(const char *err, ...) { char errbuf[1000]; va_list ap; va_start(ap, err); snprintf(errbuf, sizeof(errbuf), "SNMP: "); vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf), err, ap); va_end(ap); syslog(LOG_DEBUG, "%s", errbuf); } static void asn_error_func(const struct asn_buf *b, const char *err, ...) { char errbuf[1000]; va_list ap; u_int i; if (!(snmp_trace & LOG_ASN1_ERRORS)) return; va_start(ap, err); snprintf(errbuf, sizeof(errbuf), "ASN.1: "); vsnprintf(errbuf + strlen(errbuf), sizeof(errbuf) - strlen(errbuf), err, ap); va_end(ap); if (b != NULL) { snprintf(errbuf + strlen(errbuf), sizeof(errbuf) - strlen(errbuf), " at"); for (i = 0; b->asn_len > i; i++) snprintf(errbuf + strlen(errbuf), sizeof(errbuf) - strlen(errbuf), " %02x", b->asn_cptr[i]); } syslog(LOG_ERR, "%s", errbuf); } /* * Create a new community */ -u_int -comm_define(u_int priv, const char *descr, struct lmodule *owner, - const char *str) +struct community* +comm_define_ordered(u_int priv, const char *descr, struct asn_oid *index, + struct lmodule *owner, const char *str) { struct community *c, *p; u_int ncomm; - /* generate an identifier */ - do { - if ((ncomm = next_community_index++) == UINT_MAX) - next_community_index = 1; - TAILQ_FOREACH(c, &community_list, link) - if (c->value == ncomm) - break; - } while (c != NULL); + ncomm = index->subs[index->len - 1]; + /* check that community doesn't already exist */ + TAILQ_FOREACH(c, &community_list, link) + if (c->value == ncomm) + return (c); + if ((c = malloc(sizeof(struct community))) == NULL) { - syslog(LOG_ERR, "comm_define: %m"); - return (0); + syslog(LOG_ERR, "%s: %m", __func__); + return (NULL); } c->owner = owner; c->value = ncomm; c->descr = descr; c->string = NULL; c->private = priv; if (str != NULL) { if((c->string = malloc(strlen(str)+1)) == NULL) { free(c); - return (0); + return (NULL); } strcpy(c->string, str); } - - /* make index */ - if (c->owner == NULL) { - c->index.len = 1; - c->index.subs[0] = 0; - } else { - c->index = c->owner->index; - } - c->index.subs[c->index.len++] = c->private; - /* * Insert ordered */ + c->index = *index; TAILQ_FOREACH(p, &community_list, link) { if (asn_compare_oid(&p->index, &c->index) > 0) { TAILQ_INSERT_BEFORE(p, c, link); break; } } if (p == NULL) TAILQ_INSERT_TAIL(&community_list, c, link); + return (c); +} + +u_int +comm_define(u_int priv, const char *descr, struct lmodule *owner, + const char *str) +{ + struct asn_oid index, *p; + struct community *c; + u_int ncomm; + + /* generate an identifier */ + do { + if ((ncomm = next_community_index++) == UINT_MAX) + next_community_index = 1; + TAILQ_FOREACH(c, &community_list, link) + if (c->value == ncomm) + break; + } while (c != NULL); + + /* make index */ + if (owner != NULL) + p = &owner->index; + else { + p = &index; + p->len = 1; + p->subs[0] = 0; + } + p->subs[p->len++] = ncomm; + c = comm_define_ordered(priv, descr, p, owner, str); + if (c == NULL) + return (0); return (c->value); } const char * comm_string(u_int ncomm) { struct community *p; TAILQ_FOREACH(p, &community_list, link) if (p->value == ncomm) return (p->string); return (NULL); } /* * Delete all communities allocated by a module */ static void comm_flush(struct lmodule *mod) { struct community *p, *p1; p = TAILQ_FIRST(&community_list); while (p != NULL) { p1 = TAILQ_NEXT(p, link); if (p->owner == mod) { free(p->string); TAILQ_REMOVE(&community_list, p, link); free(p); } p = p1; } } /* * Request ID handling. * * Allocate a new range of request ids. Use a first fit algorithm. */ u_int reqid_allocate(int size, struct lmodule *mod) { u_int type; struct idrange *r, *r1; if (size <= 0 || size > INT32_MAX) { syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size); return (0); } /* allocate a type id */ do { if ((type = next_idrange++) == UINT_MAX) next_idrange = 1; TAILQ_FOREACH(r, &idrange_list, link) if (r->type == type) break; } while(r != NULL); /* find a range */ if (TAILQ_EMPTY(&idrange_list)) r = NULL; else { r = TAILQ_FIRST(&idrange_list); if (r->base < size) { while((r1 = TAILQ_NEXT(r, link)) != NULL) { if (r1->base - (r->base + r->size) >= size) break; r = r1; } r = r1; } if (r == NULL) { r1 = TAILQ_LAST(&idrange_list, idrange_list); if (INT32_MAX - size + 1 < r1->base + r1->size) { syslog(LOG_ERR, "out of id ranges (%u)", size); return (0); } } } /* allocate structure */ if ((r1 = malloc(sizeof(struct idrange))) == NULL) { syslog(LOG_ERR, "%s: %m", __FUNCTION__); return (0); } r1->type = type; r1->size = size; r1->owner = mod; if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) { r1->base = 0; TAILQ_INSERT_HEAD(&idrange_list, r1, link); } else if (r == NULL) { r = TAILQ_LAST(&idrange_list, idrange_list); r1->base = r->base + r->size; TAILQ_INSERT_TAIL(&idrange_list, r1, link); } else { r = TAILQ_PREV(r, idrange_list, link); r1->base = r->base + r->size; TAILQ_INSERT_AFTER(&idrange_list, r, r1, link); } r1->next = r1->base; return (type); } int32_t reqid_next(u_int type) { struct idrange *r; int32_t id; TAILQ_FOREACH(r, &idrange_list, link) if (r->type == type) break; if (r == NULL) { syslog(LOG_CRIT, "wrong idrange type"); abort(); } if ((id = r->next++) == r->base + (r->size - 1)) r->next = r->base; return (id); } int32_t reqid_base(u_int type) { struct idrange *r; TAILQ_FOREACH(r, &idrange_list, link) if (r->type == type) return (r->base); syslog(LOG_CRIT, "wrong idrange type"); abort(); } u_int reqid_type(int32_t reqid) { struct idrange *r; TAILQ_FOREACH(r, &idrange_list, link) if (reqid >= r->base && reqid <= r->base + (r->size - 1)) return (r->type); return (0); } int reqid_istype(int32_t reqid, u_int type) { return (reqid_type(reqid) == type); } /* * Delete all communities allocated by a module */ static void reqid_flush(struct lmodule *mod) { struct idrange *p, *p1; p = TAILQ_FIRST(&idrange_list); while (p != NULL) { p1 = TAILQ_NEXT(p, link); if (p->owner == mod) { TAILQ_REMOVE(&idrange_list, p, link); free(p); } p = p1; } } /* * Merge the given tree for the given module into the main tree. */ static int compare_node(const void *v1, const void *v2) { const struct snmp_node *n1 = v1; const struct snmp_node *n2 = v2; return (asn_compare_oid(&n1->oid, &n2->oid)); } static int tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod) { struct snmp_node *xtree; u_int i; xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize)); if (xtree == NULL) { syslog(LOG_ERR, "tree_merge: %m"); return (-1); } tree = xtree; memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize); for (i = 0; i < nsize; i++) tree[tree_size + i].tree_data = mod; tree_size += nsize; qsort(tree, tree_size, sizeof(tree[0]), compare_node); return (0); } /* * Remove all nodes belonging to the loadable module */ static void tree_unmerge(struct lmodule *mod) { u_int s, d; for(s = d = 0; s < tree_size; s++) if (tree[s].tree_data != mod) { if (s != d) tree[d] = tree[s]; d++; } tree_size = d; } /* * Loadable modules */ struct lmodule * lm_load(const char *path, const char *section) { struct lmodule *m; int err; int i; char *av[MAX_MOD_ARGS + 1]; int ac; u_int u; if ((m = malloc(sizeof(*m))) == NULL) { syslog(LOG_ERR, "lm_load: %m"); return (NULL); } m->handle = NULL; m->flags = 0; strlcpy(m->section, section, sizeof(m->section)); if ((m->path = strdup(path)) == NULL) { syslog(LOG_ERR, "lm_load: %m"); goto err; } /* * Make index */ m->index.subs[0] = strlen(section); m->index.len = m->index.subs[0] + 1; for (u = 0; u < m->index.subs[0]; u++) m->index.subs[u + 1] = section[u]; /* * Load the object file and locate the config structure */ if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) { syslog(LOG_ERR, "lm_load: open %s", dlerror()); goto err; } if ((m->config = dlsym(m->handle, "config")) == NULL) { syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror()); goto err; } /* * Insert it into the right place */ INSERT_OBJECT_OID(m, &lmodules); /* preserve order */ if (community == COMM_INITIALIZE) { m->flags |= LM_ONSTARTLIST; TAILQ_INSERT_TAIL(&modules_start, m, start); } /* * make the argument vector. */ ac = 0; for (i = 0; i < nprogargs; i++) { if (strlen(progargs[i]) >= strlen(section) + 1 && strncmp(progargs[i], section, strlen(section)) == 0 && progargs[i][strlen(section)] == ':') { if (ac == MAX_MOD_ARGS) { syslog(LOG_WARNING, "too many arguments for " "module '%s", section); break; } av[ac++] = &progargs[i][strlen(section)+1]; } } av[ac] = NULL; /* * Run the initialization function */ if ((err = (*m->config->init)(m, ac, av)) != 0) { syslog(LOG_ERR, "lm_load: init failed: %d", err); TAILQ_REMOVE(&lmodules, m, link); goto err; } return (m); err: if ((m->flags & LM_ONSTARTLIST) != 0) TAILQ_REMOVE(&modules_start, m, start); if (m->handle) dlclose(m->handle); free(m->path); free(m); return (NULL); } /* * Start a module */ void lm_start(struct lmodule *mod) { const struct lmodule *m; /* * Merge tree. If this fails, unload the module. */ if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) { lm_unload(mod); return; } /* * Read configuration */ if (read_config(config_file, mod)) { syslog(LOG_ERR, "error in config file"); lm_unload(mod); return; } if (mod->config->start) (*mod->config->start)(); mod->flags |= LM_STARTED; /* * Inform other modules */ TAILQ_FOREACH(m, &lmodules, link) if (m->config->loading) (*m->config->loading)(mod, 1); } /* * Unload a module. */ void lm_unload(struct lmodule *m) { int err; const struct lmodule *mod; TAILQ_REMOVE(&lmodules, m, link); if (m->flags & LM_ONSTARTLIST) TAILQ_REMOVE(&modules_start, m, start); tree_unmerge(m); if ((m->flags & LM_STARTED) && m->config->fini && (err = (*m->config->fini)()) != 0) syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err); comm_flush(m); reqid_flush(m); timer_flush(m); fd_flush(m); dlclose(m->handle); free(m->path); /* * Inform other modules */ TAILQ_FOREACH(mod, &lmodules, link) if (mod->config->loading) (*mod->config->loading)(m, 0); free(m); } /* * Register an object resource and return the index (or 0 on failures) */ u_int or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod) { struct objres *objres, *or1; u_int idx; /* find a free index */ idx = 1; for (objres = TAILQ_FIRST(&objres_list); objres != NULL; objres = TAILQ_NEXT(objres, link)) { if ((or1 = TAILQ_NEXT(objres, link)) == NULL || or1->index > objres->index + 1) { idx = objres->index + 1; break; } } if ((objres = malloc(sizeof(*objres))) == NULL) return (0); objres->index = idx; objres->oid = *or; strlcpy(objres->descr, descr, sizeof(objres->descr)); objres->uptime = (uint32_t)(get_ticks() - start_tick); objres->module = mod; INSERT_OBJECT_INT(objres, &objres_list); systemg.or_last_change = objres->uptime; return (idx); } void or_unregister(u_int idx) { struct objres *objres; TAILQ_FOREACH(objres, &objres_list, link) if (objres->index == idx) { TAILQ_REMOVE(&objres_list, objres, link); free(objres); return; } } /* * RFC 3414 User-based Security Model support */ struct snmpd_usmstat * bsnmpd_get_usm_stats(void) { return (&snmpd_usmstats); } void bsnmpd_reset_usm_stats(void) { memset(&snmpd_usmstats, 0, sizeof(snmpd_usmstats)); } struct usm_user * usm_first_user(void) { return (SLIST_FIRST(&usm_userlist)); } struct usm_user * usm_next_user(struct usm_user *uuser) { if (uuser == NULL) return (NULL); return (SLIST_NEXT(uuser, up)); } struct usm_user * usm_find_user(uint8_t *engine, uint32_t elen, char *uname) { struct usm_user *uuser; SLIST_FOREACH(uuser, &usm_userlist, up) if (uuser->user_engine_len == elen && memcmp(uuser->user_engine_id, engine, elen) == 0 && strlen(uuser->suser.sec_name) == strlen(uname) && strcmp(uuser->suser.sec_name, uname) == 0) break; return (uuser); } static int usm_compare_user(struct usm_user *u1, struct usm_user *u2) { uint32_t i; if (u1->user_engine_len < u2->user_engine_len) return (-1); if (u1->user_engine_len > u2->user_engine_len) return (1); for (i = 0; i < u1->user_engine_len; i++) { if (u1->user_engine_id[i] < u2->user_engine_id[i]) return (-1); if (u1->user_engine_id[i] > u2->user_engine_id[i]) return (1); } if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name)) return (-1); if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name)) return (1); for (i = 0; i < strlen(u1->suser.sec_name); i++) { if (u1->suser.sec_name[i] < u2->suser.sec_name[i]) return (-1); if (u1->suser.sec_name[i] > u2->suser.sec_name[i]) return (1); } return (0); } struct usm_user * usm_new_user(uint8_t *eid, uint32_t elen, char *uname) { int cmp; struct usm_user *uuser, *temp, *prev; for (uuser = usm_first_user(); uuser != NULL; (uuser = usm_next_user(uuser))) { if (uuser->user_engine_len == elen && strlen(uname) == strlen(uuser->suser.sec_name) && strcmp(uname, uuser->suser.sec_name) == 0 && memcmp(eid, uuser->user_engine_id, elen) == 0) return (NULL); } if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL) return (NULL); memset(uuser, 0, sizeof(*uuser)); strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ); memcpy(uuser->user_engine_id, eid, elen); uuser->user_engine_len = elen; if ((prev = SLIST_FIRST(&usm_userlist)) == NULL || usm_compare_user(uuser, prev) < 0) { SLIST_INSERT_HEAD(&usm_userlist, uuser, up); return (uuser); } SLIST_FOREACH(temp, &usm_userlist, up) { if ((cmp = usm_compare_user(uuser, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, uuser, up); else if (cmp > 0) SLIST_INSERT_AFTER(temp, uuser, up); else { syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name); free(uuser); return (NULL); } return (uuser); } void usm_delete_user(struct usm_user *uuser) { SLIST_REMOVE(&usm_userlist, uuser, usm_user, up); free(uuser); } void usm_flush_users(void) { struct usm_user *uuser; while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) { SLIST_REMOVE_HEAD(&usm_userlist, up); free(uuser); } SLIST_INIT(&usm_userlist); } /* * RFC 3415 View-based Access Control Model support */ struct vacm_user * vacm_first_user(void) { return (SLIST_FIRST(&vacm_userlist)); } struct vacm_user * vacm_next_user(struct vacm_user *vuser) { if (vuser == NULL) return (NULL); return (SLIST_NEXT(vuser, vvu)); } static int vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2) { uint32_t i; if (v1->sec_model < v2->sec_model) return (-1); if (v1->sec_model > v2->sec_model) return (1); if (strlen(v1->secname) < strlen(v2->secname)) return (-1); if (strlen(v1->secname) > strlen(v2->secname)) return (1); for (i = 0; i < strlen(v1->secname); i++) { if (v1->secname[i] < v2->secname[i]) return (-1); if (v1->secname[i] > v2->secname[i]) return (1); } return (0); } struct vacm_user * vacm_new_user(int32_t smodel, char *uname) { int cmp; struct vacm_user *user, *temp, *prev; SLIST_FOREACH(user, &vacm_userlist, vvu) if (strcmp(uname, user->secname) == 0 && smodel == user->sec_model) return (NULL); if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL) return (NULL); memset(user, 0, sizeof(*user)); user->group = &vacm_default_group; SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg); user->sec_model = smodel; strlcpy(user->secname, uname, sizeof(user->secname)); if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL || vacm_compare_user(user, prev) < 0) { SLIST_INSERT_HEAD(&vacm_userlist, user, vvu); return (user); } SLIST_FOREACH(temp, &vacm_userlist, vvu) { if ((cmp = vacm_compare_user(user, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, user, vvu); else if (cmp > 0) SLIST_INSERT_AFTER(temp, user, vvu); else { syslog(LOG_ERR, "User %s exists", user->secname); free(user); return (NULL); } return (user); } int vacm_delete_user(struct vacm_user *user) { if (user->group != NULL && user->group != &vacm_default_group) { SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); if (SLIST_EMPTY(&user->group->group_users)) { SLIST_REMOVE(&vacm_grouplist, user->group, vacm_group, vge); free(user->group); } } SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu); free(user); return (0); } int vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len) { struct vacm_group *group; if (len >= SNMP_ADM_STR32_SIZ) return (-1); SLIST_FOREACH(group, &vacm_grouplist, vge) if (strlen(group->groupname) == len && memcmp(octets, group->groupname, len) == 0) break; if (group == NULL) { if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL) return (-1); memset(group, 0, sizeof(*group)); memcpy(group->groupname, octets, len); group->groupname[len] = '\0'; SLIST_INSERT_HEAD(&vacm_grouplist, group, vge); } SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); SLIST_INSERT_HEAD(&group->group_users, user, vvg); user->group = group; return (0); } void vacm_groups_init(void) { SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge); } struct vacm_access * vacm_first_access_rule(void) { return (TAILQ_FIRST(&vacm_accesslist)); } struct vacm_access * vacm_next_access_rule(struct vacm_access *acl) { if (acl == NULL) return (NULL); return (TAILQ_NEXT(acl, vva)); } static int vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2) { uint32_t i; if (strlen(v1->group->groupname) < strlen(v2->group->groupname)) return (-1); if (strlen(v1->group->groupname) > strlen(v2->group->groupname)) return (1); for (i = 0; i < strlen(v1->group->groupname); i++) { if (v1->group->groupname[i] < v2->group->groupname[i]) return (-1); if (v1->group->groupname[i] > v2->group->groupname[i]) return (1); } if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix)) return (-1); if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix)) return (1); for (i = 0; i < strlen(v1->ctx_prefix); i++) { if (v1->ctx_prefix[i] < v2->ctx_prefix[i]) return (-1); if (v1->ctx_prefix[i] > v2->ctx_prefix[i]) return (1); } if (v1->sec_model < v2->sec_model) return (-1); if (v1->sec_model > v2->sec_model) return (1); if (v1->sec_level < v2->sec_level) return (-1); if (v1->sec_level > v2->sec_level) return (1); return (0); } struct vacm_access * vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel) { struct vacm_group *group; struct vacm_access *acl, *temp; TAILQ_FOREACH(acl, &vacm_accesslist, vva) { if (acl->group == NULL) continue; if (strcmp(gname, acl->group->groupname) == 0 && strcmp(cprefix, acl->ctx_prefix) == 0 && acl->sec_model == smodel && acl->sec_level == slevel) return (NULL); } /* Make sure the group exists */ SLIST_FOREACH(group, &vacm_grouplist, vge) if (strcmp(gname, group->groupname) == 0) break; if (group == NULL) return (NULL); if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL) return (NULL); memset(acl, 0, sizeof(*acl)); acl->group = group; strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix)); acl->sec_model = smodel; acl->sec_level = slevel; if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL || vacm_compare_access_rule(acl, temp) < 0) { TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva); return (acl); } TAILQ_FOREACH(temp, &vacm_accesslist, vva) if (vacm_compare_access_rule(acl, temp) < 0) { TAILQ_INSERT_BEFORE(temp, acl, vva); return (acl); } TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva); return (acl); } int vacm_delete_access_rule(struct vacm_access *acl) { TAILQ_REMOVE(&vacm_accesslist, acl, vva); free(acl); return (0); } struct vacm_view * vacm_first_view(void) { return (SLIST_FIRST(&vacm_viewlist)); } struct vacm_view * vacm_next_view(struct vacm_view *view) { if (view == NULL) return (NULL); return (SLIST_NEXT(view, vvl)); } static int vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2) { uint32_t i; if (strlen(v1->viewname) < strlen(v2->viewname)) return (-1); if (strlen(v1->viewname) > strlen(v2->viewname)) return (1); for (i = 0; i < strlen(v1->viewname); i++) { if (v1->viewname[i] < v2->viewname[i]) return (-1); if (v1->viewname[i] > v2->viewname[i]) return (1); } return (asn_compare_oid(&v1->subtree, &v2->subtree)); } struct vacm_view * vacm_new_view(char *vname, struct asn_oid *oid) { int cmp; struct vacm_view *view, *temp, *prev; SLIST_FOREACH(view, &vacm_viewlist, vvl) if (strcmp(vname, view->viewname) == 0) return (NULL); if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL) return (NULL); memset(view, 0, sizeof(*view)); strlcpy(view->viewname, vname, sizeof(view->viewname)); asn_append_oid(&view->subtree, oid); if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL || vacm_compare_view(view, prev) < 0) { SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl); return (view); } SLIST_FOREACH(temp, &vacm_viewlist, vvl) { if ((cmp = vacm_compare_view(view, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, view, vvl); else if (cmp > 0) SLIST_INSERT_AFTER(temp, view, vvl); else { syslog(LOG_ERR, "View %s exists", view->viewname); free(view); return (NULL); } return (view); } int vacm_delete_view(struct vacm_view *view) { SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl); free(view); return (0); } struct vacm_context * vacm_first_context(void) { return (SLIST_FIRST(&vacm_contextlist)); } struct vacm_context * vacm_next_context(struct vacm_context *vacmctx) { if (vacmctx == NULL) return (NULL); return (SLIST_NEXT(vacmctx, vcl)); } struct vacm_context * vacm_add_context(char *ctxname, int regid) { int cmp; struct vacm_context *ctx, *temp, *prev; SLIST_FOREACH(ctx, &vacm_contextlist, vcl) if (strcmp(ctxname, ctx->ctxname) == 0) { syslog(LOG_ERR, "Context %s exists", ctx->ctxname); return (NULL); } if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL) return (NULL); memset(ctx, 0, sizeof(*ctx)); strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname)); ctx->regid = regid; if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL || strlen(ctx->ctxname) < strlen(prev->ctxname) || strcmp(ctx->ctxname, prev->ctxname) < 0) { SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl); return (ctx); } SLIST_FOREACH(temp, &vacm_contextlist, vcl) { if (strlen(ctx->ctxname) < strlen(temp->ctxname) || strcmp(ctx->ctxname, temp->ctxname) < 0) { cmp = -1; break; } prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, ctx, vcl); else if (cmp > 0) SLIST_INSERT_AFTER(temp, ctx, vcl); else { syslog(LOG_ERR, "Context %s exists", ctx->ctxname); free(ctx); return (NULL); } return (ctx); } void vacm_flush_contexts(int regid) { struct vacm_context *ctx, *temp; SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp) if (ctx->regid == regid) { SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl); free(ctx); } } Index: head/contrib/bsnmp/snmpd/snmpd.config =================================================================== --- head/contrib/bsnmp/snmpd/snmpd.config (revision 328159) +++ head/contrib/bsnmp/snmpd/snmpd.config (revision 328160) @@ -1,103 +1,104 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # # Author: Harti Brandt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Begemot: bsnmp/snmpd/snmpd.config,v 1.16 2006/02/14 09:04:20 brandt_h Exp $ # # Example configuration file. # # # Set some common variables # host := foo.bar.com location := "Room 200" contact := "sysmeister@bar.com" system := 1 # FreeBSD traphost := noc.bar.com trapport := 162 read := "public" # Uncomment the line below that sets the community string # to enable write access. write := "geheim" trap := "mytrap" # # Configuration # %snmpd begemotSnmpdDebugDumpPdus = 2 begemotSnmpdDebugSyslogPri = 7 # # Set the read and write communities. # # The default value of the community strings is NULL (note, that this is # different from the empty string). This disables both read and write access. # To enable read access only the read community string must be set. Setting # the write community string enables both read and write access with that # string. # # Be sure to understand the security implications of SNMPv2 - the community # strings are readable on the wire! # begemotSnmpdCommunityString.0.1 = $(read) # begemotSnmpdCommunityString.0.2 = $(write) +# begemotSnmpdCommunityString.0.3 = "otherPublic" begemotSnmpdCommunityDisable = 1 # open standard SNMP ports begemotSnmpdPortStatus.[$(host)].161 = 1 begemotSnmpdPortStatus.127.0.0.1.161 = 1 # open a unix domain socket begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1 begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4 # send traps to the traphost begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4 begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2 begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap) sysContact = $(contact) sysLocation = $(location) sysObjectId = 1.3.6.1.4.1.12325.1.1.2.1.$(system) snmpEnableAuthenTraps = 2 # # Load MIB-2 module # begemotSnmpdModulePath."mibII" = "/usr/local/lib/snmp_mibII.so" # # Netgraph module # begemotSnmpdModulePath."netgraph" = "/usr/local/lib/snmp_netgraph.so" %netgraph begemotNgControlNodeName = "snmpd" Index: head/contrib/bsnmp/snmpd/snmpmod.h =================================================================== --- head/contrib/bsnmp/snmpd/snmpmod.h (revision 328159) +++ head/contrib/bsnmp/snmpd/snmpmod.h (revision 328160) @@ -1,625 +1,627 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/snmpmod.h,v 1.32 2006/02/14 09:04:20 brandt_h Exp $ * * SNMP daemon data and functions exported to modules. */ #ifndef snmpmod_h_ #define snmpmod_h_ #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpagent.h" #define MAX_MOD_ARGS 16 /* * These macros help to handle object lists for SNMP tables. They use * tail queues to hold the objects in ascending order in the list. * ordering can be done either on an integer/unsigned field, an asn_oid * or an ordering function. */ #define INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if (asn_compare_oid(&_lelem->INDEX, &(PTR)->INDEX) > 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((asn_subid_t)_lelem->INDEX > (asn_subid_t)(PTR)->INDEX)\ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define INSERT_OBJECT_FUNC_LINK(PTR, LIST, LINK, FUNC) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(_lelem, (PTR)) > 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define INSERT_OBJECT_FUNC_LINK_REV(PTR, LIST, HEAD, LINK, FUNC) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH_REVERSE(_lelem, (LIST), HEAD, LINK) \ if ((FUNC)(_lelem, (PTR)) < 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_HEAD((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_AFTER((LIST), _lelem, (PTR), LINK); \ } while (0) #define FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if (index_compare(OID, SUB, &_lelem->INDEX) == 0) \ break; \ (_lelem); \ }) #define NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if (index_compare(OID, SUB, &_lelem->INDEX) < 0) \ break; \ (_lelem); \ }) #define FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ if ((OID)->len - SUB != 1) \ _lelem = NULL; \ else \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((OID)->subs[SUB] == (asn_subid_t)_lelem->INDEX)\ break; \ (_lelem); \ }) #define NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ if ((OID)->len - SUB == 0) \ _lelem = TAILQ_FIRST(LIST); \ else \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((OID)->subs[SUB] < (asn_subid_t)_lelem->INDEX)\ break; \ (_lelem); \ }) #define FIND_OBJECT_FUNC_LINK(LIST, OID, SUB, LINK, FUNC) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(OID, SUB, _lelem) == 0) \ break; \ (_lelem); \ }) #define NEXT_OBJECT_FUNC_LINK(LIST, OID, SUB, LINK, FUNC) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(OID, SUB, _lelem) < 0) \ break; \ (_lelem); \ }) /* * Macros for the case where the index field is called 'index' */ #define INSERT_OBJECT_OID_LINK(PTR, LIST, LINK) \ INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, index) #define INSERT_OBJECT_INT_LINK(PTR, LIST, LINK) do { \ INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, LINK, index) #define FIND_OBJECT_OID_LINK(LIST, OID, SUB, LINK) \ FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, index) #define NEXT_OBJECT_OID_LINK(LIST, OID, SUB, LINK) \ NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, index) #define FIND_OBJECT_INT_LINK(LIST, OID, SUB, LINK) \ FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, index) #define NEXT_OBJECT_INT_LINK(LIST, OID, SUB, LINK) \ NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, index) /* * Macros for the case where the index field is called 'index' and the * link field 'link'. */ #define INSERT_OBJECT_OID(PTR, LIST) \ INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, link, index) #define INSERT_OBJECT_INT(PTR, LIST) \ INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, link, index) #define INSERT_OBJECT_FUNC_REV(PTR, LIST, HEAD, FUNC) \ INSERT_OBJECT_FUNC_LINK_REV(PTR, LIST, HEAD, link, FUNC) #define FIND_OBJECT_OID(LIST, OID, SUB) \ FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, link, index) #define FIND_OBJECT_INT(LIST, OID, SUB) \ FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, link, index) #define FIND_OBJECT_FUNC(LIST, OID, SUB, FUNC) \ FIND_OBJECT_FUNC_LINK(LIST, OID, SUB, link, FUNC) #define NEXT_OBJECT_OID(LIST, OID, SUB) \ NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, link, index) #define NEXT_OBJECT_INT(LIST, OID, SUB) \ NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, link, index) #define NEXT_OBJECT_FUNC(LIST, OID, SUB, FUNC) \ NEXT_OBJECT_FUNC_LINK(LIST, OID, SUB, link, FUNC) struct lmodule; /* The tick when the program was started. This is the absolute time of * the start in 100th of a second. */ extern uint64_t start_tick; /* The tick when the current packet was received. This is the absolute * time in 100th of second. */ extern uint64_t this_tick; /* Get the current absolute time in 100th of a second. */ uint64_t get_ticks(void); /* * Return code for proxy function */ enum snmpd_proxy_err { /* proxy code will process the PDU */ SNMPD_PROXY_OK, /* proxy code does not process PDU */ SNMPD_PROXY_REJ, /* drop this PDU */ SNMPD_PROXY_DROP, /* drop because of bad community */ SNMPD_PROXY_BADCOMM, /* drop because of bad community use */ SNMPD_PROXY_BADCOMMUSE }; /* * Input handling */ enum snmpd_input_err { /* proceed with packet */ SNMPD_INPUT_OK, /* fatal error in packet, ignore it */ SNMPD_INPUT_FAILED, /* value encoding has wrong length in a SET operation */ SNMPD_INPUT_VALBADLEN, /* value encoding is out of range */ SNMPD_INPUT_VALRANGE, /* value has bad encoding */ SNMPD_INPUT_VALBADENC, /* need more data (truncated packet) */ SNMPD_INPUT_TRUNC, /* unknown community */ SNMPD_INPUT_BAD_COMM, }; /* * Every loadable module must have one of this structures with * the external name 'config'. */ struct snmp_module { /* a comment describing what this module implements */ const char *comment; /* the initialization function */ int (*init)(struct lmodule *, int argc, char *argv[]); /* the finalisation function */ int (*fini)(void); /* the idle function */ void (*idle)(void); /* the dump function */ void (*dump)(void); /* re-configuration function */ void (*config)(void); /* start operation */ void (*start)(void); /* proxy a PDU */ enum snmpd_proxy_err (*proxy)(struct snmp_pdu *, void *, const struct asn_oid *, const struct sockaddr *, socklen_t, enum snmpd_input_err, int32_t, int); /* the tree this module is going to server */ const struct snmp_node *tree; u_int tree_size; /* function called, when another module was unloaded/loaded */ void (*loading)(const struct lmodule *, int); }; /* * Stuff exported to modules */ /* * The system group. */ struct systemg { u_char *descr; struct asn_oid object_id; u_char *contact; u_char *name; u_char *location; uint32_t services; uint32_t or_last_change; }; extern struct systemg systemg; /* * Community support. * * We have 2 fixed communities for SNMP read and write access. Modules * can create their communities dynamically. They are deleted automatically * if the module is unloaded. */ #define COMM_INITIALIZE 0 #define COMM_READ 1 #define COMM_WRITE 2 u_int comm_define(u_int, const char *descr, struct lmodule *, const char *str); +struct community *comm_define_ordered(u_int priv, const char *descr, + struct asn_oid *index, struct lmodule *owner, const char *str); const char * comm_string(u_int); /* community for current packet */ extern u_int community; /* * SNMP User-based Security Model data. Modified via the snmp_usm(3) module. */ struct snmpd_usmstat { uint32_t unsupported_seclevels; uint32_t not_in_time_windows; uint32_t unknown_users; uint32_t unknown_engine_ids; uint32_t wrong_digests; uint32_t decrypt_errors; }; extern struct snmpd_usmstat snmpd_usmstats; struct snmpd_usmstat *bsnmpd_get_usm_stats(void); void bsnmpd_reset_usm_stats(void); struct usm_user { struct snmp_user suser; uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t user_engine_len; char user_public[SNMP_ADM_STR32_SIZ]; uint32_t user_public_len; int32_t status; int32_t type; SLIST_ENTRY(usm_user) up; }; SLIST_HEAD(usm_userlist, usm_user); struct usm_user *usm_first_user(void); struct usm_user *usm_next_user(struct usm_user *); struct usm_user *usm_find_user(uint8_t *, uint32_t, char *); struct usm_user *usm_new_user(uint8_t *, uint32_t, char *); void usm_delete_user(struct usm_user *); void usm_flush_users(void); /* USM user for current packet */ extern struct usm_user *usm_user; /* * SNMP View-based Access Control Model data. Modified via the snmp_vacm(3) module. */ struct vacm_group; struct vacm_user { /* Security user name from USM */ char secname[SNMP_ADM_STR32_SIZ]; int32_t sec_model; /* Back pointer to user assigned group name */ struct vacm_group *group; int32_t type; int32_t status; SLIST_ENTRY(vacm_user) vvu; SLIST_ENTRY(vacm_user) vvg; }; SLIST_HEAD(vacm_userlist, vacm_user); struct vacm_group { char groupname[SNMP_ADM_STR32_SIZ]; struct vacm_userlist group_users; SLIST_ENTRY(vacm_group) vge; }; SLIST_HEAD(vacm_grouplist, vacm_group); struct vacm_access { /* The group name is index, not a column in the table */ struct vacm_group *group; char ctx_prefix[SNMP_ADM_STR32_SIZ]; int32_t sec_model; int32_t sec_level; int32_t ctx_match; struct vacm_view *read_view; struct vacm_view *write_view; struct vacm_view *notify_view; int32_t type; int32_t status; TAILQ_ENTRY(vacm_access) vva; }; TAILQ_HEAD(vacm_accesslist, vacm_access); struct vacm_view { char viewname[SNMP_ADM_STR32_SIZ]; /* key */ struct asn_oid subtree; /* key */ uint8_t mask[16]; uint8_t exclude; int32_t type; int32_t status; SLIST_ENTRY(vacm_view) vvl; }; SLIST_HEAD(vacm_viewlist, vacm_view); struct vacm_context { /* The ID of the module that registered this context */ int32_t regid; char ctxname[SNMP_ADM_STR32_SIZ]; SLIST_ENTRY(vacm_context) vcl; }; SLIST_HEAD(vacm_contextlist, vacm_context); void vacm_groups_init(void); struct vacm_user *vacm_first_user(void); struct vacm_user *vacm_next_user(struct vacm_user *); struct vacm_user *vacm_new_user(int32_t, char *); int vacm_delete_user(struct vacm_user *); int vacm_user_set_group(struct vacm_user *, u_char *, u_int); struct vacm_access *vacm_first_access_rule(void); struct vacm_access *vacm_next_access_rule(struct vacm_access *); struct vacm_access *vacm_new_access_rule(char *, char *, int32_t, int32_t); int vacm_delete_access_rule(struct vacm_access *); struct vacm_view *vacm_first_view(void); struct vacm_view *vacm_next_view(struct vacm_view *); struct vacm_view *vacm_new_view(char *, struct asn_oid *); int vacm_delete_view(struct vacm_view *); struct vacm_context *vacm_first_context(void); struct vacm_context *vacm_next_context(struct vacm_context *); struct vacm_context *vacm_add_context(char *, int32_t); void vacm_flush_contexts(int32_t); /* * RFC 3413 SNMP Management Target & Notification MIB */ struct snmpd_target_stats { uint32_t unavail_contexts; uint32_t unknown_contexts; }; #define SNMP_UDP_ADDR_SIZ 6 #define SNMP_TAG_SIZ (255 + 1) struct target_address { char name[SNMP_ADM_STR32_SIZ]; uint8_t address[SNMP_UDP_ADDR_SIZ]; int32_t timeout; int32_t retry; char taglist[SNMP_TAG_SIZ]; char paramname[SNMP_ADM_STR32_SIZ]; int32_t type; int32_t socket; int32_t status; SLIST_ENTRY(target_address) ta; }; SLIST_HEAD(target_addresslist, target_address); struct target_param { char name[SNMP_ADM_STR32_SIZ]; int32_t mpmodel; int32_t sec_model; char secname[SNMP_ADM_STR32_SIZ]; enum snmp_usm_level sec_level; int32_t type; int32_t status; SLIST_ENTRY(target_param) tp; }; SLIST_HEAD(target_paramlist, target_param); struct target_notify { char name[SNMP_ADM_STR32_SIZ]; char taglist[SNMP_TAG_SIZ]; int32_t notify_type; int32_t type; int32_t status; SLIST_ENTRY(target_notify) tn; }; SLIST_HEAD(target_notifylist, target_notify); extern struct snmpd_target_stats snmpd_target_stats; struct snmpd_target_stats *bsnmpd_get_target_stats(void); struct target_address *target_first_address(void); struct target_address *target_next_address(struct target_address *); struct target_address *target_new_address(char *); int target_activate_address(struct target_address *); int target_delete_address(struct target_address *); struct target_param *target_first_param(void); struct target_param *target_next_param(struct target_param *); struct target_param *target_new_param(char *); int target_delete_param(struct target_param *); struct target_notify *target_first_notify(void); struct target_notify *target_next_notify(struct target_notify *); struct target_notify *target_new_notify(char *); int target_delete_notify (struct target_notify *); void target_flush_all(void); /* * Well known OIDs */ extern const struct asn_oid oid_zeroDotZero; /* SNMPv3 Engine Discovery */ extern const struct asn_oid oid_usmUnknownEngineIDs; extern const struct asn_oid oid_usmNotInTimeWindows; /* * Request ID ranges. * * A module can request a range of request ids and associate them with a * type field. All ranges are deleted if a module is unloaded. */ u_int reqid_allocate(int size, struct lmodule *); int32_t reqid_next(u_int type); int32_t reqid_base(u_int type); int reqid_istype(int32_t reqid, u_int type); u_int reqid_type(int32_t reqid); /* * Timers. */ void *timer_start(u_int, void (*)(void *), void *, struct lmodule *); void *timer_start_repeat(u_int, u_int, void (*)(void *), void *, struct lmodule *); void timer_stop(void *); /* * File descriptors */ void *fd_select(int, void (*)(int, void *), void *, struct lmodule *); void fd_deselect(void *); void fd_suspend(void *); int fd_resume(void *); /* * Object resources */ u_int or_register(const struct asn_oid *, const char *, struct lmodule *); void or_unregister(u_int); /* * Buffers */ void *buf_alloc(int tx); size_t buf_size(int tx); /* decode PDU and find community */ enum snmpd_input_err snmp_input_start(const u_char *, size_t, const char *, struct snmp_pdu *, int32_t *, size_t *); /* process the pdu. returns either _OK or _FAILED */ enum snmpd_input_err snmp_input_finish(struct snmp_pdu *, const u_char *, size_t, u_char *, size_t *, const char *, enum snmpd_input_err, int32_t, void *); void snmp_output(struct snmp_pdu *, u_char *, size_t *, const char *); void snmp_send_port(void *, const struct asn_oid *, struct snmp_pdu *, const struct sockaddr *, socklen_t); enum snmp_code snmp_pdu_auth_access(struct snmp_pdu *, int32_t *); /* sending traps */ void snmp_send_trap(const struct asn_oid *, ...); /* * Action support */ int string_save(struct snmp_value *, struct snmp_context *, ssize_t, u_char **); void string_commit(struct snmp_context *); void string_rollback(struct snmp_context *, u_char **); int string_get(struct snmp_value *, const u_char *, ssize_t); int string_get_max(struct snmp_value *, const u_char *, ssize_t, size_t); void string_free(struct snmp_context *); int ip_save(struct snmp_value *, struct snmp_context *, u_char *); void ip_rollback(struct snmp_context *, u_char *); void ip_commit(struct snmp_context *); int ip_get(struct snmp_value *, u_char *); int oid_save(struct snmp_value *, struct snmp_context *, struct asn_oid *); void oid_rollback(struct snmp_context *, struct asn_oid *); void oid_commit(struct snmp_context *); int oid_get(struct snmp_value *, const struct asn_oid *); int index_decode(const struct asn_oid *oid, u_int sub, u_int code, ...); int index_compare(const struct asn_oid *, u_int, const struct asn_oid *); int index_compare_off(const struct asn_oid *, u_int, const struct asn_oid *, u_int); void index_append(struct asn_oid *, u_int, const struct asn_oid *); void index_append_off(struct asn_oid *, u_int, const struct asn_oid *, u_int); #endif Index: head/contrib/bsnmp/snmpd/tree.def =================================================================== --- head/contrib/bsnmp/snmpd/tree.def (revision 328159) +++ head/contrib/bsnmp/snmpd/tree.def (revision 328160) @@ -1,222 +1,223 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # # Author: Harti Brandt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Begemot: tree.def 517 2006-10-31 08:52:04Z brandt_h $ # # System group and private Begemot SNMPd MIB. # #include "tc.def" typedef RowStatus ENUM ( 1 active 2 notInService 3 notReady 4 createAndGo 5 createAndWait 6 destroy ) (1 internet (2 mgmt (1 mibII (1 system # # The standard System group # (1 sysDescr OCTETSTRING op_system_group GET) (2 sysObjectId OID op_system_group GET) (3 sysUpTime TIMETICKS op_system_group GET) (4 sysContact OCTETSTRING op_system_group GET SET) (5 sysName OCTETSTRING op_system_group GET SET) (6 sysLocation OCTETSTRING op_system_group GET SET) (7 sysServices INTEGER op_system_group GET) (8 sysORLastChange TIMETICKS op_system_group GET) (9 sysORTable (1 sysOREntry : INTEGER op_or_table (1 sysORIndex INTEGER) (2 sysORID OID GET) (3 sysORDescr OCTETSTRING GET) (4 sysORUpTime TIMETICKS GET) )) ) (11 snmp (1 snmpInPkts COUNTER op_snmp GET) (3 snmpInBadVersions COUNTER op_snmp GET) (4 snmpInBadCommunityNames COUNTER op_snmp GET) (5 snmpInBadCommunityUses COUNTER op_snmp GET) (6 snmpInASNParseErrs COUNTER op_snmp GET) (30 snmpEnableAuthenTraps INTEGER op_snmp GET SET) (31 snmpSilentDrops COUNTER op_snmp GET) (32 snmpProxyDrops COUNTER op_snmp GET) ) )) (4 private (1 enterprises # # FreeBSD stuff # (2238 freeBSD (4 freeBSDVersion) ) # # Private Begemot Stuff # (12325 fokus (1 begemot # # Daemon infrastructure # (1 begemotSnmpd (1 begemotSnmpdObjects # # Configuration # (1 begemotSnmpdConfig (1 begemotSnmpdTransmitBuffer INTEGER op_snmpd_config GET SET) (2 begemotSnmpdReceiveBuffer INTEGER op_snmpd_config GET SET) (3 begemotSnmpdCommunityDisable INTEGER op_snmpd_config GET SET) (4 begemotSnmpdTrap1Addr IPADDRESS op_snmpd_config GET SET) (5 begemotSnmpdVersionEnable UNSIGNED32 op_snmpd_config GET SET) ) (2 begemotTrapSinkTable (1 begemotTrapSinkEntry : IPADDRESS INTEGER op_trapsink (1 begemotTrapSinkAddr IPADDRESS) (2 begemotTrapSinkPort INTEGER) (3 begemotTrapSinkStatus INTEGER GET SET) (4 begemotTrapSinkComm OCTETSTRING GET SET) (5 begemotTrapSinkVersion INTEGER GET SET) ) ) # # Port table # (4 begemotSnmpdPortTable (1 begemotSnmpdPortEntry : IPADDRESS INTEGER op_snmp_port (1 begemotSnmpdPortAddress IPADDRESS) (2 begemotSnmpdPortPort UNSIGNED32) (3 begemotSnmpdPortStatus INTEGER GET SET) )) # # Community table # (5 begemotSnmpdCommunityTable (1 begemotSnmpdCommunityEntry : OCTETSTRING UNSIGNED32 op_community (1 begemotSnmpdCommunityModule OCTETSTRING) (2 begemotSnmpdCommunityIndex UNSIGNED32) (3 begemotSnmpdCommunityString OCTETSTRING GET SET) (4 begemotSnmpdCommunityDescr OCTETSTRING GET) + (5 begemotSnmpdCommunityPermission INTEGER GET SET) )) # # Module table # (6 begemotSnmpdModuleTable (1 begemotSnmpdModuleEntry : OCTETSTRING op_modules (1 begemotSnmpdModuleSection OCTETSTRING) (2 begemotSnmpdModulePath OCTETSTRING GET SET) (3 begemotSnmpdModuleComment OCTETSTRING GET) )) # # Statistics # (7 begemotSnmpdStats (1 begemotSnmpdStatsNoRxBufs COUNTER op_snmpd_stats GET) (2 begemotSnmpdStatsNoTxBufs COUNTER op_snmpd_stats GET) (3 begemotSnmpdStatsInTooLongPkts COUNTER op_snmpd_stats GET) (4 begemotSnmpdStatsInBadPduTypes COUNTER op_snmpd_stats GET)) # # Debugging # (8 begemotSnmpdDebug (1 begemotSnmpdDebugDumpPdus INTEGER op_debug GET SET) (2 begemotSnmpdDebugSnmpTrace UNSIGNED32 op_debug GET SET) (3 begemotSnmpdDebugSyslogPri INTEGER op_debug GET SET)) # # Local (UNIX domain) port table # (9 begemotSnmpdLocalPortTable (1 begemotSnmpdLocalPortEntry : OCTETSTRING op_lsock_port (1 begemotSnmpdLocalPortPath OCTETSTRING) (2 begemotSnmpdLocalPortStatus INTEGER GET SET) (3 begemotSnmpdLocalPortType INTEGER GET SET) )) (10 begemotSnmpdTransportMappings (1 begemotSnmpdTransportTable (1 begemotSnmpdTransportEntry : OCTETSTRING op_transport_table (1 begemotSnmpdTransportName OCTETSTRING) (2 begemotSnmpdTransportStatus INTEGER GET) (3 begemotSnmpdTransportOid OID GET) )) (2 begemotSnmpdTransUdp OID op_transport_dummy) (3 begemotSnmpdTransLsock OID op_transport_dummy) ) ) (2 begemotSnmpdDefs (1 begemotSnmpdAgent (1 begemotSnmpdAgentFreeBSD OID op_dummy) ) ) ) )) ) ) (6 snmpV2 (3 snmpModules (1 snmpMIB (1 snmpMIBObjects (4 snmpTrap (1 snmpTrapOID OID op_snmp_trap) ) (5 snmpTraps (1 coldStart OID op_snmp_trap) (2 warmStart OID op_snmp_trap) (5 authenticationFailure OID op_snmp_trap) ) (6 snmpSet (1 snmpSetSerialNo INTEGER op_snmp_set GET SET) ) ) ) (10 snmpFrameworkMIB (2 snmpFrameworkMIBObjects (1 snmpEngine (1 snmpEngineID OCTETSTRING | SnmpEngineID op_snmp_engine GET) (2 snmpEngineBoots INTEGER op_snmp_engine GET) (3 snmpEngineTime INTEGER op_snmp_engine GET) (4 snmpEngineMaxMessageSize INTEGER op_snmp_engine GET) ) ) ) )) )