/* 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 #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if HAVE_UNISTD_H #include #endif #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include #include #include /* strlcpy() */ #include #include 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; iviews[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; }