blob: 8b2ae778b7f25c8722f6a4df579352c6a0df3a85 [file] [log] [blame]
/*
* 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 "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 [context]]]");
/* easy user auth handler */
snmpd_register_config_handler("authuser",
vacm_parse_authuser,
NULL, "authtype1,authtype2 [-s secmodel] user [noauth|auth|priv [oid|-V view [context]]]");
/* easy group auth handler */
snmpd_register_config_handler("authgroup",
vacm_parse_authuser,
NULL, "authtype1,authtype2 [-s secmodel] group [noauth|auth|priv [oid|-V view [context]]]");
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_PRE_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|-V view [context]]]");
snmpd_register_config_handler("rocommunity", vacm_parse_rocommunity, NULL,
"community [default|hostname|network/bits [oid|-V view [context]]]");
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
snmpd_register_config_handler("rwcommunity6", vacm_parse_rwcommunity6, NULL,
"community [default|hostname|network/bits [oid|-V view [context]]]");
snmpd_register_config_handler("rocommunity6", vacm_parse_rocommunity6, NULL,
"community [default|hostname|network/bits [oid|-V view [context]]]");
#endif
#endif /* support for community based SNMP */
snmpd_register_config_handler("rwuser", vacm_parse_rwuser, NULL,
"user [noauth|auth|priv [oid|-V view [context]]]");
snmpd_register_config_handler("rouser", vacm_parse_rouser, NULL,
"user [noauth|auth|priv [oid|-V view [context]]]");
}
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[VACMSTRINGLEN], model[VACMSTRINGLEN], security[VACMSTRINGLEN];
int imodel;
struct vacm_groupEntry *gp = NULL;
char *st;
st = copy_nword(param, group, sizeof(group)-1);
st = copy_nword(st, model, sizeof(model)-1);
st = copy_nword(st, security, sizeof(security)-1);
if (group[0] == 0) {
config_perror("missing GROUP parameter");
return;
}
if (model[0] == 0) {
config_perror("missing MODEL parameter");
return;
}
if (security[0] == 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;
}
strlcpy(gp->groupName, group, sizeof(gp->groupName));
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, *tmp;
const char *context;
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 = tmp = strtok_r(NULL, " \t\n", &st);
if (tmp) {
tmp = (tmp + strlen(tmp)-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;
}
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, const char *community, const char *addressname,
const char *publishtoken,
void (*parser)(const char *, char *),
char *secname, size_t secname_len,
char *viewname, size_t viewname_len, int version,
const char *context)
{
char line[SPRINT_MAX_LEN];
/*
* com2sec6|comsec [-Cn CONTEXT] 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';
}
if ( context && *context )
snprintf(line, sizeof(line), "-Cn %s %s %s '%s'",
context, secname, addressname, community);
else
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, context);
}
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
if (parsetype == VACM_CREATE_SIMPLE_COMUNIX ||
parsetype == VACM_CREATE_SIMPLE_COM) {
if (context && *context)
snprintf(line, sizeof(line), "-Cn %s %s %s '%s'",
context, secname, addressname, community);
else
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, context);
}
#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 ) {
strlcpy(grpname, community, sizeof(grpname));
} else {
strlcpy(secname, community, sizeof(secname));
/*
* 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)
{
return vacm_check_view_contents(pdu, name, namelen, check_subtree, viewtype,
VACM_CHECK_VIEW_CONTENTS_NO_FLAGS);
}
int
vacm_check_view_contents(netsnmp_pdu *pdu, oid * name, size_t namelen,
int check_subtree, int viewtype, int flags)
{
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;
const 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 = (const char *) 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=%ld, 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,
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,
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,
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=%ld, 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",
(int)pdu->contextNameLen));
return VACM_NOSUCHCONTEXT;
}
/*
* NULL termination of the pdu field is ugly here. Do in PDU parsing?
*/
if (pdu->contextName)
memcpy(contextNameIndex, pdu->contextName, pdu->contextNameLen);
else
contextNameIndex[0] = '\0';
contextNameIndex[pdu->contextNameLen] = '\0';
if (!(flags & VACM_CHECK_VIEW_CONTENTS_DNE_CONTEXT_OK) &&
!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 == NULL) { /* 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() */