diff options
Diffstat (limited to 'agent/mibgroup/mibII/vacm_conf.c')
-rw-r--r-- | agent/mibgroup/mibII/vacm_conf.c | 1494 |
1 files changed, 1494 insertions, 0 deletions
diff --git a/agent/mibgroup/mibII/vacm_conf.c b/agent/mibgroup/mibII/vacm_conf.c new file mode 100644 index 0000000..7e19314 --- /dev/null +++ b/agent/mibgroup/mibII/vacm_conf.c @@ -0,0 +1,1494 @@ +/* + * SNMPv3 View-based Access Control Model + */ +/* Portions of this file are subject to the following copyright(s). See + * the Net-SNMP's COPYING file for more details and other copyrights + * that may apply: + */ +/* + * Portions of this file are copyrighted by: + * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms specified in the COPYING file + * distributed with the Net-SNMP package. + */ + +#include <net-snmp/net-snmp-config.h> + +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#if HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <ctype.h> +#include <sys/types.h> +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#if HAVE_NETDB_H +#include <netdb.h> +#endif +#if HAVE_WINSOCK_H +#include <winsock.h> +#endif + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include <net-snmp/agent/agent_callbacks.h> +#include "vacm_conf.h" +#include "util_funcs.h" + +#ifdef USING_MIBII_SYSORTABLE_MODULE +#if TIME_WITH_SYS_TIME +# ifdef WIN32 +# include <sys/timeb.h> +# else +# include <sys/time.h> +# endif +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#include "sysORTable.h" +#endif + +#include "snmpd.h" + +/** + * Registers the VACM token handlers for inserting rows into the vacm tables. + * These tokens will be recognised by both 'snmpd' and 'snmptrapd'. + */ +void +init_vacm_config_tokens(void) { + snmpd_register_config_handler("group", vacm_parse_group, + vacm_free_group, + "name v1|v2c|usm|... security"); + snmpd_register_config_handler("access", vacm_parse_access, + vacm_free_access, + "name context model level prefix read write notify"); + snmpd_register_config_handler("setaccess", vacm_parse_setaccess, + vacm_free_access, + "name context model level prefix viewname viewval"); + snmpd_register_config_handler("view", vacm_parse_view, vacm_free_view, + "name type subtree [mask]"); + snmpd_register_config_handler("vacmView", vacm_parse_config_view, NULL, + NULL); + snmpd_register_config_handler("vacmGroup", vacm_parse_config_group, + NULL, NULL); + snmpd_register_config_handler("vacmAccess", vacm_parse_config_access, + NULL, NULL); + snmpd_register_config_handler("vacmAuthAccess", vacm_parse_config_auth_access, + NULL, NULL); + + /* easy community auth handler */ + snmpd_register_config_handler("authcommunity", + vacm_parse_authcommunity, + NULL, "authtype1,authtype2 community [default|hostname|network/bits [oid|-V view]]"); + + /* easy user auth handler */ + snmpd_register_config_handler("authuser", + vacm_parse_authuser, + NULL, "authtype1,authtype2 [-s secmodel] user [noauth|auth|priv [oid|-V view]]"); + /* easy group auth handler */ + snmpd_register_config_handler("authgroup", + vacm_parse_authuser, + NULL, "authtype1,authtype2 [-s secmodel] group [noauth|auth|priv [oid|-V view]]"); + + snmpd_register_config_handler("authaccess", vacm_parse_authaccess, + vacm_free_access, + "name authtype1,authtype2 [-s secmodel] group view [noauth|auth|priv [context|context*]]"); + + /* + * Define standard views "_all_" and "_none_" + */ + snmp_register_callback(SNMP_CALLBACK_LIBRARY, + SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, + vacm_standard_views, NULL); + snmp_register_callback(SNMP_CALLBACK_LIBRARY, + SNMP_CALLBACK_POST_READ_CONFIG, + vacm_warn_if_not_configured, NULL); +} + +/** + * Registers the easier-to-use VACM token handlers for quick access rules. + * These tokens will only be recognised by 'snmpd'. + */ +void +init_vacm_snmpd_easy_tokens(void) { +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + snmpd_register_config_handler("rwcommunity", vacm_parse_rwcommunity, NULL, + "community [default|hostname|network/bits [oid]]"); + snmpd_register_config_handler("rocommunity", vacm_parse_rocommunity, NULL, + "community [default|hostname|network/bits [oid]]"); +#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN + snmpd_register_config_handler("rwcommunity6", vacm_parse_rwcommunity6, NULL, + "community [default|hostname|network/bits [oid]]"); + snmpd_register_config_handler("rocommunity6", vacm_parse_rocommunity6, NULL, + "community [default|hostname|network/bits [oid]]"); +#endif +#endif /* support for community based SNMP */ + snmpd_register_config_handler("rwuser", vacm_parse_rwuser, NULL, + "user [noauth|auth|priv [oid]]"); + snmpd_register_config_handler("rouser", vacm_parse_rouser, NULL, + "user [noauth|auth|priv [oid]]"); +} + +void +init_vacm_conf(void) +{ + init_vacm_config_tokens(); + init_vacm_snmpd_easy_tokens(); + /* + * register ourselves to handle access control ('snmpd' only) + */ + snmp_register_callback(SNMP_CALLBACK_APPLICATION, + SNMPD_CALLBACK_ACM_CHECK, vacm_in_view_callback, + NULL); + snmp_register_callback(SNMP_CALLBACK_APPLICATION, + SNMPD_CALLBACK_ACM_CHECK_INITIAL, + vacm_in_view_callback, NULL); + snmp_register_callback(SNMP_CALLBACK_APPLICATION, + SNMPD_CALLBACK_ACM_CHECK_SUBTREE, + vacm_in_view_callback, NULL); +} + + + +void +vacm_parse_group(const char *token, char *param) +{ + char *group, *model, *security; + int imodel; + struct vacm_groupEntry *gp = NULL; + char *st; + + group = strtok_r(param, " \t\n", &st); + model = strtok_r(NULL, " \t\n", &st); + security = strtok_r(NULL, " \t\n", &st); + + if (group == NULL || *group == 0) { + config_perror("missing GROUP parameter"); + return; + } + if (model == NULL || *model == 0) { + config_perror("missing MODEL parameter"); + return; + } + if (security == NULL || *security == 0) { + config_perror("missing SECURITY parameter"); + return; + } + if (strcasecmp(model, "v1") == 0) + imodel = SNMP_SEC_MODEL_SNMPv1; + else if (strcasecmp(model, "v2c") == 0) + imodel = SNMP_SEC_MODEL_SNMPv2c; + else if (strcasecmp(model, "any") == 0) { + config_perror + ("bad security model \"any\" should be: v1, v2c, usm or a registered security plugin name - installing anyway"); + imodel = SNMP_SEC_MODEL_ANY; + } else { + if ((imodel = se_find_value_in_slist("snmp_secmods", model)) == + SE_DNE) { + config_perror + ("bad security model, should be: v1, v2c or usm or a registered security plugin name"); + return; + } + } + if (strlen(security) + 1 > sizeof(gp->groupName)) { + config_perror("security name too long"); + return; + } + gp = vacm_createGroupEntry(imodel, security); + if (!gp) { + config_perror("failed to create group entry"); + return; + } + strncpy(gp->groupName, group, sizeof(gp->groupName)); + gp->groupName[ sizeof(gp->groupName)-1 ] = 0; + gp->storageType = SNMP_STORAGE_PERMANENT; + gp->status = SNMP_ROW_ACTIVE; + free(gp->reserved); + gp->reserved = NULL; +} + +void +vacm_free_group(void) +{ + vacm_destroyAllGroupEntries(); +} + +#define PARSE_CONT 0 +#define PARSE_FAIL 1 + +int +_vacm_parse_access_common(const char *token, char *param, char **st, + char **name, char **context, int *imodel, + int *ilevel, int *iprefix) +{ + char *model, *level, *prefix; + + *name = strtok_r(param, " \t\n", st); + if (!*name) { + config_perror("missing NAME parameter"); + return PARSE_FAIL; + } + *context = strtok_r(NULL, " \t\n", st); + if (!*context) { + config_perror("missing CONTEXT parameter"); + return PARSE_FAIL; + } + + model = strtok_r(NULL, " \t\n", st); + if (!model) { + config_perror("missing MODEL parameter"); + return PARSE_FAIL; + } + level = strtok_r(NULL, " \t\n", st); + if (!level) { + config_perror("missing LEVEL parameter"); + return PARSE_FAIL; + } + prefix = strtok_r(NULL, " \t\n", st); + if (!prefix) { + config_perror("missing PREFIX parameter"); + return PARSE_FAIL; + } + + if (strcmp(*context, "\"\"") == 0) + **context = 0; + if (strcasecmp(model, "any") == 0) + *imodel = SNMP_SEC_MODEL_ANY; + else if (strcasecmp(model, "v1") == 0) + *imodel = SNMP_SEC_MODEL_SNMPv1; + else if (strcasecmp(model, "v2c") == 0) + *imodel = SNMP_SEC_MODEL_SNMPv2c; + else { + if ((*imodel = se_find_value_in_slist("snmp_secmods", model)) + == SE_DNE) { + config_perror + ("bad security model, should be: v1, v2c or usm or a registered security plugin name"); + return PARSE_FAIL; + } + } + + if (strcasecmp(level, "noauth") == 0) + *ilevel = SNMP_SEC_LEVEL_NOAUTH; + else if (strcasecmp(level, "noauthnopriv") == 0) + *ilevel = SNMP_SEC_LEVEL_NOAUTH; + else if (strcasecmp(level, "auth") == 0) + *ilevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + else if (strcasecmp(level, "authnopriv") == 0) + *ilevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + else if (strcasecmp(level, "priv") == 0) + *ilevel = SNMP_SEC_LEVEL_AUTHPRIV; + else if (strcasecmp(level, "authpriv") == 0) + *ilevel = SNMP_SEC_LEVEL_AUTHPRIV; + else { + config_perror + ("bad security level (noauthnopriv, authnopriv, authpriv)"); + return PARSE_FAIL; + } + + if (strcmp(prefix, "exact") == 0) + *iprefix = 1; + else if (strcmp(prefix, "prefix") == 0) + *iprefix = 2; + else if (strcmp(prefix, "0") == 0) { + config_perror + ("bad prefix match parameter \"0\", should be: exact or prefix - installing anyway"); + *iprefix = 1; + } else { + config_perror + ("bad prefix match parameter, should be: exact or prefix"); + return PARSE_FAIL; + } + + return PARSE_CONT; +} + +/* **************************************/ +/* authorization parsing token handlers */ +/* **************************************/ + +int +vacm_parse_authtokens(const char *token, char **confline) +{ + char authspec[SNMP_MAXBUF_MEDIUM]; + char *strtok_state; + char *type; + int viewtype, viewtypes = 0; + + *confline = copy_nword(*confline, authspec, sizeof(authspec)); + + DEBUGMSGTL(("vacm_parse_authtokens","parsing %s",authspec)); + if (!*confline) { + config_perror("Illegal configuration line: missing fields"); + return -1; + } + + type = strtok_r(authspec, ",|:", &strtok_state); + while(type && *type != '\0') { + viewtype = se_find_value_in_slist(VACM_VIEW_ENUM_NAME, type); + if (viewtype < 0 || viewtype >= VACM_MAX_VIEWS) { + config_perror("Illegal view name"); + } else { + viewtypes |= (1 << viewtype); + } + type = strtok_r(NULL, ",|:", &strtok_state); + } + DEBUGMSG(("vacm_parse_authtokens"," .. result = 0x%x\n",viewtypes)); + return viewtypes; +} + +void +vacm_parse_authuser(const char *token, char *confline) +{ + int viewtypes = vacm_parse_authtokens(token, &confline); + if (viewtypes != -1) + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_V3, viewtypes); +} + +void +vacm_parse_authcommunity(const char *token, char *confline) +{ + int viewtypes = vacm_parse_authtokens(token, &confline); + if (viewtypes != -1) + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_COM, viewtypes); +} + +void +vacm_parse_authaccess(const char *token, char *confline) +{ + char *group, *view, *context, *tmp; + int model = SNMP_SEC_MODEL_ANY; + int level, prefix; + int i; + char *st; + struct vacm_accessEntry *ap; + int viewtypes = vacm_parse_authtokens(token, &confline); + + if (viewtypes == -1) + return; + + group = strtok_r(confline, " \t\n", &st); + if (!group) { + config_perror("missing GROUP parameter"); + return; + } + view = strtok_r(NULL, " \t\n", &st); + if (!view) { + config_perror("missing VIEW parameter"); + return; + } + + /* + * Check for security model option + */ + if ( strcasecmp(view, "-s") == 0 ) { + tmp = strtok_r(NULL, " \t\n", &st); + if (tmp) { + if (strcasecmp(tmp, "any") == 0) + model = SNMP_SEC_MODEL_ANY; + else if (strcasecmp(tmp, "v1") == 0) + model = SNMP_SEC_MODEL_SNMPv1; + else if (strcasecmp(tmp, "v2c") == 0) + model = SNMP_SEC_MODEL_SNMPv2c; + else { + model = se_find_value_in_slist("snmp_secmods", tmp); + if (model == SE_DNE) { + config_perror + ("bad security model, should be: v1, v2c or usm or a registered security plugin name"); + return; + } + } + } else { + config_perror("missing SECMODEL parameter"); + return; + } + view = strtok_r(NULL, " \t\n", &st); + if (!view) { + config_perror("missing VIEW parameter"); + return; + } + } + if (strlen(view) >= VACMSTRINGLEN ) { + config_perror("View value too long"); + return; + } + + /* + * Now parse optional fields, or provide default values + */ + + tmp = strtok_r(NULL, " \t\n", &st); + if (tmp) { + if (strcasecmp(tmp, "noauth") == 0) + level = SNMP_SEC_LEVEL_NOAUTH; + else if (strcasecmp(tmp, "noauthnopriv") == 0) + level = SNMP_SEC_LEVEL_NOAUTH; + else if (strcasecmp(tmp, "auth") == 0) + level = SNMP_SEC_LEVEL_AUTHNOPRIV; + else if (strcasecmp(tmp, "authnopriv") == 0) + level = SNMP_SEC_LEVEL_AUTHNOPRIV; + else if (strcasecmp(tmp, "priv") == 0) + level = SNMP_SEC_LEVEL_AUTHPRIV; + else if (strcasecmp(tmp, "authpriv") == 0) + level = SNMP_SEC_LEVEL_AUTHPRIV; + else { + config_perror + ("bad security level (noauthnopriv, authnopriv, authpriv)"); + return; + } + } else { + /* What about SNMP_SEC_MODEL_ANY ?? */ + if ( model == SNMP_SEC_MODEL_SNMPv1 || + model == SNMP_SEC_MODEL_SNMPv2c ) + level = SNMP_SEC_LEVEL_NOAUTH; + else + level = SNMP_SEC_LEVEL_AUTHNOPRIV; + } + + + context = strtok_r(NULL, " \t\n", &st); + if (context) { + tmp = (context + strlen(context)-1); + if (tmp && *tmp == '*') { + *tmp = '\0'; + prefix = 2; + } else { + prefix = 1; + } + } else { + context = ""; + prefix = 1; /* Or prefix(2) ?? */ + } + + /* + * Now we can create the access entry + */ + ap = vacm_getAccessEntry(group, context, model, level); + if (!ap) { + ap = vacm_createAccessEntry(group, context, model, level); + DEBUGMSGTL(("vacm:conf:authaccess", + "no existing access found; creating a new one\n")); + } else { + DEBUGMSGTL(("vacm:conf:authaccess", + "existing access found, using it\n")); + } + if (!ap) { + config_perror("failed to create access entry"); + return; + } + + for (i = 0; i <= VACM_MAX_VIEWS; i++) { + if (viewtypes & (1 << i)) { + strcpy(ap->views[i], view); + } + } + ap->contextMatch = prefix; + ap->storageType = SNMP_STORAGE_PERMANENT; + ap->status = SNMP_ROW_ACTIVE; + if (ap->reserved) + free(ap->reserved); + ap->reserved = NULL; +} + +void +vacm_parse_setaccess(const char *token, char *param) +{ + char *name, *context, *viewname, *viewval; + int imodel, ilevel, iprefix; + int viewnum; + char *st; + struct vacm_accessEntry *ap; + + if (_vacm_parse_access_common(token, param, &st, &name, + &context, &imodel, &ilevel, &iprefix) + == PARSE_FAIL) { + return; + } + + viewname = strtok_r(NULL, " \t\n", &st); + if (!viewname) { + config_perror("missing viewname parameter"); + return; + } + viewval = strtok_r(NULL, " \t\n", &st); + if (!viewval) { + config_perror("missing viewval parameter"); + return; + } + + if (strlen(viewval) + 1 > sizeof(ap->views[VACM_VIEW_NOTIFY])) { + config_perror("View value too long"); + return; + } + + viewnum = se_find_value_in_slist(VACM_VIEW_ENUM_NAME, viewname); + if (viewnum < 0 || viewnum >= VACM_MAX_VIEWS) { + config_perror("Illegal view name"); + return; + } + + ap = vacm_getAccessEntry(name, context, imodel, ilevel); + if (!ap) { + ap = vacm_createAccessEntry(name, context, imodel, ilevel); + DEBUGMSGTL(("vacm:conf:setaccess", + "no existing access found; creating a new one\n")); + } else { + DEBUGMSGTL(("vacm:conf:setaccess", + "existing access found, using it\n")); + } + if (!ap) { + config_perror("failed to create access entry"); + return; + } + if (!ap) { + config_perror("failed to create access entry"); + return; + } + + strcpy(ap->views[viewnum], viewval); + ap->contextMatch = iprefix; + ap->storageType = SNMP_STORAGE_PERMANENT; + ap->status = SNMP_ROW_ACTIVE; + free(ap->reserved); + ap->reserved = NULL; +} + +void +vacm_parse_access(const char *token, char *param) +{ + char *name, *context, *readView, *writeView, *notify; + int imodel, ilevel, iprefix; + struct vacm_accessEntry *ap; + char *st; + + + if (_vacm_parse_access_common(token, param, &st, &name, + &context, &imodel, &ilevel, &iprefix) + == PARSE_FAIL) { + return; + } + + readView = strtok_r(NULL, " \t\n", &st); + if (!readView) { + config_perror("missing readView parameter"); + return; + } + writeView = strtok_r(NULL, " \t\n", &st); + if (!writeView) { + config_perror("missing writeView parameter"); + return; + } + notify = strtok_r(NULL, " \t\n", &st); + if (!notify) { + config_perror("missing notifyView parameter"); + return; + } + + if (strlen(readView) + 1 > sizeof(ap->views[VACM_VIEW_READ])) { + config_perror("readView too long"); + return; + } + if (strlen(writeView) + 1 > sizeof(ap->views[VACM_VIEW_WRITE])) { + config_perror("writeView too long"); + return; + } + if (strlen(notify) + 1 > sizeof(ap->views[VACM_VIEW_NOTIFY])) { + config_perror("notifyView too long"); + return; + } + ap = vacm_createAccessEntry(name, context, imodel, ilevel); + if (!ap) { + config_perror("failed to create access entry"); + return; + } + strcpy(ap->views[VACM_VIEW_READ], readView); + strcpy(ap->views[VACM_VIEW_WRITE], writeView); + strcpy(ap->views[VACM_VIEW_NOTIFY], notify); + ap->contextMatch = iprefix; + ap->storageType = SNMP_STORAGE_PERMANENT; + ap->status = SNMP_ROW_ACTIVE; + free(ap->reserved); + ap->reserved = NULL; +} + +void +vacm_free_access(void) +{ + vacm_destroyAllAccessEntries(); +} + +void +vacm_parse_view(const char *token, char *param) +{ + char *name, *type, *subtree, *mask; + int inclexcl; + struct vacm_viewEntry *vp; + oid suboid[MAX_OID_LEN]; + size_t suboid_len = 0; + size_t mask_len = 0; + u_char viewMask[VACMSTRINGLEN]; + int i; + char *st; + + name = strtok_r(param, " \t\n", &st); + if (!name) { + config_perror("missing NAME parameter"); + return; + } + type = strtok_r(NULL, " \n\t", &st); + if (!type) { + config_perror("missing TYPE parameter"); + return; + } + subtree = strtok_r(NULL, " \t\n", &st); + if (!subtree) { + config_perror("missing SUBTREE parameter"); + return; + } + mask = strtok_r(NULL, "\0", &st); + + if (strcmp(type, "included") == 0) + inclexcl = SNMP_VIEW_INCLUDED; + else if (strcmp(type, "excluded") == 0) + inclexcl = SNMP_VIEW_EXCLUDED; + else { + config_perror("TYPE must be included/excluded?"); + return; + } + suboid_len = strlen(subtree)-1; + if (subtree[suboid_len] == '.') + subtree[suboid_len] = '\0'; /* stamp on a trailing . */ + suboid_len = MAX_OID_LEN; + if (!snmp_parse_oid(subtree, suboid, &suboid_len)) { + config_perror("bad SUBTREE object id"); + return; + } + if (mask) { + int val; + i = 0; + for (mask = strtok_r(mask, " .:", &st); mask; mask = strtok_r(NULL, " .:", &st)) { + if (i >= sizeof(viewMask)) { + config_perror("MASK too long"); + return; + } + if (sscanf(mask, "%x", &val) == 0) { + config_perror("invalid MASK"); + return; + } + viewMask[i] = val; + i++; + } + mask_len = i; + } else { + for (i = 0; i < sizeof(viewMask); i++) + viewMask[i] = 0xff; + } + vp = vacm_createViewEntry(name, suboid, suboid_len); + if (!vp) { + config_perror("failed to create view entry"); + return; + } + memcpy(vp->viewMask, viewMask, sizeof(viewMask)); + vp->viewMaskLen = mask_len; + vp->viewType = inclexcl; + vp->viewStorageType = SNMP_STORAGE_PERMANENT; + vp->viewStatus = SNMP_ROW_ACTIVE; + free(vp->reserved); + vp->reserved = NULL; +} + +void +vacm_free_view(void) +{ + vacm_destroyAllViewEntries(); +} + +void +vacm_gen_com2sec(int commcount, char *community, char *addressname, + char *publishtoken, + void (*parser)(const char *, char *), + char *secname, size_t secname_len, + char *viewname, size_t viewname_len, int version) +{ + char line[SPRINT_MAX_LEN]; + + /* + * com2sec6|comsec anonymousSecNameNUM ADDRESS COMMUNITY + */ + snprintf(secname, secname_len-1, "comm%d", commcount); + secname[secname_len-1] = '\0'; + if (viewname) { + snprintf(viewname, viewname_len-1, "viewComm%d", commcount); + viewname[viewname_len-1] = '\0'; + } + snprintf(line, sizeof(line), "%s %s '%s'", + secname, addressname, community); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((publishtoken, "passing: %s %s\n", publishtoken, line)); + (*parser)(publishtoken, line); + + /* + * sec->group mapping + */ + /* + * group anonymousGroupNameNUM any anonymousSecNameNUM + */ + if ( version & SNMP_SEC_MODEL_SNMPv1 ) { + snprintf(line, sizeof(line), + "grp%.28s v1 %s", secname, secname); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((publishtoken, "passing: %s %s\n", "group", line)); + vacm_parse_group("group", line); + } + + if ( version & SNMP_SEC_MODEL_SNMPv2c ) { + snprintf(line, sizeof(line), + "grp%.28s v2c %s", secname, secname); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((publishtoken, "passing: %s %s\n", "group", line)); + vacm_parse_group("group", line); + } +} + +void +vacm_parse_rwuser(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_V3, + VACM_VIEW_READ_BIT | VACM_VIEW_WRITE_BIT); +} + +void +vacm_parse_rouser(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_V3, + VACM_VIEW_READ_BIT); +} + +void +vacm_parse_rocommunity(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_COMIPV4, + VACM_VIEW_READ_BIT); +} + +void +vacm_parse_rwcommunity(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_COMIPV4, + VACM_VIEW_READ_BIT | VACM_VIEW_WRITE_BIT); +} + +void +vacm_parse_rocommunity6(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_COMIPV6, + VACM_VIEW_READ_BIT); +} + +void +vacm_parse_rwcommunity6(const char *token, char *confline) +{ + vacm_create_simple(token, confline, VACM_CREATE_SIMPLE_COMIPV6, + VACM_VIEW_READ_BIT | VACM_VIEW_WRITE_BIT); +} + + +void +vacm_create_simple(const char *token, char *confline, + int parsetype, int viewtypes) +{ + char line[SPRINT_MAX_LEN]; + char community[COMMUNITY_MAX_LEN]; + char theoid[SPRINT_MAX_LEN]; + char viewname[SPRINT_MAX_LEN]; + char *view_ptr = viewname; +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + char addressname[SPRINT_MAX_LEN]; +#endif + const char *rw = "none"; + char model[SPRINT_MAX_LEN]; + char *cp, *tmp; + char secname[SPRINT_MAX_LEN]; + char grpname[SPRINT_MAX_LEN]; + char authlevel[SPRINT_MAX_LEN]; + char context[SPRINT_MAX_LEN]; + int ctxprefix = 1; /* Default to matching all contexts */ + static int commcount = 0; + /* Conveniently, the community-based security + model values can also be used as bit flags */ + int commversion = SNMP_SEC_MODEL_SNMPv1 | + SNMP_SEC_MODEL_SNMPv2c; + + /* + * init + */ + strcpy(model, "any"); + memset(context, 0, sizeof(context)); + memset(secname, 0, sizeof(secname)); + memset(grpname, 0, sizeof(grpname)); + + /* + * community name or user name + */ + cp = copy_nword(confline, community, sizeof(community)); + + if (parsetype == VACM_CREATE_SIMPLE_V3) { + /* + * maybe security model type + */ + if (strcmp(community, "-s") == 0) { + /* + * -s model ... + */ + if (cp) + cp = copy_nword(cp, model, sizeof(model)); + if (!cp) { + config_perror("illegal line"); + return; + } + if (cp) + cp = copy_nword(cp, community, sizeof(community)); + } else { + strcpy(model, "usm"); + } + /* + * authentication level + */ + if (cp && *cp) + cp = copy_nword(cp, authlevel, sizeof(authlevel)); + else + strcpy(authlevel, "auth"); + DEBUGMSGTL((token, "setting auth level: \"%s\"\n", authlevel)); +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + } else { + if (strcmp(community, "-v") == 0) { + /* + * -v version ... + */ + if (cp) + cp = copy_nword(cp, model, sizeof(model)); + if (!cp) { + config_perror("illegal line"); + return; + } + if ( strcasecmp( model, "1" ) == 0 ) + strcpy(model, "v1"); + if ( strcasecmp( model, "v1" ) == 0 ) + commversion = SNMP_SEC_MODEL_SNMPv1; + if ( strcasecmp( model, "2c" ) == 0 ) + strcpy(model, "v2c"); + if ( strcasecmp( model, "v2c" ) == 0 ) + commversion = SNMP_SEC_MODEL_SNMPv2c; + if (cp) + cp = copy_nword(cp, community, sizeof(community)); + } + /* + * source address + */ + if (cp && *cp) { + cp = copy_nword(cp, addressname, sizeof(addressname)); + } else { + strcpy(addressname, "default"); + } + /* + * authlevel has to be noauth + */ + strcpy(authlevel, "noauth"); +#endif /* support for community based SNMP */ + } + + /* + * oid they can touch + */ + if (cp && *cp) { + if (strncmp(cp, "-V ", 3) == 0) { + cp = skip_token(cp); + cp = copy_nword(cp, viewname, sizeof(viewname)); + view_ptr = NULL; + } else { + cp = copy_nword(cp, theoid, sizeof(theoid)); + } + } else { + strcpy(theoid, ".1"); + strcpy(viewname, "_all_"); + view_ptr = NULL; + } + /* + * optional, non-default context + */ + if (cp && *cp) { + cp = copy_nword(cp, context, sizeof(context)); + tmp = (context + strlen(context)-1); + if (tmp && *tmp == '*') { + *tmp = '\0'; + ctxprefix = 1; + } else { + /* + * If no context field is given, then we default to matching + * all contexts (for compatability with previous releases). + * But if a field context is specified (not ending with '*') + * then this should be taken as an exact match. + * Specifying a context field of "" will match the default + * context (and *only* the default context). + */ + ctxprefix = 0; + } + } + + if (viewtypes & VACM_VIEW_WRITE_BIT) + rw = viewname; + + commcount++; + +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + if (parsetype == VACM_CREATE_SIMPLE_COMIPV4 || + parsetype == VACM_CREATE_SIMPLE_COM) { + vacm_gen_com2sec(commcount, community, addressname, + "com2sec", &netsnmp_udp_parse_security, + secname, sizeof(secname), + view_ptr, sizeof(viewname), commversion); + } + +#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN + if (parsetype == VACM_CREATE_SIMPLE_COMUNIX || + parsetype == VACM_CREATE_SIMPLE_COM) { + snprintf(line, sizeof(line), "%s %s '%s'", + secname, addressname, community); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((token, "passing: %s %s\n", "com2secunix", line)); + netsnmp_unix_parse_security("com2secunix", line); + } +#endif + +#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN + if (parsetype == VACM_CREATE_SIMPLE_COMIPV6 || + parsetype == VACM_CREATE_SIMPLE_COM) { + vacm_gen_com2sec(commcount, community, addressname, + "com2sec6", &netsnmp_udp6_parse_security, + secname, sizeof(secname), + view_ptr, sizeof(viewname), commversion); + } +#endif +#endif /* support for community based SNMP */ + + if (parsetype == VACM_CREATE_SIMPLE_V3) { + /* support for SNMPv3 user names */ + if (view_ptr) { + sprintf(viewname,"viewUSM%d",commcount); + } + if ( strcmp( token, "authgroup" ) == 0 ) { + strncpy(grpname, community, sizeof(grpname)); + grpname[ sizeof(grpname)-1 ] = 0; + } else { + strncpy(secname, community, sizeof(secname)); + secname[ sizeof(secname)-1 ] = 0; + + /* + * sec->group mapping + */ + /* + * group anonymousGroupNameNUM any anonymousSecNameNUM + */ + snprintf(grpname, sizeof(grpname), "grp%.28s", secname); + for (tmp=grpname; *tmp; tmp++) + if (!isalnum(*tmp)) + *tmp = '_'; + snprintf(line, sizeof(line), + "%s %s %s", grpname, model, secname); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((token, "passing: %s %s\n", "group", line)); + vacm_parse_group("group", line); + } + } else { + snprintf(grpname, sizeof(grpname), "grp%.28s", secname); + for (tmp=grpname; *tmp; tmp++) + if (!isalnum(*tmp)) + *tmp = '_'; + } + + /* + * view definition + */ + /* + * view anonymousViewNUM included OID + */ + if (view_ptr) { + snprintf(line, sizeof(line), "%s included %s", viewname, theoid); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((token, "passing: %s %s\n", "view", line)); + vacm_parse_view("view", line); + } + + /* + * map everything together + */ + if ((viewtypes == VACM_VIEW_READ_BIT) || + (viewtypes == (VACM_VIEW_READ_BIT | VACM_VIEW_WRITE_BIT))) { + /* Use the simple line access command */ + /* + * access anonymousGroupNameNUM "" MODEL AUTHTYPE prefix anonymousViewNUM [none/anonymousViewNUM] [none/anonymousViewNUM] + */ + snprintf(line, sizeof(line), + "%s %s %s %s %s %s %s %s", + grpname, context[0] ? context : "\"\"", + model, authlevel, + (ctxprefix ? "prefix" : "exact"), + viewname, rw, rw); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((token, "passing: %s %s\n", "access", line)); + vacm_parse_access("access", line); + } else { + /* Use one setaccess line per access type */ + /* + * setaccess anonymousGroupNameNUM "" MODEL AUTHTYPE prefix viewname viewval + */ + int i; + DEBUGMSGTL((token, " checking view levels for %x\n", viewtypes)); + for(i = 0; i <= VACM_MAX_VIEWS; i++) { + if (viewtypes & (1 << i)) { + snprintf(line, sizeof(line), + "%s %s %s %s %s %s %s", + grpname, context[0] ? context : "\"\"", + model, authlevel, + (ctxprefix ? "prefix" : "exact"), + se_find_label_in_slist(VACM_VIEW_ENUM_NAME, i), + viewname); + line[ sizeof(line)-1 ] = 0; + DEBUGMSGTL((token, "passing: %s %s\n", "setaccess", line)); + vacm_parse_setaccess("setaccess", line); + } + } + } +} + +int +vacm_standard_views(int majorID, int minorID, void *serverarg, + void *clientarg) +{ + char line[SPRINT_MAX_LEN]; + + memset(line, 0, sizeof(line)); + + snprintf(line, sizeof(line), "_all_ included .0"); + vacm_parse_view("view", line); + snprintf(line, sizeof(line), "_all_ included .1"); + vacm_parse_view("view", line); + snprintf(line, sizeof(line), "_all_ included .2"); + vacm_parse_view("view", line); + + snprintf(line, sizeof(line), "_none_ excluded .0"); + vacm_parse_view("view", line); + snprintf(line, sizeof(line), "_none_ excluded .1"); + vacm_parse_view("view", line); + snprintf(line, sizeof(line), "_none_ excluded .2"); + vacm_parse_view("view", line); + + return SNMP_ERR_NOERROR; +} + +int +vacm_warn_if_not_configured(int majorID, int minorID, void *serverarg, + void *clientarg) +{ + const char * name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_APPTYPE); + const int agent_mode = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_ROLE); + if (NULL==name) + name = "snmpd"; + + if (!vacm_is_configured()) { + /* + * An AgentX subagent relies on the master agent to apply suitable + * access control checks, so doesn't need local VACM configuration. + * The trap daemon has a separate check (see below). + * + * Otherwise, an AgentX master or SNMP standalone agent requires some + * form of VACM configuration. No config means that no incoming + * requests will be accepted, so warn the user accordingly. + */ + if ((MASTER_AGENT == agent_mode) && (strcmp(name, "snmptrapd") != 0)) { + snmp_log(LOG_WARNING, + "Warning: no access control information configured.\n" + " (Config search path: %s)\n" + " It's unlikely this agent can serve any useful purpose in this state.\n" + " Run \"snmpconf -g basic_setup\" to help you " + "configure the %s.conf file for this agent.\n", + get_configuration_directory(), name); + } + + /* + * The trap daemon implements VACM-style access control for incoming + * notifications, but offers a way of turning this off (for backwards + * compatability). Check for this explicitly, and warn if necessary. + * + * NB: The NETSNMP_DS_APP_NO_AUTHORIZATION definition is a duplicate + * of an identical setting in "apps/snmptrapd_ds.h". + * These two need to be kept in synch. + */ +#ifndef NETSNMP_DS_APP_NO_AUTHORIZATION +#define NETSNMP_DS_APP_NO_AUTHORIZATION 17 +#endif + if (!strcmp(name, "snmptrapd") && + !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_APP_NO_AUTHORIZATION)) { + snmp_log(LOG_WARNING, + "Warning: no access control information configured.\n" + " (Config search path: %s)\n" + "This receiver will *NOT* accept any incoming notifications.\n", + get_configuration_directory()); + } + } + return SNMP_ERR_NOERROR; +} + +int +vacm_in_view_callback(int majorID, int minorID, void *serverarg, + void *clientarg) +{ + struct view_parameters *view_parms = + (struct view_parameters *) serverarg; + int retval; + + if (view_parms == NULL) + return 1; + retval = vacm_in_view(view_parms->pdu, view_parms->name, + view_parms->namelen, view_parms->check_subtree); + if (retval != 0) + view_parms->errorcode = retval; + return retval; +} + + +/** + * vacm_in_view: decides if a given PDU can be acted upon + * + * Parameters: + * *pdu + * *name + * namelen + * check_subtree + * + * Returns: + * VACM_SUCCESS(0) On success. + * VACM_NOSECNAME(1) Missing security name. + * VACM_NOGROUP(2) Missing group + * VACM_NOACCESS(3) Missing access + * VACM_NOVIEW(4) Missing view + * VACM_NOTINVIEW(5) Not in view + * VACM_NOSUCHCONTEXT(6) No Such Context + * VACM_SUBTREE_UNKNOWN(7) When testing an entire subtree, UNKNOWN (ie, the entire + * subtree has both allowed and disallowed portions) + * + * Debug output listed as follows: + * \<securityName\> \<groupName\> \<viewName\> \<viewType\> + */ +int +vacm_in_view(netsnmp_pdu *pdu, oid * name, size_t namelen, + int check_subtree) +{ + int viewtype; + + switch (pdu->command) { + case SNMP_MSG_GET: + case SNMP_MSG_GETNEXT: + case SNMP_MSG_GETBULK: + viewtype = VACM_VIEW_READ; + break; + case SNMP_MSG_SET: + viewtype = VACM_VIEW_WRITE; + break; + case SNMP_MSG_TRAP: + case SNMP_MSG_TRAP2: + case SNMP_MSG_INFORM: + viewtype = VACM_VIEW_NOTIFY; + break; + default: + snmp_log(LOG_ERR, "bad msg type in vacm_in_view: %d\n", + pdu->command); + viewtype = VACM_VIEW_READ; + } + return vacm_check_view(pdu, name, namelen, check_subtree, viewtype); +} + +/** + * vacm_check_view: decides if a given PDU can be taken based on a view type + * + * Parameters: + * *pdu + * *name + * namelen + * check_subtree + * viewtype + * + * Returns: + * VACM_SUCCESS(0) On success. + * VACM_NOSECNAME(1) Missing security name. + * VACM_NOGROUP(2) Missing group + * VACM_NOACCESS(3) Missing access + * VACM_NOVIEW(4) Missing view + * VACM_NOTINVIEW(5) Not in view + * VACM_NOSUCHCONTEXT(6) No Such Context + * VACM_SUBTREE_UNKNOWN(7) When testing an entire subtree, UNKNOWN (ie, the entire + * subtree has both allowed and disallowed portions) + * + * Debug output listed as follows: + * \<securityName\> \<groupName\> \<viewName\> \<viewType\> + */ +int +vacm_check_view(netsnmp_pdu *pdu, oid * name, size_t namelen, + int check_subtree, int viewtype) +{ + struct vacm_accessEntry *ap; + struct vacm_groupEntry *gp; + struct vacm_viewEntry *vp; + char vacm_default_context[1] = ""; + char *contextName = vacm_default_context; + char *sn = NULL; + char *vn; + char *pdu_community; + + /* + * len defined by the vacmContextName object + */ +#define CONTEXTNAMEINDEXLEN 32 + char contextNameIndex[CONTEXTNAMEINDEXLEN + 1]; + +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) +#if defined(NETSNMP_DISABLE_SNMPV1) + if (pdu->version == SNMP_VERSION_2c) +#else +#if defined(NETSNMP_DISABLE_SNMPV2C) + if (pdu->version == SNMP_VERSION_1) +#else + if (pdu->version == SNMP_VERSION_1 || pdu->version == SNMP_VERSION_2c) +#endif +#endif + { + pdu_community = pdu->community; + if (!pdu_community) + pdu_community = ""; + if (snmp_get_do_debugging()) { + char *buf; + if (pdu->community) { + buf = (char *) malloc(1 + pdu->community_len); + memcpy(buf, pdu->community, pdu->community_len); + buf[pdu->community_len] = '\0'; + } else { + DEBUGMSGTL(("mibII/vacm_vars", "NULL community")); + buf = strdup("NULL"); + } + + DEBUGMSGTL(("mibII/vacm_vars", + "vacm_in_view: ver=%d, community=%s\n", + pdu->version, buf)); + free(buf); + } + + /* + * Okay, if this PDU was received from a UDP or a TCP transport then + * ask the transport abstraction layer to map its source address and + * community string to a security name for us. + */ + + if (pdu->tDomain == netsnmpUDPDomain +#ifdef NETSNMP_TRANSPORT_TCP_DOMAIN + || pdu->tDomain == netsnmp_snmpTCPDomain +#endif + ) { + if (!netsnmp_udp_getSecName(pdu->transport_data, + pdu->transport_data_length, + (char *) pdu_community, + pdu->community_len, &sn, + &contextName)) { + /* + * There are no com2sec entries. + */ + sn = NULL; + } + /* force the community -> context name mapping here */ + SNMP_FREE(pdu->contextName); + pdu->contextName = strdup(contextName); + pdu->contextNameLen = strlen(contextName); +#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN + } else if (pdu->tDomain == netsnmp_UDPIPv6Domain +#ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN + || pdu->tDomain == netsnmp_TCPIPv6Domain +#endif + ) { + if (!netsnmp_udp6_getSecName(pdu->transport_data, + pdu->transport_data_length, + (char *) pdu_community, + pdu->community_len, &sn, + &contextName)) { + /* + * There are no com2sec entries. + */ + sn = NULL; + } + /* force the community -> context name mapping here */ + SNMP_FREE(pdu->contextName); + pdu->contextName = strdup(contextName); + pdu->contextNameLen = strlen(contextName); +#endif +#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN + } else if (pdu->tDomain == netsnmp_UnixDomain){ + if (!netsnmp_unix_getSecName(pdu->transport_data, + pdu->transport_data_length, + (char *) pdu_community, + pdu->community_len, &sn, + &contextName)) { + sn = NULL; + } + /* force the community -> context name mapping here */ + SNMP_FREE(pdu->contextName); + pdu->contextName = strdup(contextName); + pdu->contextNameLen = strlen(contextName); +#endif + } else { + /* + * Map other <community, transport-address> pairs to security names + * here. For now just let non-IPv4 transport always succeed. + * + * WHAAAATTTT. No, we don't let non-IPv4 transports + * succeed! You must fix this to make it usable, sorry. + * From a security standpoint this is insane. -- Wes + */ + /** @todo alternate com2sec mappings for non v4 transports. + Should be implemented via registration */ + sn = NULL; + } + + } else +#endif /* support for community based SNMP */ + if (find_sec_mod(pdu->securityModel)) { + /* + * any legal defined v3 security model + */ + DEBUGMSG(("mibII/vacm_vars", + "vacm_in_view: ver=%d, model=%d, secName=%s\n", + pdu->version, pdu->securityModel, pdu->securityName)); + sn = pdu->securityName; + contextName = pdu->contextName; + } else { + sn = NULL; + } + + if (sn == NULL) { +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + snmp_increment_statistic(STAT_SNMPINBADCOMMUNITYNAMES); +#endif + DEBUGMSGTL(("mibII/vacm_vars", + "vacm_in_view: No security name found\n")); + return VACM_NOSECNAME; + } + + if (pdu->contextNameLen > CONTEXTNAMEINDEXLEN) { + DEBUGMSGTL(("mibII/vacm_vars", + "vacm_in_view: bad ctxt length %d\n", + pdu->contextNameLen)); + return VACM_NOSUCHCONTEXT; + } + /* + * NULL termination of the pdu field is ugly here. Do in PDU parsing? + */ + if (pdu->contextName) + strncpy(contextNameIndex, pdu->contextName, pdu->contextNameLen); + else + contextNameIndex[0] = '\0'; + + contextNameIndex[pdu->contextNameLen] = '\0'; + if (!netsnmp_subtree_find_first(contextNameIndex)) { + /* + * rfc 3415 section 3.2, step 1 + * no such context here; return no such context error + */ + DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: no such ctxt \"%s\"\n", + contextNameIndex)); + return VACM_NOSUCHCONTEXT; + } + + DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: sn=%s", sn)); + + gp = vacm_getGroupEntry(pdu->securityModel, sn); + if (gp == NULL) { + DEBUGMSG(("mibII/vacm_vars", "\n")); + return VACM_NOGROUP; + } + DEBUGMSG(("mibII/vacm_vars", ", gn=%s", gp->groupName)); + + ap = vacm_getAccessEntry(gp->groupName, contextNameIndex, + pdu->securityModel, pdu->securityLevel); + if (ap == NULL) { + DEBUGMSG(("mibII/vacm_vars", "\n")); + return VACM_NOACCESS; + } + + if (name == 0) { /* only check the setup of the vacm for the request */ + DEBUGMSG(("mibII/vacm_vars", ", Done checking setup\n")); + return VACM_SUCCESS; + } + + if (viewtype < 0 || viewtype >= VACM_MAX_VIEWS) { + DEBUGMSG(("mibII/vacm_vars", " illegal view type\n")); + return VACM_NOACCESS; + } + vn = ap->views[viewtype]; + DEBUGMSG(("mibII/vacm_vars", ", vn=%s", vn)); + + if (check_subtree) { + DEBUGMSG(("mibII/vacm_vars", "\n")); + return vacm_checkSubtree(vn, name, namelen); + } + + vp = vacm_getViewEntry(vn, name, namelen, VACM_MODE_FIND); + + if (vp == NULL) { + DEBUGMSG(("mibII/vacm_vars", "\n")); + return VACM_NOVIEW; + } + DEBUGMSG(("mibII/vacm_vars", ", vt=%d\n", vp->viewType)); + + if (vp->viewType == SNMP_VIEW_EXCLUDED) { +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) +#if defined(NETSNMP_DISABLE_SNMPV1) + if (pdu->version == SNMP_VERSION_2c) +#else +#if defined(NETSNMP_DISABLE_SNMPV2C) + if (pdu->version == SNMP_VERSION_1) +#else + if (pdu->version == SNMP_VERSION_1 || pdu->version == SNMP_VERSION_2c) +#endif +#endif + { + snmp_increment_statistic(STAT_SNMPINBADCOMMUNITYUSES); + } +#endif + return VACM_NOTINVIEW; + } + + return VACM_SUCCESS; + +} /* end vacm_in_view() */ + + |