net-snmp/snmplib/vacm.c

1360 lines
43 KiB
C

/* 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.
*
* Portions of this file are copyrighted by:
* Copyright (c) 2016 VMware, Inc. All rights reserved.
* Use is subject to license terms specified in the COPYING file
* distributed with the Net-SNMP package.
*/
/*
* vacm.c
*
* SNMPv3 View-based Access Control Model
*/
#include <net-snmp/net-snmp-config.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <ctype.h>
#include <net-snmp/types.h>
#include <net-snmp/output_api.h>
#include <net-snmp/config_api.h>
#include <net-snmp/library/snmp_api.h>
#include <net-snmp/library/system.h> /* strlcpy() */
#include <net-snmp/library/tools.h>
#include <net-snmp/library/vacm.h>
static struct vacm_viewEntry *viewList = NULL, *viewScanPtr = NULL;
static struct vacm_accessEntry *accessList = NULL, *accessScanPtr = NULL;
static struct vacm_groupEntry *groupList = NULL, *groupScanPtr = NULL;
/*
* Macro to extend view masks with 1 bits when shorter than subtree lengths
* REF: vacmViewTreeFamilyMask [RFC3415], snmpNotifyFilterMask [RFC3413]
*/
#define VIEW_MASK(viewPtr, idx, mask) \
((idx >= viewPtr->viewMaskLen) ? mask : (viewPtr->viewMask[idx] & mask))
/**
* Initilizes the VACM code.
* Specifically:
* - adds a set of enums mapping view numbers to human readable names
*/
void
init_vacm(void)
{
/* views for access via get/set/send-notifications */
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("read"),
VACM_VIEW_READ);
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("write"),
VACM_VIEW_WRITE);
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("notify"),
VACM_VIEW_NOTIFY);
/* views for permissions when receiving notifications */
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("log"),
VACM_VIEW_LOG);
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("execute"),
VACM_VIEW_EXECUTE);
se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("net"),
VACM_VIEW_NET);
}
void
vacm_save(const char *token, const char *type)
{
struct vacm_viewEntry *vptr;
struct vacm_accessEntry *aptr;
struct vacm_groupEntry *gptr;
int i;
for (vptr = viewList; vptr != NULL; vptr = vptr->next) {
if (vptr->viewStorageType == ST_NONVOLATILE)
vacm_save_view(vptr, token, type);
}
for (aptr = accessList; aptr != NULL; aptr = aptr->next) {
if (aptr->storageType == ST_NONVOLATILE) {
/* Store the standard views (if set) */
if ( aptr->views[VACM_VIEW_READ ][0] ||
aptr->views[VACM_VIEW_WRITE ][0] ||
aptr->views[VACM_VIEW_NOTIFY][0] )
vacm_save_access(aptr, token, type);
/* Store any other (valid) access views */
for ( i=VACM_VIEW_NOTIFY+1; i<VACM_MAX_VIEWS; i++ ) {
if ( aptr->views[i][0] )
vacm_save_auth_access(aptr, token, type, i);
}
}
}
for (gptr = groupList; gptr != NULL; gptr = gptr->next) {
if (gptr->storageType == ST_NONVOLATILE)
vacm_save_group(gptr, token, type);
}
}
/*
* vacm_save_view(): saves a view entry to the persistent cache
*/
void
vacm_save_view(struct vacm_viewEntry *view, const char *token,
const char *type)
{
char line[4096];
char *cptr;
memset(line, 0, sizeof(line));
snprintf(line, sizeof(line), "%s%s %d %d %d ", token, "View",
view->viewStatus, view->viewStorageType, view->viewType);
line[ sizeof(line)-1 ] = 0;
cptr = &line[strlen(line)]; /* the NULL */
cptr =
read_config_save_octet_string(cptr, (u_char *) view->viewName + 1,
view->viewName[0]);
*cptr++ = ' ';
cptr =
read_config_save_objid(cptr, view->viewSubtree+1,
view->viewSubtreeLen-1);
*cptr++ = ' ';
cptr = read_config_save_octet_string(cptr, (u_char *) view->viewMask,
view->viewMaskLen);
read_config_store(type, line);
}
void
vacm_parse_config_view(const char *token, const char *line)
{
struct vacm_viewEntry view;
struct vacm_viewEntry *vptr;
char *viewName = (char *) &view.viewName;
oid *viewSubtree = (oid *) & view.viewSubtree;
u_char *viewMask;
size_t len;
view.viewStatus = atoi(line);
line = skip_token_const(line);
view.viewStorageType = atoi(line);
line = skip_token_const(line);
view.viewType = atoi(line);
line = skip_token_const(line);
len = sizeof(view.viewName);
line =
read_config_read_octet_string(line, (u_char **) & viewName, &len);
view.viewSubtreeLen = MAX_OID_LEN + 1;
line =
read_config_read_objid_const(line, (oid **) & viewSubtree,
&view.viewSubtreeLen);
vptr =
vacm_createViewEntry(view.viewName, view.viewSubtree,
view.viewSubtreeLen);
if (!vptr) {
return;
}
vptr->viewStatus = view.viewStatus;
vptr->viewStorageType = view.viewStorageType;
vptr->viewType = view.viewType;
viewMask = vptr->viewMask;
vptr->viewMaskLen = sizeof(vptr->viewMask);
line =
read_config_read_octet_string(line, &viewMask, &vptr->viewMaskLen);
}
/*
* vacm_save_access(): saves an access entry to the persistent cache
*/
void
vacm_save_access(struct vacm_accessEntry *access_entry, const char *token,
const char *type)
{
char line[4096];
char *cptr;
memset(line, 0, sizeof(line));
snprintf(line, sizeof(line), "%s%s %d %d %d %d %d ",
token, "Access", access_entry->status,
access_entry->storageType, access_entry->securityModel,
access_entry->securityLevel, access_entry->contextMatch);
line[ sizeof(line)-1 ] = 0;
cptr = &line[strlen(line)]; /* the NULL */
cptr =
read_config_save_octet_string(cptr,
(u_char *) access_entry->groupName + 1,
access_entry->groupName[0] + 1);
*cptr++ = ' ';
cptr =
read_config_save_octet_string(cptr,
(u_char *) access_entry->contextPrefix + 1,
access_entry->contextPrefix[0] + 1);
*cptr++ = ' ';
cptr = read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_READ],
strlen(access_entry->views[VACM_VIEW_READ]) + 1);
*cptr++ = ' ';
cptr =
read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_WRITE],
strlen(access_entry->views[VACM_VIEW_WRITE]) + 1);
*cptr++ = ' ';
cptr =
read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_NOTIFY],
strlen(access_entry->views[VACM_VIEW_NOTIFY]) + 1);
read_config_store(type, line);
}
void
vacm_save_auth_access(struct vacm_accessEntry *access_entry,
const char *token, const char *type, int authtype)
{
char line[4096];
char *cptr;
memset(line, 0, sizeof(line));
snprintf(line, sizeof(line), "%s%s %d %d %d %d %d ",
token, "AuthAccess", access_entry->status,
access_entry->storageType, access_entry->securityModel,
access_entry->securityLevel, access_entry->contextMatch);
line[ sizeof(line)-1 ] = 0;
cptr = &line[strlen(line)]; /* the NULL */
cptr =
read_config_save_octet_string(cptr,
(u_char *) access_entry->groupName + 1,
access_entry->groupName[0] + 1);
*cptr++ = ' ';
cptr =
read_config_save_octet_string(cptr,
(u_char *) access_entry->contextPrefix + 1,
access_entry->contextPrefix[0] + 1);
snprintf(cptr, sizeof(line)-(cptr-line), " %d ", authtype);
while ( *cptr )
cptr++;
*cptr++ = ' ';
cptr = read_config_save_octet_string(cptr,
(u_char *)access_entry->views[authtype],
strlen(access_entry->views[authtype]) + 1);
read_config_store(type, line);
}
char *
_vacm_parse_config_access_common(struct vacm_accessEntry **aptr,
const char *line)
{
struct vacm_accessEntry access;
char *cPrefix = (char *) &access.contextPrefix;
char *gName = (char *) &access.groupName;
size_t len;
access.status = atoi(line);
line = skip_token_const(line);
access.storageType = atoi(line);
line = skip_token_const(line);
access.securityModel = atoi(line);
line = skip_token_const(line);
access.securityLevel = atoi(line);
line = skip_token_const(line);
access.contextMatch = atoi(line);
line = skip_token_const(line);
len = sizeof(access.groupName);
line = read_config_read_octet_string(line, (u_char **) &gName, &len);
len = sizeof(access.contextPrefix);
line = read_config_read_octet_string(line, (u_char **) &cPrefix, &len);
*aptr = vacm_getAccessEntry(access.groupName,
access.contextPrefix,
access.securityModel,
access.securityLevel);
if (!*aptr)
*aptr = vacm_createAccessEntry(access.groupName,
access.contextPrefix,
access.securityModel,
access.securityLevel);
if (!*aptr)
return NULL;
(*aptr)->status = access.status;
(*aptr)->storageType = access.storageType;
(*aptr)->securityModel = access.securityModel;
(*aptr)->securityLevel = access.securityLevel;
(*aptr)->contextMatch = access.contextMatch;
return NETSNMP_REMOVE_CONST(char *, line);
}
void
vacm_parse_config_access(const char *token, const char *line)
{
struct vacm_accessEntry *aptr;
char *readView, *writeView, *notifyView;
size_t len;
line = _vacm_parse_config_access_common(&aptr, line);
if (!line)
return;
readView = (char *) aptr->views[VACM_VIEW_READ];
len = sizeof(aptr->views[VACM_VIEW_READ]);
line =
read_config_read_octet_string(line, (u_char **) & readView, &len);
writeView = (char *) aptr->views[VACM_VIEW_WRITE];
len = sizeof(aptr->views[VACM_VIEW_WRITE]);
line =
read_config_read_octet_string(line, (u_char **) & writeView, &len);
notifyView = (char *) aptr->views[VACM_VIEW_NOTIFY];
len = sizeof(aptr->views[VACM_VIEW_NOTIFY]);
line =
read_config_read_octet_string(line, (u_char **) & notifyView,
&len);
}
void
vacm_parse_config_auth_access(const char *token, const char *line)
{
struct vacm_accessEntry *aptr;
int authtype;
char *view;
size_t len;
line = _vacm_parse_config_access_common(&aptr, line);
if (!line)
return;
authtype = atoi(line);
line = skip_token_const(line);
view = (char *) aptr->views[authtype];
len = sizeof(aptr->views[authtype]);
line = read_config_read_octet_string(line, (u_char **) & view, &len);
}
/*
* vacm_save_group(): saves a group entry to the persistent cache
*/
void
vacm_save_group(struct vacm_groupEntry *group_entry, const char *token,
const char *type)
{
char line[4096];
char *cptr;
memset(line, 0, sizeof(line));
snprintf(line, sizeof(line), "%s%s %d %d %d ",
token, "Group", group_entry->status,
group_entry->storageType, group_entry->securityModel);
line[ sizeof(line)-1 ] = 0;
cptr = &line[strlen(line)]; /* the NULL */
cptr =
read_config_save_octet_string(cptr,
(u_char *) group_entry->securityName + 1,
group_entry->securityName[0] + 1);
*cptr++ = ' ';
cptr = read_config_save_octet_string(cptr, (u_char *) group_entry->groupName,
strlen(group_entry->groupName) + 1);
read_config_store(type, line);
}
void
vacm_parse_config_group(const char *token, const char *line)
{
struct vacm_groupEntry group;
struct vacm_groupEntry *gptr;
char *securityName = (char *) &group.securityName;
char *groupName;
size_t len;
group.status = atoi(line);
line = skip_token_const(line);
group.storageType = atoi(line);
line = skip_token_const(line);
group.securityModel = atoi(line);
line = skip_token_const(line);
len = sizeof(group.securityName);
line =
read_config_read_octet_string(line, (u_char **) & securityName,
&len);
gptr = vacm_createGroupEntry(group.securityModel, group.securityName);
if (!gptr)
return;
gptr->status = group.status;
gptr->storageType = group.storageType;
groupName = (char *) gptr->groupName;
len = sizeof(group.groupName);
line =
read_config_read_octet_string(line, (u_char **) & groupName, &len);
}
struct vacm_viewEntry *
netsnmp_view_get(struct vacm_viewEntry *head, const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen, int mode)
{
struct vacm_viewEntry *vp, *vpret = NULL;
char view[VACMSTRINGLEN];
int found, glen;
int count=0;
glen = (int) strlen(viewName);
if (glen < 0 || glen > VACM_MAX_STRING)
return NULL;
view[0] = glen;
strlcpy(view + 1, viewName, sizeof(view) - 1);
for (vp = head; vp; vp = vp->next) {
if (!memcmp(view, vp->viewName, glen + 1)
&& viewSubtreeLen >= (vp->viewSubtreeLen - 1)) {
int mask = 0x80;
unsigned int oidpos, maskpos = 0;
found = 1;
for (oidpos = 0;
found && oidpos < vp->viewSubtreeLen - 1;
oidpos++) {
if (mode==VACM_MODE_IGNORE_MASK || (VIEW_MASK(vp, maskpos, mask) != 0)) {
if (viewSubtree[oidpos] !=
vp->viewSubtree[oidpos + 1])
found = 0;
}
if (mask == 1) {
mask = 0x80;
maskpos++;
} else
mask >>= 1;
}
if (found) {
/*
* match successful, keep this node if its longer than
* the previous or (equal and lexicographically greater
* than the previous).
*/
count++;
if (mode == VACM_MODE_CHECK_SUBTREE) {
vpret = vp;
} else if (vpret == NULL
|| vp->viewSubtreeLen > vpret->viewSubtreeLen
|| (vp->viewSubtreeLen == vpret->viewSubtreeLen
&& snmp_oid_compare(vp->viewSubtree + 1,
vp->viewSubtreeLen - 1,
vpret->viewSubtree + 1,
vpret->viewSubtreeLen - 1) >
0)) {
vpret = vp;
}
}
}
}
DEBUGMSGTL(("vacm:getView", ", %s\n", (vpret) ? "found" : "none"));
if (mode == VACM_MODE_CHECK_SUBTREE && count > 1) {
return NULL;
}
return vpret;
}
/*******************************************************************o-o******
* netsnmp_view_exists
*
* Check to see if a view with the given name exists.
*
* Parameters:
* viewName - Name of view to check
*
* Returns 0 if the view does not exist. Otherwise, it returns the number
* of OID rows for the given name.
*/
int
netsnmp_view_exists(struct vacm_viewEntry *head, const char *viewName)
{
struct vacm_viewEntry *vp;
char view[VACMSTRINGLEN];
int len, count = 0;
len = (int) strlen(viewName);
if (len < 0 || len > VACM_MAX_STRING)
return 0;
view[0] = len;
strcpy(view + 1, viewName);
DEBUGMSGTL(("9:vacm:view_exists", "checking %s\n", viewName));
for (vp = head; vp; vp = vp->next) {
if (memcmp(view, vp->viewName, len + 1) == 0)
++count;
}
return count;
}
/*******************************************************************o-o******
* vacm_checkSubtree
*
* Check to see if everything within a subtree is in view, not in view,
* or possibly both.
*
* Parameters:
* *viewName - Name of view to check
* *viewSubtree - OID of subtree
* viewSubtreeLen - length of subtree OID
*
* Returns:
* VACM_SUCCESS The OID is included in the view.
* VACM_NOTINVIEW If no entry in the view list includes the
* provided OID, or the OID is explicitly excluded
* from the view.
* VACM_SUBTREE_UNKNOWN The entire subtree has both allowed and disallowed
* portions.
*/
int
netsnmp_view_subtree_check(struct vacm_viewEntry *head, const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
struct vacm_viewEntry *vp, *vpShorter = NULL, *vpLonger = NULL;
char view[VACMSTRINGLEN];
int found, glen;
glen = (int) strlen(viewName);
if (glen < 0 || glen > VACM_MAX_STRING)
return VACM_NOTINVIEW;
view[0] = glen;
strlcpy(view + 1, viewName, sizeof(view) - 1);
DEBUGMSGTL(("9:vacm:checkSubtree", "view %s\n", viewName));
for (vp = head; vp; vp = vp->next) {
if (!memcmp(view, vp->viewName, glen + 1)) {
/*
* If the subtree defined in the view is shorter than or equal
* to the subtree we are comparing, then it might envelop the
* subtree we are comparing against.
*/
if (viewSubtreeLen >= (vp->viewSubtreeLen - 1)) {
int mask = 0x80;
unsigned int oidpos, maskpos = 0;
found = 1;
/*
* check the mask
*/
for (oidpos = 0;
found && oidpos < vp->viewSubtreeLen - 1;
oidpos++) {
if (VIEW_MASK(vp, maskpos, mask) != 0) {
if (viewSubtree[oidpos] !=
vp->viewSubtree[oidpos + 1])
found = 0;
}
if (mask == 1) {
mask = 0x80;
maskpos++;
} else
mask >>= 1;
}
if (found) {
/*
* match successful, keep this node if it's longer than
* the previous or (equal and lexicographically greater
* than the previous).
*/
DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched?\n", vp->viewName));
if (vpShorter == NULL
|| vp->viewSubtreeLen > vpShorter->viewSubtreeLen
|| (vp->viewSubtreeLen == vpShorter->viewSubtreeLen
&& snmp_oid_compare(vp->viewSubtree + 1,
vp->viewSubtreeLen - 1,
vpShorter->viewSubtree + 1,
vpShorter->viewSubtreeLen - 1) >
0)) {
vpShorter = vp;
}
}
}
/*
* If the subtree defined in the view is longer than the
* subtree we are comparing, then it might ambiguate our
* response.
*/
else {
int mask = 0x80;
unsigned int oidpos, maskpos = 0;
found = 1;
/*
* check the mask up to the length of the provided subtree
*/
for (oidpos = 0;
found && oidpos < viewSubtreeLen;
oidpos++) {
if (VIEW_MASK(vp, maskpos, mask) != 0) {
if (viewSubtree[oidpos] !=
vp->viewSubtree[oidpos + 1])
found = 0;
}
if (mask == 1) {
mask = 0x80;
maskpos++;
} else
mask >>= 1;
}
if (found) {
/*
* match successful. If we already found a match
* with a different view type, then parts of the subtree
* are included and others are excluded, so return UNKNOWN.
*/
DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched?\n", vp->viewName));
if (vpLonger != NULL
&& (vpLonger->viewType != vp->viewType)) {
DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "unknown"));
return VACM_SUBTREE_UNKNOWN;
}
else if (vpLonger == NULL) {
vpLonger = vp;
}
}
}
}
}
DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched\n", viewName));
/*
* If we found a matching view subtree with a longer OID than the provided
* OID, check to see if its type is consistent with any matching view
* subtree we may have found with a shorter OID than the provided OID.
*
* The view type of the longer OID is inconsistent with the shorter OID in
* either of these two cases:
* 1) No matching shorter OID was found and the view type of the longer
* OID is INCLUDE.
* 2) A matching shorter ID was found and its view type doesn't match
* the view type of the longer OID.
*/
if (vpLonger != NULL) {
if ((!vpShorter && vpLonger->viewType != SNMP_VIEW_EXCLUDED)
|| (vpShorter && vpLonger->viewType != vpShorter->viewType)) {
DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "unknown"));
return VACM_SUBTREE_UNKNOWN;
}
}
if (vpShorter && vpShorter->viewType != SNMP_VIEW_EXCLUDED) {
DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "included"));
return VACM_SUCCESS;
}
DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "excluded"));
return VACM_NOTINVIEW;
}
void
vacm_scanViewInit(void)
{
viewScanPtr = viewList;
}
struct vacm_viewEntry *
vacm_scanViewNext(void)
{
struct vacm_viewEntry *returnval = viewScanPtr;
if (viewScanPtr)
viewScanPtr = viewScanPtr->next;
return returnval;
}
struct vacm_viewEntry *
netsnmp_view_create(struct vacm_viewEntry **head, const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
struct vacm_viewEntry *vp, *lp, *op = NULL;
int cmp, cmp2, glen;
glen = (int) strlen(viewName);
if (glen < 0 || glen > VACM_MAX_STRING || viewSubtreeLen > MAX_OID_LEN)
return NULL;
vp = (struct vacm_viewEntry *) calloc(1,
sizeof(struct vacm_viewEntry));
if (vp == NULL)
return NULL;
vp->reserved =
(struct vacm_viewEntry *) calloc(1, sizeof(struct vacm_viewEntry));
if (vp->reserved == NULL) {
free(vp);
return NULL;
}
vp->viewName[0] = glen;
strlcpy(vp->viewName + 1, viewName, sizeof(vp->viewName) - 1);
vp->viewSubtree[0] = viewSubtreeLen;
memcpy(vp->viewSubtree + 1, viewSubtree, viewSubtreeLen * sizeof(oid));
vp->viewSubtreeLen = viewSubtreeLen + 1;
lp = *head;
while (lp) {
cmp = memcmp(lp->viewName, vp->viewName, glen + 1);
cmp2 = snmp_oid_compare(lp->viewSubtree, lp->viewSubtreeLen,
vp->viewSubtree, vp->viewSubtreeLen);
if (cmp == 0 && cmp2 > 0)
break;
if (cmp > 0)
break;
op = lp;
lp = lp->next;
}
vp->next = lp;
if (op)
op->next = vp;
else
*head = vp;
return vp;
}
void
netsnmp_view_destroy(struct vacm_viewEntry **head, const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
struct vacm_viewEntry *vp, *lastvp = NULL;
if ((*head) && !strcmp((*head)->viewName + 1, viewName)
&& (*head)->viewSubtreeLen == viewSubtreeLen
&& !memcmp((char *) (*head)->viewSubtree, (char *) viewSubtree,
viewSubtreeLen * sizeof(oid))) {
vp = (*head);
(*head) = (*head)->next;
} else {
for (vp = (*head); vp; vp = vp->next) {
if (!strcmp(vp->viewName + 1, viewName)
&& vp->viewSubtreeLen == viewSubtreeLen
&& !memcmp((char *) vp->viewSubtree, (char *) viewSubtree,
viewSubtreeLen * sizeof(oid)))
break;
lastvp = vp;
}
if (!vp || !lastvp)
return;
lastvp->next = vp->next;
}
if (vp->reserved)
free(vp->reserved);
free(vp);
return;
}
void
netsnmp_view_clear(struct vacm_viewEntry **head)
{
struct vacm_viewEntry *vp;
while ((vp = (*head))) {
(*head) = vp->next;
if (vp->reserved)
free(vp->reserved);
free(vp);
}
}
struct vacm_groupEntry *
vacm_getGroupEntry(int securityModel, const char *securityName)
{
struct vacm_groupEntry *vp;
char secname[VACMSTRINGLEN];
int glen;
glen = (int) strlen(securityName);
if (glen < 0 || glen > VACM_MAX_STRING)
return NULL;
secname[0] = glen;
strlcpy(secname + 1, securityName, sizeof(secname) - 1);
for (vp = groupList; vp; vp = vp->next) {
if ((securityModel == vp->securityModel
|| vp->securityModel == SNMP_SEC_MODEL_ANY)
&& !memcmp(vp->securityName, secname, glen + 1))
return vp;
}
return NULL;
}
void
vacm_scanGroupInit(void)
{
groupScanPtr = groupList;
}
struct vacm_groupEntry *
vacm_scanGroupNext(void)
{
struct vacm_groupEntry *returnval = groupScanPtr;
if (groupScanPtr)
groupScanPtr = groupScanPtr->next;
return returnval;
}
struct vacm_groupEntry *
vacm_createGroupEntry(int securityModel, const char *securityName)
{
struct vacm_groupEntry *gp, *lg, *og;
int cmp, glen;
glen = (int) strlen(securityName);
if (glen < 0 || glen > VACM_MAX_STRING)
return NULL;
gp = (struct vacm_groupEntry *) calloc(1,
sizeof(struct vacm_groupEntry));
if (gp == NULL)
return NULL;
gp->reserved =
(struct vacm_groupEntry *) calloc(1,
sizeof(struct vacm_groupEntry));
if (gp->reserved == NULL) {
free(gp);
return NULL;
}
gp->securityModel = securityModel;
gp->securityName[0] = glen;
strlcpy(gp->securityName + 1, securityName, sizeof(gp->securityName) - 1);
lg = groupList;
og = NULL;
while (lg) {
if (lg->securityModel > securityModel)
break;
if (lg->securityModel == securityModel &&
(cmp =
memcmp(lg->securityName, gp->securityName, glen + 1)) > 0)
break;
/*
* if (lg->securityModel == securityModel && cmp == 0) abort();
*/
og = lg;
lg = lg->next;
}
gp->next = lg;
if (og == NULL)
groupList = gp;
else
og->next = gp;
return gp;
}
void
vacm_destroyGroupEntry(int securityModel, const char *securityName)
{
struct vacm_groupEntry *vp, *lastvp = NULL;
if (groupList && groupList->securityModel == securityModel
&& !strcmp(groupList->securityName + 1, securityName)) {
vp = groupList;
groupList = groupList->next;
} else {
for (vp = groupList; vp; vp = vp->next) {
if (vp->securityModel == securityModel
&& !strcmp(vp->securityName + 1, securityName))
break;
lastvp = vp;
}
if (!vp || !lastvp)
return;
lastvp->next = vp->next;
}
if (vp->reserved)
free(vp->reserved);
free(vp);
return;
}
void
vacm_destroyAllGroupEntries(void)
{
struct vacm_groupEntry *gp;
while ((gp = groupList)) {
groupList = gp->next;
if (gp->reserved)
free(gp->reserved);
free(gp);
}
}
struct vacm_accessEntry *
_vacm_choose_best( struct vacm_accessEntry *current,
struct vacm_accessEntry *candidate)
{
/*
* RFC 3415: vacmAccessTable:
* 2) if this set has [more than] one member, ...
* it comes down to deciding how to weight the
* preferences between ContextPrefixes,
* SecurityModels, and SecurityLevels
*/
if (( !current ) ||
/* a) if the subset of entries with securityModel
* matching the securityModel in the message is
* not empty, then discard the rest
*/
( current->securityModel == SNMP_SEC_MODEL_ANY &&
candidate->securityModel != SNMP_SEC_MODEL_ANY ) ||
/* b) if the subset of entries with vacmAccessContextPrefix
* matching the contextName in the message is
* not empty, then discard the rest
*/
( current->contextMatch == CONTEXT_MATCH_PREFIX &&
candidate->contextMatch == CONTEXT_MATCH_EXACT ) ||
/* c) discard all entries with ContextPrefixes shorter
* than the longest one remaining in the set
*/
( current->contextMatch == CONTEXT_MATCH_PREFIX &&
current->contextPrefix[0] < candidate->contextPrefix[0] ) ||
/* d) select the entry with the highest securityLevel
*/
( current->securityLevel < candidate->securityLevel )) {
return candidate;
}
return current;
}
struct vacm_accessEntry *
vacm_getAccessEntry(const char *groupName,
const char *contextPrefix,
int securityModel, int securityLevel)
{
struct vacm_accessEntry *vp, *best=NULL;
char group[VACMSTRINGLEN];
char context[VACMSTRINGLEN];
int glen, clen;
glen = (int) strlen(groupName);
if (glen < 0 || glen > VACM_MAX_STRING)
return NULL;
clen = (int) strlen(contextPrefix);
if (clen < 0 || clen > VACM_MAX_STRING)
return NULL;
group[0] = glen;
strlcpy(group + 1, groupName, sizeof(group) - 1);
context[0] = clen;
strlcpy(context + 1, contextPrefix, sizeof(context) - 1);
for (vp = accessList; vp; vp = vp->next) {
if ((securityModel == vp->securityModel
|| vp->securityModel == SNMP_SEC_MODEL_ANY)
&& securityLevel >= vp->securityLevel
&& !memcmp(vp->groupName, group, glen + 1)
&&
((vp->contextMatch == CONTEXT_MATCH_EXACT
&& clen == vp->contextPrefix[0]
&& (memcmp(vp->contextPrefix, context, clen + 1) == 0))
|| (vp->contextMatch == CONTEXT_MATCH_PREFIX
&& clen >= vp->contextPrefix[0]
&& (memcmp(vp->contextPrefix + 1, context + 1,
vp->contextPrefix[0]) == 0))))
best = _vacm_choose_best( best, vp );
}
return best;
}
void
vacm_scanAccessInit(void)
{
accessScanPtr = accessList;
}
struct vacm_accessEntry *
vacm_scanAccessNext(void)
{
struct vacm_accessEntry *returnval = accessScanPtr;
if (accessScanPtr)
accessScanPtr = accessScanPtr->next;
return returnval;
}
struct vacm_accessEntry *
vacm_createAccessEntry(const char *groupName,
const char *contextPrefix,
int securityModel, int securityLevel)
{
struct vacm_accessEntry *vp, *lp, *op = NULL;
int cmp, glen, clen;
glen = (int) strlen(groupName);
if (glen < 0 || glen > VACM_MAX_STRING)
return NULL;
clen = (int) strlen(contextPrefix);
if (clen < 0 || clen > VACM_MAX_STRING)
return NULL;
vp = (struct vacm_accessEntry *) calloc(1,
sizeof(struct
vacm_accessEntry));
if (vp == NULL)
return NULL;
vp->reserved =
(struct vacm_accessEntry *) calloc(1,
sizeof(struct
vacm_accessEntry));
if (vp->reserved == NULL) {
free(vp);
return NULL;
}
vp->securityModel = securityModel;
vp->securityLevel = securityLevel;
vp->groupName[0] = glen;
strlcpy(vp->groupName + 1, groupName, sizeof(vp->groupName) - 1);
vp->contextPrefix[0] = clen;
strlcpy(vp->contextPrefix + 1, contextPrefix,
sizeof(vp->contextPrefix) - 1);
lp = accessList;
while (lp) {
cmp = memcmp(lp->groupName, vp->groupName, glen + 1);
if (cmp > 0)
break;
if (cmp < 0)
goto next;
cmp = memcmp(lp->contextPrefix, vp->contextPrefix, clen + 1);
if (cmp > 0)
break;
if (cmp < 0)
goto next;
if (lp->securityModel > securityModel)
break;
if (lp->securityModel < securityModel)
goto next;
if (lp->securityLevel > securityLevel)
break;
next:
op = lp;
lp = lp->next;
}
vp->next = lp;
if (op == NULL)
accessList = vp;
else
op->next = vp;
return vp;
}
void
vacm_destroyAccessEntry(const char *groupName,
const char *contextPrefix,
int securityModel, int securityLevel)
{
struct vacm_accessEntry *vp, *lastvp = NULL;
if (accessList && accessList->securityModel == securityModel
&& accessList->securityLevel == securityLevel
&& !strcmp(accessList->groupName + 1, groupName)
&& !strcmp(accessList->contextPrefix + 1, contextPrefix)) {
vp = accessList;
accessList = accessList->next;
} else {
for (vp = accessList; vp; vp = vp->next) {
if (vp->securityModel == securityModel
&& vp->securityLevel == securityLevel
&& !strcmp(vp->groupName + 1, groupName)
&& !strcmp(vp->contextPrefix + 1, contextPrefix))
break;
lastvp = vp;
}
if (!vp || !lastvp)
return;
lastvp->next = vp->next;
}
if (vp->reserved)
free(vp->reserved);
free(vp);
return;
}
void
vacm_destroyAllAccessEntries(void)
{
struct vacm_accessEntry *ap;
while ((ap = accessList)) {
accessList = ap->next;
if (ap->reserved)
free(ap->reserved);
free(ap);
}
}
int
store_vacm(int majorID, int minorID, void *serverarg, void *clientarg)
{
/*
* figure out our application name
*/
char *appname = (char *) clientarg;
if (appname == NULL) {
appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_APPTYPE);
}
/*
* save the VACM MIB
*/
vacm_save("vacm", appname);
return SNMPERR_SUCCESS;
}
/*
* returns 1 if vacm has *any* (non-built-in) configuration entries,
* regardless of whether or not there is enough to make a decision,
* else return 0
*/
int
vacm_is_configured(void)
{
if (accessList == NULL && groupList == NULL) {
return 0;
}
return 1;
}
/*
* backwards compatability
*/
struct vacm_viewEntry *
vacm_getViewEntry(const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen, int mode)
{
return netsnmp_view_get( viewList, viewName, viewSubtree, viewSubtreeLen,
mode);
}
int
vacm_checkSubtree(const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
return netsnmp_view_subtree_check( viewList, viewName, viewSubtree,
viewSubtreeLen);
}
struct vacm_viewEntry *
vacm_createViewEntry(const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
return netsnmp_view_create( &viewList, viewName, viewSubtree,
viewSubtreeLen);
}
void
vacm_destroyViewEntry(const char *viewName,
oid * viewSubtree, size_t viewSubtreeLen)
{
netsnmp_view_destroy( &viewList, viewName, viewSubtree, viewSubtreeLen);
}
void
vacm_destroyAllViewEntries(void)
{
netsnmp_view_clear( &viewList );
}
/*
* vacm simple api
*/
int
netsnmp_vacm_simple_usm_add(const char *user, int rw, int authLevel,
const char *view, oid *oidView, size_t oidViewLen,
const char *context)
{
struct vacm_viewEntry *vacmEntry = NULL;
struct vacm_groupEntry *groupEntry = NULL;
struct vacm_accessEntry *accessEntry = NULL;
char *tmp, localContext[VACMSTRINGLEN];
int exact = 1; /* exact context match */
if (NULL == user)
return SNMPERR_GENERR;
if (authLevel < SNMP_SEC_LEVEL_NOAUTH ||
authLevel > SNMP_SEC_LEVEL_AUTHPRIV)
return SNMPERR_GENERR;
if (NULL != view) {
/*
* if we are given a view name, it is an error if
* - it exists and we have an oid
* - it doesn't exist and we don't have an oid
*/
if (netsnmp_view_exists(viewList, view) != 0) {
if (NULL != oidView || oidViewLen > 0) {
DEBUGMSGTL(("vacm:simple_usm", "can't modify existing view"));
return SNMPERR_GENERR;
}
} else {
if (NULL == oidView || oidViewLen == 0) {
DEBUGMSGTL(("vacm:simple_usm", "can't create view w/out oid"));
return SNMPERR_GENERR;
}
/** try and create view for oid */
vacmEntry = vacm_createViewEntry(view, oidView, oidViewLen);
if (NULL == vacmEntry) {
DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed"));
return SNMPERR_GENERR;
}
SNMP_FREE(vacmEntry->reserved);
}
} else if (0 == oidViewLen || NULL == oidView) {
view = "_all_"; /* no oid either, just use _all_ */
} else {
DEBUGMSGTL(("vacm:simple_usm", "need view name for new views"));
return SNMPERR_GENERR;
}
/*
* group
* grpv3user usm \"v3user\"\000prefix\000_all_\000_all_\000_all_\000\060"
* vacm_createGroupEntry() also automatically inserts into group list.
*/
groupEntry = vacm_createGroupEntry(SNMP_SEC_MODEL_USM, user);
if (NULL == groupEntry) {
DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed"));
goto bail;
}
snprintf(groupEntry->groupName, sizeof(groupEntry->groupName)-2,
"grp%.28s", user);
for (tmp=groupEntry->groupName; *tmp; tmp++)
if (!isalnum((unsigned char)(*tmp)))
*tmp = '_';
groupEntry->storageType = SNMP_STORAGE_PERMANENT;
groupEntry->status = SNMP_ROW_ACTIVE;
SNMP_FREE(groupEntry->reserved);
/*
* access
* grpv3user myctx usm noauth exact _all_ none none
*/
if (NULL == context) {
localContext[0] = 0;
context = localContext;
} else {
/** check for wildcard in context */
int contextLen = strlen(context);
if ('*' == context[contextLen - 1]) {
strlcpy(localContext, context, sizeof(localContext));
localContext[contextLen - 1] = 0;
context = localContext;
exact = 2; /* not exact, have context prefix */
}
}
accessEntry = vacm_createAccessEntry(groupEntry->groupName, context,
SNMP_SEC_MODEL_USM, authLevel);
if (NULL == accessEntry) {
DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed"));
goto bail;
}
strlcpy(accessEntry->views[VACM_VIEW_READ], view,
sizeof(accessEntry->views[VACM_VIEW_READ]));
if (0 == rw)
view = "none";
strlcpy(accessEntry->views[VACM_VIEW_WRITE], view,
sizeof(accessEntry->views[VACM_VIEW_WRITE]));
strlcpy(accessEntry->views[VACM_VIEW_NOTIFY], view,
sizeof(accessEntry->views[VACM_VIEW_NOTIFY]));
accessEntry->contextMatch = exact;
accessEntry->storageType = SNMP_STORAGE_PERMANENT;
accessEntry->status = SNMP_ROW_ACTIVE;
SNMP_FREE(accessEntry->reserved);
return SNMPERR_SUCCESS;
bail:
if (NULL != groupEntry)
vacm_destroyGroupEntry(SNMP_SEC_MODEL_USM, user);
if (NULL != vacmEntry)
vacm_destroyViewEntry(vacmEntry->viewName+1, vacmEntry->viewSubtree,
vacmEntry->viewSubtreeLen);
return SNMPERR_GENERR;
}
int
netsnmp_vacm_simple_usm_del(const char *user, int authLevel,
const char *view, oid *oidView, size_t oidViewLen,
const char *context)
{
char localContext[VACMSTRINGLEN];
char group[VACMSTRINGLEN];
/*
* only delete simple views (one OID) for which we have an OID.
* never delete '_all_'.
*/
if ((NULL != view) && (NULL != oidView) && (oidViewLen > 0) &&
(strcmp(view, "_all_") != 0) &&
(netsnmp_view_exists(viewList, view) == 1)) {
vacm_destroyViewEntry(view, oidView, oidViewLen);
}
vacm_destroyGroupEntry(SNMP_SEC_MODEL_USM, user);
snprintf(group, sizeof(group)-2, "grp%.28s", user);
if (NULL == context) {
localContext[0] = 0;
context = localContext;
} else {
/** check for wildcard in context */
int contextLen = strlen(context);
if ('*' == context[contextLen - 1]) {
strlcpy(localContext, context, sizeof(localContext));
localContext[contextLen - 1] = 0;
context = localContext;
}
}
vacm_destroyAccessEntry(group, context, SNMP_SEC_MODEL_USM, authLevel);
return SNMPERR_SUCCESS;
}