Index: stable/10/contrib/bsnmp/snmp_usm/usm_snmp.c =================================================================== --- stable/10/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 300560) +++ stable/10/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 300561) @@ -1,614 +1,614 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "usm_tree.h" #include "usm_oid.h" static struct lmodule *usm_module; /* For the registration. */ static const struct asn_oid oid_usm = OIDX_snmpUsmMIB; static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol; static const struct asn_oid oid_usmHMACMD5AuthProtocol = \ OIDX_usmHMACMD5AuthProtocol; static const struct asn_oid oid_usmHMACSHAAuthProtocol = \ OIDX_usmHMACSHAAuthProtocol; static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol; static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol; static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol; static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName; /* The registration. */ static uint reg_usm; static int32_t usm_lock; static struct usm_user * usm_get_user(const struct asn_oid *, uint); static struct usm_user * usm_get_next_user(const struct asn_oid *, uint); static void usm_append_userindex(struct asn_oid *, uint, const struct usm_user *); static int usm_user_index_decode(const struct asn_oid *, uint, uint8_t *, uint32_t *, char *); int op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_usmstat *usmstats; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((usmstats = bsnmpd_get_usm_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_usmStatsUnsupportedSecLevels: val->v.uint32 = usmstats->unsupported_seclevels; break; case LEAF_usmStatsNotInTimeWindows: val->v.uint32 = usmstats->not_in_time_windows; break; case LEAF_usmStatsUnknownUserNames: val->v.uint32 = usmstats->unknown_users; break; case LEAF_usmStatsUnknownEngineIDs: val->v.uint32 = usmstats->unknown_engine_ids; break; case LEAF_usmStatsWrongDigests: val->v.uint32 = usmstats->wrong_digests; break; case LEAF_usmStatsDecryptionErrors: val->v.uint32 = usmstats->decrypt_errors; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (++usm_lock == INT32_MAX) usm_lock = 0; val->v.integer = usm_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != usm_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } int op_usm_users(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t elen; struct usm_user *uuser, *clone; char uname[SNMP_ADM_STR32_SIZ]; uint8_t eid[SNMP_ENGINE_ID_SIZ]; switch (op) { case SNMP_OP_GET: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((uuser = usm_get_next_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); usm_append_userindex(&val->var, sub, uuser); break; case SNMP_OP_SET: if ((uuser = usm_get_user(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_usmUserStatus && val->var.subs[sub - 1] != LEAF_usmUserCloneFrom) return (SNMP_ERR_NOSUCHNAME); if (community != COMM_INITIALIZE && uuser->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_usmUserCloneFrom: if (uuser != NULL || usm_user_index_decode(&val->var, sub, eid, &elen, uname) < 0 || !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid))) return (SNMP_ERR_WRONG_VALUE); if ((clone = usm_get_user(&val->v.oid, sub)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((uuser = usm_new_user(eid, elen, uname)) == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; uuser->suser.auth_proto = clone->suser.auth_proto; uuser->suser.priv_proto = clone->suser.priv_proto; memcpy(uuser->suser.auth_key, clone->suser.auth_key, sizeof(uuser->suser.auth_key)); memcpy(uuser->suser.priv_key, clone->suser.priv_key, sizeof(uuser->suser.priv_key)); ctx->scratch->int1 = RowStatus_createAndWait; break; case LEAF_usmUserAuthProtocol: ctx->scratch->int1 = uuser->suser.auth_proto; if (asn_compare_oid(&oid_usmNoAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_NOAUTH; else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5; else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnAuthKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.auth_key, SNMP_AUTH_KEY_SIZ); memcpy(uuser->suser.auth_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPrivProtocol: ctx->scratch->int1 = uuser->suser.priv_proto; if (asn_compare_oid(&oid_usmNoPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_NOPRIV; else if (asn_compare_oid(&oid_usmDESPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_DES; else if (asn_compare_oid(&oid_usmAesCfb128Protocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_AES; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnPrivKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.priv_key, - SNMP_PRIV_KEY_SIZ); + sizeof(uuser->suser.priv_key)); memcpy(uuser->suser.priv_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPublic: if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); if (uuser->user_public_len > 0) { ctx->scratch->ptr2 = malloc(uuser->user_public_len); if (ctx->scratch->ptr2 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr2, uuser->user_public, uuser->user_public_len); ctx->scratch->int2 = uuser->user_public_len; } if (val->v.octetstring.len > 0) { memcpy(uuser->user_public, val->v.octetstring.octets, val->v.octetstring.len); uuser->user_public_len = val->v.octetstring.len; } else { memset(uuser->user_public, 0, - SNMP_ADM_STR32_SIZ); + sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_usmUserStatus: if (uuser == NULL) { if (val->v.integer != RowStatus_createAndWait || usm_user_index_decode(&val->var, sub, eid, &elen, uname) < 0) return (SNMP_ERR_INCONS_VALUE); uuser = usm_new_user(eid, elen, uname); if (uuser == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); uuser->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) free(ctx->scratch->ptr2); break; case LEAF_usmUserStatus: if (val->v.integer != RowStatus_destroy) break; if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); usm_delete_user(uuser); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthProtocol: uuser->suser.auth_proto = ctx->scratch->int1; break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: memcpy(uuser->suser.auth_key, ctx->scratch->ptr1, - SNMP_AUTH_KEY_SIZ); + sizeof(uuser->suser.auth_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPrivProtocol: uuser->suser.priv_proto = ctx->scratch->int1; break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: memcpy(uuser->suser.priv_key, ctx->scratch->ptr1, - SNMP_AUTH_KEY_SIZ); + sizeof(uuser->suser.priv_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) { memcpy(uuser->user_public, ctx->scratch->ptr2, ctx->scratch->int2); uuser->user_public_len = ctx->scratch->int2; free(ctx->scratch->ptr2); } else { memset(uuser->user_public, 0, - SNMP_ADM_STR32_SIZ); + sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserCloneFrom: case LEAF_usmUserStatus: if (ctx->scratch->int1 == RowStatus_createAndWait) usm_delete_user(uuser); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (string_get(val, uuser->suser.sec_name, -1)); case LEAF_usmUserCloneFrom: memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero)); break; case LEAF_usmUserAuthProtocol: switch (uuser->suser.auth_proto) { case SNMP_AUTH_HMAC_MD5: memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol, sizeof(oid_usmHMACMD5AuthProtocol)); break; case SNMP_AUTH_HMAC_SHA: memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol, sizeof(oid_usmHMACSHAAuthProtocol)); break; default: memcpy(&val->v.oid, &oid_usmNoAuthProtocol, sizeof(oid_usmNoAuthProtocol)); break; } break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: return (string_get(val, (char *)uuser->suser.auth_key, 0)); case LEAF_usmUserPrivProtocol: switch (uuser->suser.priv_proto) { case SNMP_PRIV_DES: memcpy(&val->v.oid, &oid_usmDESPrivProtocol, sizeof(oid_usmDESPrivProtocol)); break; case SNMP_PRIV_AES: memcpy(&val->v.oid, &oid_usmAesCfb128Protocol, sizeof(oid_usmAesCfb128Protocol)); break; default: memcpy(&val->v.oid, &oid_usmNoPrivProtocol, sizeof(oid_usmNoPrivProtocol)); break; } break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: return (string_get(val, (char *)uuser->suser.priv_key, 0)); case LEAF_usmUserPublic: return (string_get(val, uuser->user_public, uuser->user_public_len)); case LEAF_usmUserStorageType: val->v.integer = uuser->type; break; case LEAF_usmUserStatus: val->v.integer = uuser->status; break; } return (SNMP_ERR_NOERROR); } static int usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine, uint32_t *elen, char *uname) { uint32_t i, nlen; int uname_off; if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) engine[i] = oid->subs[sub + i + 1]; *elen = i; uname_off = sub + oid->subs[sub] + 1; if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < nlen; i++) uname[i] = oid->subs[uname_off + i + 1]; uname[nlen] = '\0'; return (0); } static void usm_append_userindex(struct asn_oid *oid, uint sub, const struct usm_user *uuser) { uint32_t i; oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name); oid->len += 2; oid->subs[sub] = uuser->user_engine_len; for (i = 1; i < uuser->user_engine_len + 1; i++) oid->subs[sub + i] = uuser->user_engine_id[i - 1]; sub += uuser->user_engine_len + 1; oid->subs[sub] = strlen(uuser->suser.sec_name); for (i = 1; i <= oid->subs[sub]; i++) oid->subs[sub + i] = uuser->suser.sec_name[i - 1]; } static struct usm_user * usm_get_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); return (usm_find_user(engineid, enginelen, username)); } static struct usm_user * usm_get_next_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; struct usm_user *uuser; if (oid->len - sub == 0) return (usm_first_user()); if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL) return (usm_next_user(uuser)); return (NULL); } /* * USM snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) { usm_module = mod; usm_lock = random(); bsnmpd_reset_usm_stats(); return (0); } /* * USM snmp module finalization hook. */ static int usm_fini(void) { usm_flush_users(); or_unregister(reg_usm); return (0); } /* * USM snmp module start operation. */ static void usm_start(void) { reg_usm = or_register(&oid_usm, "The MIB module for managing SNMP User-Based Security Model.", usm_module); } static void usm_dump(void) { struct usm_user *uuser; struct snmpd_usmstat *usmstats; const char *const authstr[] = { "noauth", "md5", "sha", NULL }; const char *const privstr[] = { "nopriv", "des", "aes", NULL }; if ((usmstats = bsnmpd_get_usm_stats()) != NULL) { syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u", usmstats->unsupported_seclevels); syslog(LOG_ERR, "NotInTimeWindows\t\t%u", usmstats->not_in_time_windows); syslog(LOG_ERR, "UnknownUserNames\t\t%u", usmstats->unknown_users); syslog(LOG_ERR, "UnknownEngineIDs\t\t%u", usmstats->unknown_engine_ids); syslog(LOG_ERR, "WrongDigests\t\t%u", usmstats->wrong_digests); syslog(LOG_ERR, "DecryptionErrors\t\t%u", usmstats->decrypt_errors); } syslog(LOG_ERR, "USM users"); for (uuser = usm_first_user(); uuser != NULL; (uuser = usm_next_user(uuser))) syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name, authstr[uuser->suser.auth_proto], privstr[uuser->suser.priv_proto]); } const char usm_comment[] = \ "This module implements SNMP User-based Security Model defined in RFC 3414."; const struct snmp_module config = { .comment = usm_comment, .init = usm_init, .fini = usm_fini, .start = usm_start, .tree = usm_ctree, .dump = usm_dump, .tree_size = usm_CTREE_SIZE, }; Index: stable/10/contrib/bsnmp/snmpd/bsnmpd.1 =================================================================== --- stable/10/contrib/bsnmp/snmpd/bsnmpd.1 (revision 300560) +++ stable/10/contrib/bsnmp/snmpd/bsnmpd.1 (revision 300561) @@ -1,280 +1,280 @@ .\" .\" Copyright (c) 2004-2005 .\" Hartmut Brandt. .\" All rights reserved. .\" 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/bsnmpd.1,v 1.12 2006/02/27 09:50:03 brandt_h Exp $ .\" .Dd September 9, 2010 .Dt BSNMPD 1 .Os .Sh NAME .Nm bsnmpd .Nd "simple and extensible SNMP daemon" .Sh SYNOPSIS .Nm .Op Fl dh .Op Fl c Ar file .Op Fl D Ar options .Op Fl e Ar file .Op Fl I Ar paths .Op Fl l Ar prefix .Op Fl m Ar variable Ns Op = Ns Ar value .Op Fl p Ar file .Sh DESCRIPTION The .Nm daemon serves the internet SNMP (Simple Network Management Protocol). It is intended to serve only the absolute basic MIBs and implement all other MIBs through loadable modules. In this way the .Nm can be used in unexpected ways. .Pp The options are as follows: .Bl -tag -width ".It Fl D Ar options" .It Fl d Do not daemonize. Used for debugging. .It Fl h Print a short usage message. .It Fl c Ar file Use .Ar file as configuration file instead of the standard one. .It Fl D Ar options Debugging options are specified with a .Fl o flag followed by a comma separated string of options. The following options are available. .Bl -tag -width ".It Cm trace Ns Cm = Ns Cm level" .It Cm dump Dump all sent and received PDUs to the terminal. .It Cm events Set the debugging level of the event library (see .Xr eventlib 3 ) to 10. .It Cm trace Ns Cm = Ns Cm level Set the snmp library trace flag to the specified value. .El The value can be specified in the usual C-syntax for numbers. .It Fl e Ar file Specify an alternate file where the agent's engine id and number of boots are saved. .It Fl I Ar paths Specify a colon separated list of directories to search for configuration include files. The default is .Pa /etc:/usr/etc/:/usr/local/etc . These paths are only searched for include specified within <> parentheses. .It Fl l Ar prefix Use .Ar prefix as the default basename for the pid and the configuration files. .It Fl m Ar variable Ns Op = Ns Ar value Define a configuration variable. .It Fl p Ar file Specify an alternate pid file instead of the default one. .El .Sh CONFIGURATION .Nm reads its configuration from either the default or the user specified configuration file. The configuration file consists of the following types of lines: .Bl -bullet -offset indent .It variable assignments .It section separators .It include directives .It MIB variable assignments .El .Pp If a line is too long it can be continued on the next line by ending it with a backslash. Empty lines and lines in which the first non-blank character is a .Dq # sign are ignored. .Pp All MIB variable assignments of the entire configuration (including nested configuration files) are handled as one transaction, i.e., as if they arrived in a single SET PDU. Any failure during the initial configuration read causes .Nm to exit. A failure during the configuration read caused by a module load causes the loading of the module to fail. .Pp The configuration is read during initialization of .Nm , when a module is loaded and when .Nm receives a SIGHUP. .Ss VARIABLE ASSIGNMENTS Variable assignments can take one of two forms: .Bd -unfilled -offset indent variable := string variable ?= string .Ed .Pp The string reaches from the first non-blank character after the equal sign until the first new line or .Dq # character. In the first case the string is assigned to the variable unconditionally, in the second case the variable is only assigned if it does not exist yet. .Pp Variable names must begin with a letter or underscore and contain only letters, digits or underscores. .Ss SECTION SEPARATORS The configuration consists of named sections. The MIB variable assignments in the section named .Dq snmpd are executed only during initial setup or when .Nm receives a SIGHUP. All other sections are executed when either a module with the same name as the section is loaded or .Nm receives a SIGHUP and that module is already loaded. The default section at the start of the configuration is .Dq snmpd . One can switch to another section with the syntax .Bd -unfilled -offset indent %secname .Ed .Pp Where .Ar secname is the name of the section. The same .Ar secname can be used in more than one place in the configuration. All of these parts are collected into one section. .Ss INCLUDE DIRECTIVES Another configuration file can be included into the current one with the include directive that takes one of two forms: .Bd -unfilled -offset indent \&.include "file" \&.include <"file"> .Ed .Pp The first form causes the file to be searched in the current directory, the second form causes the file to be searched in the directories specified in the system include path. Nesting depth is only restricted by available memory. .Ss MIB VARIABLE ASSIGNMENTS A MIB variable is assigned with the syntax .Bd -unfilled -offset indent oid [ suboids ] = value .Ed .Pp .Va oid is the name of the variable to be set. Only the last component of the entire name is used here. If the variable is a scalar, the index (.0) is automatically appended and need not to be specified. If the variable is a table column, the index .Pq Va suboids must be specified. The index consist of elements each separated from the previous one by a dot. Elements may be either numbers, strings or hostnames enclosed in [] brackets. If the element is a number it is appended to the current oid. If the element is a string, its length and the .Tn ASCII code of each of its characters are appended to the current oid. If the element is a hostname, the IP address of the host is looked up and the four elements of the IP address are appended to the oid. .Pp For example, an oid of .Bd -unfilled -offset indent myvariable.27.foooll.[localhost]."&^!" .Ed .Pp results in the oid .Bd -unfilled -offset indent myvariable.27.6.102.111.111.111.108.108.127.0.0.1.38.94.33 .Ed .Pp The value of the assignment may be either empty, a string or a number. If a string starts with a letter or an underscore and consists only of letters, digits, underscores and minus signs, it can be written without quotes. In all other cases the string must be enclosed in double quotes. .Sh SUBSTITUTIONS A variable substitution is written as .Bd -unfilled -offset indent $(variable) .Ed .Pp where .Ar variable is the name of the variable to substitute. Using an undefined variable is considered an error. .Sh FILES .Bl -tag -width ".It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid" -compact .It Pa /etc/ Ns Ao Ar prefix Ac Ns \&.config Default configuration file, where the default .Aq prefix is .Dq snmpd . .It Pa /var/ Ns Ao Ar prefix Ac Ns \&.engine Default engine id file. .It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid Default pid file. .It Pa /etc:/usr/etc/:/usr/local/etc Default search path for system include files. .It Pa @MIBSPATH@FOKUS-MIB.txt .It Pa @MIBSPATH@BEGEMOT-MIB.txt .It Pa @MIBSPATH@BEGEMOT-SNMPD.txt Definitions for the MIBs implemented in the daemon. .It Pa /etc/hosts.allow, /etc/hosts.deny Access controls that should be enforced by TCP wrappers are defined here. Further details are described in .Xr hosts_access 5 . .El .Sh SEE ALSO .Xr gensnmptree 1 , .Xr hosts_access 5 .Sh STANDARDS The .Nm conforms to the applicable IETF RFCs. .Sh AUTHORS .An Hartmut Brandt Aq harti@FreeBSD.org .Sh BUGS Sure. Index: stable/10/contrib/bsnmp/snmpd/config.c =================================================================== --- stable/10/contrib/bsnmp/snmpd/config.c (revision 300560) +++ stable/10/contrib/bsnmp/snmpd/config.c (revision 300561) @@ -1,1382 +1,1383 @@ /* * 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/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $ * * Parse configuration file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "tree.h" /* #define DEBUGGING */ /* * config_file: EMPTY | config_file line * * line: oid '=' value * | '%' STRING * | STRING := REST_OF_LINE * | STRING ?= REST_OF_LINE * | . INCLUDE STRING * * oid: STRING suboid * * suboid: EMPTY | suboid '.' subid * * subid: NUM | STRING | '[' STRING ']' * * value: EMPTY | STRING | NUM */ /* * Input context for macros and includes */ enum input_type { INPUT_FILE = 1, INPUT_STRING }; struct input { enum input_type type; union { struct { FILE *fp; char *filename; u_int lno; } file; struct { char *macro; char *str; char *ptr; size_t left; } str; } u; LIST_ENTRY(input) link; }; static LIST_HEAD(, input) inputs; #define input_fp u.file.fp #define input_filename u.file.filename #define input_lno u.file.lno #define input_macro u.str.macro #define input_str u.str.str #define input_ptr u.str.ptr #define input_left u.str.left static int input_push; static int input_buf[2]; /* * Configuration data. The configuration file is handled as one single * SNMP transaction. So we need to keep the assignment data for the * commit or rollback pass. Note, that dependencies and finish functions * are NOT allowed here. */ struct assign { struct snmp_value value; struct snmp_scratch scratch; const char *node_name; TAILQ_ENTRY(assign) link; }; static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns); static struct snmp_context *snmp_ctx; struct macro { char *name; char *value; size_t length; LIST_ENTRY(macro) link; int perm; }; static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros); enum { TOK_EOF = 0200, TOK_EOL, TOK_NUM, TOK_STR, TOK_HOST, TOK_ASSIGN, TOK_QASSIGN, }; /* lexer values and last token */ static uint64_t numval; static char strval[_POSIX2_LINE_MAX]; static size_t strvallen; static int token; /* error return */ static jmp_buf errjmp[4]; static volatile int errstk; # define ERRPUSH() (setjmp(errjmp[errstk++])) # define ERRPOP() ((void)(errstk--)) # define ERRNEXT() (longjmp(errjmp[--errstk], 1)) # define ERR() (longjmp(errjmp[--errstk], 1)) /* section context */ static int ignore; /* * Report an error and jump to the error label */ static void report(const char *fmt, ...) __dead2 __printflike(1, 2); static void report(const char *fmt, ...) { va_list ap; const struct input *input; va_start(ap, fmt); vsyslog(LOG_ERR, fmt, ap); va_end(ap); LIST_FOREACH(input, &inputs, link) { switch (input->type) { case INPUT_FILE: syslog(LOG_ERR, " in file %s line %u", input->input_filename, input->input_lno); break; case INPUT_STRING: syslog(LOG_ERR, " in macro %s pos %td", input->input_macro, input->input_ptr - input->input_str); break; } } ERR(); } /* * Open a file for input */ static int input_open_file(const char *fname, int sysdir) { struct input *input; FILE *fp; char path[PATH_MAX + 1]; const char *col; const char *ptr; if (sysdir) { ptr = syspath; fp = NULL; while (*ptr != '\0') { if ((col = strchr(ptr, ':')) == NULL) { snprintf(path, sizeof(path), "%s/%s", ptr, fname); col = ptr + strlen(ptr) - 1; } else if (col == ptr) snprintf(path, sizeof(path), "./%s", fname); else snprintf(path, sizeof(path), "%.*s/%s", (int)(col - ptr), ptr, fname); if ((fp = fopen(path, "r")) != NULL) break; ptr = col + 1; } } else fp = fopen(fname, "r"); if (fp == NULL) report("%s: %m", fname); if ((input = malloc(sizeof(*input))) == NULL) { fclose(fp); return (-1); } if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) { fclose(fp); free(input); return (-1); } strcpy(input->input_filename, fname); input->input_fp = fp; input->input_lno = 1; input->type = INPUT_FILE; LIST_INSERT_HEAD(&inputs, input, link); return (0); } /* * Make a macro the next input */ static void input_open_macro(struct macro *m) { struct input *input; if ((input = malloc(sizeof(*input))) == NULL) report("%m"); input->type = INPUT_STRING; input->input_macro = m->name; if ((input->input_str = malloc(m->length)) == NULL) { free(input); report("%m"); } memcpy(input->input_str, m->value, m->length); input->input_ptr = input->input_str; input->input_left = m->length; LIST_INSERT_HEAD(&inputs, input, link); } /* * Close top input source */ static void input_close(void) { struct input *input; if ((input = LIST_FIRST(&inputs)) == NULL) abort(); switch (input->type) { case INPUT_FILE: fclose(input->input_fp); free(input->input_filename); break; case INPUT_STRING: free(input->input_str); break; } LIST_REMOVE(input, link); free(input); } /* * Close all inputs */ static void input_close_all(void) { while (!LIST_EMPTY(&inputs)) input_close(); } /* * Push back one character */ static void input_ungetc(int c) { if (c == EOF) report("pushing EOF"); if (input_push == 2) report("pushing third char"); input_buf[input_push++] = c; } /* * Return next character from the input without preprocessing. */ static int input_getc_raw(void) { int c; struct input *input; if (input_push != 0) { c = input_buf[--input_push]; goto ok; } while ((input = LIST_FIRST(&inputs)) != NULL) { switch (input->type) { case INPUT_FILE: if ((c = getc(input->input_fp)) == EOF) { if (ferror(input->input_fp)) report("read error: %m"); input_close(); break; } if (c == '\n') input->input_lno++; goto ok; case INPUT_STRING: if (input->input_left-- == 0) { input_close(); break; } c = *input->input_ptr++; goto ok; } } # ifdef DEBUGGING fprintf(stderr, "EOF"); # endif return (EOF); ok: # ifdef DEBUGGING if (!isascii(c) || !isprint(c)) fprintf(stderr, "'%#2x'", c); else fprintf(stderr, "'%c'", c); # endif return (c); } /* * Get character with and \\n -> processing. */ static int input_getc_plain(void) { int c; again: if ((c = input_getc_raw()) == '\\') { if ((c = input_getc_raw()) == '\n') goto again; if (c != EOF) input_ungetc(c); return ('\\'); } return (c); } /* * Get next character with substitution of macros */ static int input_getc(void) { int c; struct macro *m; char name[_POSIX2_LINE_MAX]; size_t namelen; again: if ((c = input_getc_plain()) != '$') return (c); if ((c = input_getc()) == EOF) report("unexpected EOF"); if (c != '(') report("expecting '(' after '$'"); namelen = 0; while ((c = input_getc()) != EOF && c != ')') { if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c))) name[namelen++] = c; else goto badchar; } if (c == EOF) report("unexpected EOF"); name[namelen++] = '\0'; LIST_FOREACH(m, ¯os, link) if (strcmp(m->name, name) == 0) break; if (m == NULL) report("undefined macro '%s'", name); input_open_macro(m); goto again; badchar: if (!isascii(c) || !isprint(c)) report("unexpected character %#2x", (u_int)c); else report("bad character '%c'", c); } static void input_getnum(u_int base, u_int flen) { int c; u_int cnt; cnt = 0; numval = 0; while (flen == 0 || cnt < flen) { if ((c = input_getc()) == EOF) { if (cnt == 0) report("bad number"); return; } if (isdigit(c)) { if (base == 8 && (c == '8' || c == '9')) { input_ungetc(c); if (cnt == 0) report("bad number"); return; } numval = numval * base + (c - '0'); } else if (base == 16 && isxdigit(c)) { if (islower(c)) numval = numval * base + (c - 'a' + 10); else numval = numval * base + (c - 'A' + 10); } else { input_ungetc(c); if (cnt == 0) report("bad number"); return; } cnt++; } } static int # ifdef DEBUGGING _gettoken(void) # else gettoken(void) # endif { int c; char *end; static const char esc[] = "abfnrtv"; static const char chr[] = "\a\b\f\n\r\t\v"; /* * Skip any whitespace before the next token */ while ((c = input_getc()) != EOF) { if (!isspace(c) || c == '\n') break; } if (c == EOF) return (token = TOK_EOF); if (!isascii(c)) goto badchar; /* * Skip comments */ if (c == '#') { while ((c = input_getc_plain()) != EOF) { if (c == '\n') return (token = TOK_EOL); } goto badeof; } /* * Single character tokens */ if (c == '\n') return (token = TOK_EOL); if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>') return (token = c); if (c == ':') { if ((c = input_getc()) == '=') return (token = TOK_ASSIGN); input_ungetc(c); return (token = ':'); } if (c == '?') { if ((c = input_getc()) == '=') return (token = TOK_QASSIGN); input_ungetc(c); goto badchar; } /* * Sort out numbers */ if (isdigit(c)) { if (c == '0') { if ((c = input_getc()) == 'x' || c == 'X') { input_getnum(16, 0); } else if (isdigit(c)) { input_ungetc(c); input_getnum(8, 0); } else { if (c != EOF) input_ungetc(c); numval = 0; c = 1; } } else { input_ungetc(c); input_getnum(10, 0); } return (token = TOK_NUM); } /* * Must be a string then */ strvallen = 0; # define GETC(C) do { \ if ((c = input_getc()) == EOF) \ goto badeof; \ if (!isascii(c) || (!isprint(c) && c != '\t')) \ goto badchar; \ } while(0) if (c == '"') { for(;;) { GETC(c); if (c == '"') { strval[strvallen] = '\0'; break; } if (c != '\\') { strval[strvallen++] = c; continue; } GETC(c); if ((end = strchr(esc, c)) != NULL) { strval[strvallen++] = chr[end - esc]; continue; } if (c == 'x') { input_getnum(16, 2); c = numval; } else if (c >= '0' && c <= '7') { input_ungetc(c); input_getnum(8, 3); c = numval; } strval[strvallen++] = c; } # undef GETC } else if (c == '[') { /* * Skip leading space */ while ((c = input_getc()) != EOF && isspace(c)) ; if (c == EOF) goto badeof; while (c != ']' && !isspace(c)) { if (!isalnum(c) && c != '.' && c != '-') goto badchar; strval[strvallen++] = c; if ((c = input_getc()) == EOF) goto badeof; } while (c != ']' && isspace(c)) { if ((c = input_getc()) == EOF) goto badeof; } if (c != ']') goto badchar; strval[strvallen] = '\0'; return (token = TOK_HOST); } else if (!isalpha(c) && c != '_') { goto badchar; } else { for (;;) { strval[strvallen++] = c; if ((c = input_getc()) == EOF) goto badeof; if (!isalnum(c) && c != '_' && c != '-') { input_ungetc(c); strval[strvallen] = '\0'; break; } } } return (token = TOK_STR); badeof: report("unexpected EOF"); badchar: if (!isascii(c) || !isprint(c)) report("unexpected character %#2x", (u_int)c); else report("bad character '%c'", c); } # ifdef DEBUGGING static int gettoken() { _gettoken(); if (isascii(token) && isprint(token)) printf("(%c)", token); else { switch (token) { case TOK_EOF: printf("(EOF)"); break; case TOK_EOL: printf("(EOL)"); break; case TOK_NUM: printf("(NUM %llu)", numval); break; case TOK_STR: printf("(STR %.*s)", (int)strvallen, strval); break; case TOK_HOST: printf("(HOST %s)", strval); break; default: printf("(%#2x)", token); break; } } return (token); } #endif /* * Try to execute the assignment. */ static void handle_assignment(const struct snmp_node *node, struct asn_oid *vindex, const struct snmp_value *value) { u_int i; int err; struct assign *tp; char nodename[100]; if (node->type == SNMP_NODE_LEAF) { /* index must be one single zero or no index at all */ if (vindex->len > 1 || (vindex->len == 1 && vindex->subs[0] != 0)) report("bad index on leaf node"); vindex->len = 1; vindex->subs[0] = 0; } else { /* resulting oid must not be too long */ if (node->oid.len + vindex->len > ASN_MAXOIDLEN) report("resulting OID too long"); } /* * Get the next assignment entry for the transaction. */ if ((tp = malloc(sizeof(*tp))) == NULL) report("%m"); tp->value = *value; tp->node_name = node->name; /* * Build the OID */ tp->value.var = node->oid; for (i = 0; i < vindex->len; i++) tp->value.var.subs[tp->value.var.len++] = vindex->subs[i]; /* * Puzzle together the variables for the call and call the * set routine. The set routine may make our node pointer * invalid (if we happend to call the module loader) so * get a copy of the node name beforehands. */ snprintf(nodename, sizeof(nodename), "%s", node->name); snmp_ctx->scratch = &tp->scratch; snmp_ctx->var_index = 0; err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index, SNMP_OP_SET); if (err != 0) { free(tp); report("assignment to %s.%s returns %d", nodename, asn_oid2str(vindex), err); } TAILQ_INSERT_TAIL(&assigns, tp, link); } /* * Parse the section statement */ static void parse_section(const struct lmodule *mod) { if (token != TOK_STR) report("expecting section name"); if (strcmp(strval, "snmpd") == 0) { if (mod != NULL) /* loading a module - ignore common stuff */ ignore = 1; else /* global configuration - don't ignore */ ignore = 0; } else { if (mod == NULL) { /* global configuration - ignore module stuff */ ignore = 1; } else { /* loading module - check if it's our section */ ignore = (strcmp(strval, mod->section) != 0); } } gettoken(); } /* * Convert a hostname to four u_chars */ static void gethost(const char *host, u_char *ip) { struct addrinfo hints, *res; int error; struct sockaddr_in *sain; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(host, NULL, &hints, &res); if (error != 0) report("%s: %s", host, gai_strerror(error)); if (res == NULL) report("%s: unknown hostname", host); sain = (struct sockaddr_in *)(void *)res->ai_addr; sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr); ip[0] = sain->sin_addr.s_addr >> 24; ip[1] = sain->sin_addr.s_addr >> 16; ip[2] = sain->sin_addr.s_addr >> 8; ip[3] = sain->sin_addr.s_addr >> 0; freeaddrinfo(res); } /* * Parse the left hand side of a config line. */ static const struct snmp_node * parse_oid(const char *varname, struct asn_oid *oid) { struct snmp_node *node; u_int i; u_char ip[4]; struct asn_oid str_oid; for (node = tree; node < &tree[tree_size]; node++) if (strcmp(varname, node->name) == 0) break; if (node == &tree[tree_size]) node = NULL; oid->len = 0; while (token == '.') { if (gettoken() == TOK_NUM) { if (numval > ASN_MAXID) - report("subid too large %#"QUADXFMT, numval); + report("subid too large %#jx", + (uintmax_t)numval); if (oid->len == ASN_MAXOIDLEN) report("index too long"); if (gettoken() != ':') oid->subs[oid->len++] = numval; else { str_oid.len = 0; str_oid.subs[str_oid.len++] = numval; while (gettoken() == TOK_NUM) { str_oid.subs[str_oid.len++] = numval; if (gettoken() != ':') break; } oid->subs[oid->len++] = str_oid.len; asn_append_oid(oid, &str_oid); } } else if (token == TOK_STR) { if (strvallen + oid->len + 1 > ASN_MAXOIDLEN) report("oid too long"); oid->subs[oid->len++] = strvallen; for (i = 0; i < strvallen; i++) oid->subs[oid->len++] = strval[i]; gettoken(); } else if (token == TOK_HOST) { gethost(strval, ip); if (oid->len + 4 > ASN_MAXOIDLEN) report("index too long"); for (i = 0; i < 4; i++) oid->subs[oid->len++] = ip[i]; gettoken(); } else report("bad token in index"); } return (node); } /* * Parse the value for an assignment. */ static void parse_syntax_null(struct snmp_value *value __unused) { if (token != TOK_EOL) report("bad NULL syntax"); } static void parse_syntax_integer(struct snmp_value *value) { if (token != TOK_NUM) report("bad INTEGER syntax"); if (numval > 0x7fffffff) - report("INTEGER too large %"QUADFMT, numval); + report("INTEGER too large %ju", (uintmax_t)numval); value->v.integer = numval; gettoken(); } static void parse_syntax_counter64(struct snmp_value *value) { if (token != TOK_NUM) report("bad COUNTER64 syntax"); value->v.counter64 = numval; gettoken(); } static void parse_syntax_octetstring(struct snmp_value *value) { u_long alloc; u_char *noct; if (token == TOK_STR) { value->v.octetstring.len = strvallen; value->v.octetstring.octets = malloc(strvallen); (void)memcpy(value->v.octetstring.octets, strval, strvallen); gettoken(); return; } /* XX:XX:XX syntax */ value->v.octetstring.octets = NULL; value->v.octetstring.len = 0; if (token != TOK_NUM) /* empty string is allowed */ return; if (ERRPUSH()) { free(value->v.octetstring.octets); ERRNEXT(); } alloc = 0; for (;;) { if (token != TOK_NUM) report("bad OCTETSTRING syntax"); if (numval > 0xff) report("byte value too large"); if (alloc == value->v.octetstring.len) { alloc += 100; noct = realloc(value->v.octetstring.octets, alloc); if (noct == NULL) report("%m"); value->v.octetstring.octets = noct; } value->v.octetstring.octets[value->v.octetstring.len++] = numval; if (gettoken() != ':') break; gettoken(); } ERRPOP(); } static void parse_syntax_oid(struct snmp_value *value) { value->v.oid.len = 0; if (token != TOK_NUM) return; for (;;) { if (token != TOK_NUM) report("bad OID syntax"); if (numval > ASN_MAXID) report("subid too large"); if (value->v.oid.len == ASN_MAXOIDLEN) report("OID too long"); value->v.oid.subs[value->v.oid.len++] = numval; if (gettoken() != '.') break; gettoken(); } } static void parse_syntax_ipaddress(struct snmp_value *value) { int i; u_char ip[4]; if (token == TOK_NUM) { /* numerical address */ i = 0; for (;;) { if (numval >= 256) report("ip address part too large"); value->v.ipaddress[i++] = numval; if (i == 4) break; if (gettoken() != '.') report("expecting '.' in ip address"); } gettoken(); } else if (token == TOK_HOST) { /* host name */ gethost(strval, ip); for (i = 0; i < 4; i++) value->v.ipaddress[i] = ip[i]; gettoken(); } else report("bad ip address syntax"); } static void parse_syntax_uint32(struct snmp_value *value) { if (token != TOK_NUM) report("bad number syntax"); if (numval > 0xffffffff) report("number too large"); value->v.uint32 = numval; gettoken(); } /* * Parse an assignement line */ static void parse_assign(const char *varname) { struct snmp_value value; struct asn_oid vindex; const struct snmp_node *node; node = parse_oid(varname, &vindex); if (token != '=') report("'=' expected, got '%c'", token); gettoken(); if (ignore) { /* skip rest of line */ while (token != TOK_EOL && token != TOK_EOF) gettoken(); return; } if (node == NULL) report("unknown variable"); switch (value.syntax = node->syntax) { case SNMP_SYNTAX_NULL: parse_syntax_null(&value); break; case SNMP_SYNTAX_INTEGER: parse_syntax_integer(&value); break; case SNMP_SYNTAX_COUNTER64: parse_syntax_counter64(&value); break; case SNMP_SYNTAX_OCTETSTRING: parse_syntax_octetstring(&value); break; case SNMP_SYNTAX_OID: parse_syntax_oid(&value); break; case SNMP_SYNTAX_IPADDRESS: parse_syntax_ipaddress(&value); break; case SNMP_SYNTAX_COUNTER: case SNMP_SYNTAX_GAUGE: case SNMP_SYNTAX_TIMETICKS: parse_syntax_uint32(&value); break; case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: abort(); } if (ERRPUSH()) { snmp_value_free(&value); ERRNEXT(); } handle_assignment(node, &vindex, &value); ERRPOP(); } /* * Handle macro definition line * We have already seen the := and the input now stands at the character * after the =. Skip whitespace and then call the input routine directly to * eat up characters. */ static void parse_define(const char *varname) { char *volatile string; char *new; volatile size_t alloc, length; int c; struct macro *m; int t = token; alloc = 100; length = 0; if ((string = malloc(alloc)) == NULL) report("%m"); if (ERRPUSH()) { free(string); ERRNEXT(); } while ((c = input_getc_plain()) != EOF) { if (c == '\n' || !isspace(c)) break; } while (c != EOF && c != '#' && c != '\n') { if (alloc == length) { alloc *= 2; if ((new = realloc(string, alloc)) == NULL) report("%m"); string = new; } string[length++] = c; c = input_getc_plain(); } if (c == '#') { while ((c = input_getc_plain()) != EOF && c != '\n') ; } if (c == EOF) report("EOF in macro definition"); LIST_FOREACH(m, ¯os, link) if (strcmp(m->name, varname) == 0) break; if (m == NULL) { if ((m = malloc(sizeof(*m))) == NULL) report("%m"); if ((m->name = malloc(strlen(varname) + 1)) == NULL) { free(m); report("%m"); } strcpy(m->name, varname); m->perm = 0; LIST_INSERT_HEAD(¯os, m, link); m->value = string; m->length = length; } else { if (t == TOK_ASSIGN) { free(m->value); m->value = string; m->length = length; } else free(string); } token = TOK_EOL; ERRPOP(); } /* * Free all macros */ static void macro_free_all(void) { static struct macro *m, *m1; m = LIST_FIRST(¯os); while (m != NULL) { m1 = LIST_NEXT(m, link); if (!m->perm) { free(m->name); free(m->value); LIST_REMOVE(m, link); free(m); } m = m1; } } /* * Parse an include directive and switch to the new file */ static void parse_include(void) { int sysdir = 0; char fname[_POSIX2_LINE_MAX]; if (gettoken() == '<') { sysdir = 1; if (gettoken() != TOK_STR) report("expecting filename after in .include"); } else if (token != TOK_STR) report("expecting filename after in .include"); strcpy(fname, strval); if (sysdir && gettoken() != '>') report("expecting '>'"); gettoken(); if (input_open_file(fname, sysdir) == -1) report("%s: %m", fname); } /* * Parse the configuration file */ static void parse_file(const struct lmodule *mod) { char varname[_POSIX2_LINE_MAX]; while (gettoken() != TOK_EOF) { if (token == TOK_EOL) /* empty line */ continue; if (token == '%') { gettoken(); parse_section(mod); } else if (token == '.') { if (gettoken() != TOK_STR) report("keyword expected after '.'"); if (strcmp(strval, "include") == 0) parse_include(); else report("unknown keyword '%s'", strval); } else if (token == TOK_STR) { strcpy(varname, strval); if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN) parse_define(varname); else parse_assign(varname); } if (token != TOK_EOL) report("eol expected"); } } /* * Do rollback on errors */ static void do_rollback(void) { struct assign *tp; struct snmp_node *node; while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) { TAILQ_REMOVE(&assigns, tp, link); for (node = tree; node < &tree[tree_size]; node++) if (node->name == tp->node_name) { snmp_ctx->scratch = &tp->scratch; (void)(*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index, SNMP_OP_ROLLBACK); break; } if (node == &tree[tree_size]) syslog(LOG_ERR, "failed to find node for " "rollback"); snmp_value_free(&tp->value); free(tp); } } /* * Do commit */ static void do_commit(void) { struct assign *tp; struct snmp_node *node; while ((tp = TAILQ_FIRST(&assigns)) != NULL) { TAILQ_REMOVE(&assigns, tp, link); for (node = tree; node < &tree[tree_size]; node++) if (node->name == tp->node_name) { snmp_ctx->scratch = &tp->scratch; (void)(*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index, SNMP_OP_COMMIT); break; } if (node == &tree[tree_size]) syslog(LOG_ERR, "failed to find node for commit"); snmp_value_free(&tp->value); free(tp); } } /* * Read the configuration file. Handle the entire file as one transaction. * * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are * executed. If it is not NULL, only the sections for that module are handled. */ int read_config(const char *fname, struct lmodule *lodmod) { int err; char objbuf[ASN_OIDSTRLEN]; char idxbuf[ASN_OIDSTRLEN]; ignore = 0; input_push = 0; if (ERRPUSH()) return (-1); if (input_open_file(fname, 0) == -1) { syslog(LOG_ERR, "%s: %m", fname); return (-1); } ERRPOP(); community = COMM_INITIALIZE; if ((snmp_ctx = snmp_init_context()) == NULL) { input_close_all(); syslog(LOG_ERR, "%m"); return (-1); } if (ERRPUSH()) { do_rollback(); input_close_all(); macro_free_all(); free(snmp_ctx); return (-1); } parse_file(lodmod); ERRPOP(); if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) { syslog(LOG_ERR, "init dep failed: %u %s %s", err, asn_oid2str_r(&snmp_ctx->dep->obj, objbuf), asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf)); snmp_dep_rollback(snmp_ctx); do_rollback(); input_close_all(); macro_free_all(); free(snmp_ctx); return (-1); } do_commit(); snmp_dep_finish(snmp_ctx); macro_free_all(); free(snmp_ctx); return (0); } /* * Define a permanent macro */ int define_macro(const char *name, const char *value) { struct macro *m; if ((m = malloc(sizeof(*m))) == NULL) return (-1); if ((m->name = malloc(strlen(name) + 1)) == NULL) { free(m); return (-1); } strcpy(m->name, name); if ((m->value = malloc(strlen(value) + 1)) == NULL) { free(m->name); free(m); return (-1); } strcpy(m->value, value); m->length = strlen(value); m->perm = 1; LIST_INSERT_HEAD(¯os, m, link); return (0); } Index: stable/10/contrib/bsnmp/snmpd/main.c =================================================================== --- stable/10/contrib/bsnmp/snmpd/main.c (revision 300560) +++ stable/10/contrib/bsnmp/snmpd/main.c (revision 300561) @@ -1,3281 +1,3281 @@ /* * 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 #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; resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; if (snmp_pdu_encode(pdu, &resp_b) != 0) { syslog(LOG_ERR, "cannot encode message"); 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) { uint64_t etime; 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); 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) { etime = (get_ticks() - start_tick) / 100ULL; if (etime < INT32_MAX) snmpd_engine.engine_time = etime; else { start_tick = get_ticks(); set_snmpd_engine(); snmpd_engine.engine_time = start_tick; } 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) { 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; /* 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); ret = SNMPD_INPUT_OK; 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); 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 = SNMP_CODE_FAILED; + ret = SNMPD_INPUT_FAILED; } if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK) - ret = SNMP_CODE_FAILED; + 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; TAILQ_FOREACH(p, &t->table, link) { if (asn_compare_oid(&p->index, &port->index) > 0) { TAILQ_INSERT_BEFORE(p, port, link); return; } } port->transport = t; 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; } static void check_priv_dgram(struct port_input *pi, struct sockcred *cred) { /* process explicitly sends credentials */ if (cred) pi->priv = (cred->sc_euid == 0); else pi->priv = 0; } static void check_priv_stream(struct port_input *pi) { struct xucred ucred; socklen_t ucredlen; /* obtain the accept time credentials */ ucredlen = sizeof(ucred); if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 && ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION) pi->priv = (ucred.cr_uid == 0); else pi->priv = 0; } /* * Input from a stream socket. */ static int recv_stream(struct port_input *pi) { struct msghdr msg; struct iovec iov[1]; ssize_t len; if (pi->buf == NULL) { /* no buffer yet - allocate one */ if ((pi->buf = buf_alloc(0)) == NULL) { /* ups - could not get buffer. Return an error * the caller must close the transport. */ return (-1); } pi->buflen = buf_size(0); pi->consumed = 0; pi->length = 0; } /* try to get a message */ msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; iov[0].iov_base = pi->buf + pi->length; iov[0].iov_len = pi->buflen - pi->length; len = recvmsg(pi->fd, &msg, 0); if (len == -1 || len == 0) /* receive error */ return (-1); pi->length += len; if (pi->cred) check_priv_stream(pi); return (0); } /* * Input from a datagram socket. * Each receive should return one datagram. */ static int recv_dgram(struct port_input *pi, struct in_addr *laddr) { u_char embuf[1000]; char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(struct in_addr))]; struct msghdr msg; struct iovec iov[1]; ssize_t len; struct cmsghdr *cmsg; struct sockcred *cred = NULL; if (pi->buf == NULL) { /* no buffer yet - allocate one */ if ((pi->buf = buf_alloc(0)) == NULL) { /* ups - could not get buffer. Read away input * and drop it */ (void)recvfrom(pi->fd, embuf, sizeof(embuf), 0, NULL, NULL); /* return error */ return (-1); } pi->buflen = buf_size(0); } /* try to get a message */ msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; memset(cbuf, 0, sizeof(cbuf)); msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); msg.msg_flags = 0; iov[0].iov_base = pi->buf; iov[0].iov_len = pi->buflen; len = recvmsg(pi->fd, &msg, 0); if (len == -1 || len == 0) /* receive error */ return (-1); if (msg.msg_flags & MSG_TRUNC) { /* truncated - drop */ snmpd_stats.silentDrops++; snmpd_stats.inTooLong++; return (-1); } pi->length = (size_t)len; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) cred = (struct sockcred *)CMSG_DATA(cmsg); } if (pi->cred) check_priv_dgram(pi, cred); return (0); } /* * 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; int32_t vi; int ret; ssize_t slen; #ifdef USE_TCPWRAPPERS char client[16]; #endif struct msghdr msg; struct iovec iov[1]; char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; struct cmsghdr *cmsgp; /* get input depending on the transport */ if (pi->stream) { msg.msg_control = NULL; msg.msg_controllen = 0; ret = recv_stream(pi); } else { struct in_addr *laddr; memset(cbuf, 0, CMSG_SPACE(sizeof(struct in_addr))); msg.msg_control = cbuf; msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); cmsgp = CMSG_FIRSTHDR(&msg); cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); cmsgp->cmsg_level = IPPROTO_IP; cmsgp->cmsg_type = IP_SENDSRCADDR; laddr = (struct in_addr *)CMSG_DATA(cmsgp); ret = recv_dgram(pi, laddr); if (laddr->s_addr == 0) { msg.msg_control = NULL; msg.msg_controllen = 0; } } 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)))) { 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) { msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_flags = 0; iov[0].iov_base = sndbuf; iov[0].iov_len = sndlen; slen = sendmsg(pi->fd, &msg, 0); if (slen == -1) syslog(LOG_ERR, "sendmsg: %m"); else if ((size_t)slen != sndlen) syslog(LOG_ERR, "sendmsg: 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); 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 *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); if ((c = malloc(sizeof(struct community))) == NULL) { syslog(LOG_ERR, "comm_define: %m"); return (0); } 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); } 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 */ 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->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; strcpy(m->section, section); if ((m->path = malloc(strlen(path) + 1)) == NULL) { syslog(LOG_ERR, "lm_load: %m"); goto err; } strcpy(m->path, path); /* * 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: stable/10/usr.sbin/bsnmpd/bsnmpd/Makefile =================================================================== --- stable/10/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 300560) +++ stable/10/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 300561) @@ -1,52 +1,54 @@ # $FreeBSD$ # # Author: Harti Brandt .include CONTRIB=${.CURDIR}/../../../contrib/bsnmp .PATH: ${CONTRIB}/snmpd PROG= bsnmpd SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c SRCS+= oid.h tree.c tree.h XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ sysUpTime snmpTrapOID coldStart authenticationFailure \ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \ freeBSD freeBSDVersion CLEANFILES= oid.h tree.c tree.h MAN= bsnmpd.1 snmpmod.3 -NO_WERROR= FILESGROUPS= BMIBS DEFS BMIBS= FOKUS-MIB.txt BEGEMOT-MIB.txt BEGEMOT-SNMPD.txt BMIBSDIR= ${SHAREDIR}/snmp/mibs DEFS= tree.def DEFSDIR= ${SHAREDIR}/snmp/defs CFLAGS+= -DSNMPTREE_TYPES CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I. -DUSE_LIBBEGEMOT -CFLAGS+= -DUSE_TCPWRAPPERS -DQUADFMT='"llu"' -DQUADXFMT='"llx"' +CFLAGS+= -DUSE_TCPWRAPPERS CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DHAVE_ERR_H -DHAVE_STRLCPY DPADD= ${LIBBEGEMOT} ${LIBBSNMP} ${LIBWRAP} LDADD= -lbegemot -lbsnmp -lwrap LDFLAGS= -Wl,-export-dynamic .if ${MK_OPENSSL} != "no" CFLAGS+= -DHAVE_LIBCRYPTO .endif oid.h: tree.def Makefile gensnmptree -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET} .ORDER: tree.c tree.h tree.c tree.h: tree.def gensnmptree -l < ${.ALLSRC} MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ -e 's%@DEFPATH@%${DEFSDIR}/%g' \ -e 's%@MIBSPATH@%${BMIBSDIR}/%g' + +NO_WCAST_ALIGN= yes +WARNS?= 6 .include Index: stable/10 =================================================================== --- stable/10 (revision 300560) +++ stable/10 (revision 300561) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r299465,299807-299808,299817,299831-299832,300167