/*
 * 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.
 *
 * 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.
 */

#include <net-snmp/net-snmp-config.h>

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <ctype.h>
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#ifdef HAVE_NETDB_H
#include <netdb.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_const_config_handler("vacmView",
                                        vacm_parse_config_view, NULL, NULL);
    snmpd_register_const_config_handler("vacmGroup",
                                        vacm_parse_config_group,
                                        NULL, NULL);
    snmpd_register_const_config_handler("vacmAccess",
                                        vacm_parse_config_access,
                                        NULL, NULL);
    snmpd_register_const_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 || 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)) {
            strlcpy(ap->views[i], view, sizeof(ap->views[i]));
        }
    }
    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;
    }

    strlcpy(ap->views[viewnum], viewval, sizeof(ap->views[viewnum]));
    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;
    }
    strlcpy(ap->views[VACM_VIEW_READ], readView,
            sizeof(ap->views[VACM_VIEW_READ]));
    strlcpy(ap->views[VACM_VIEW_WRITE], writeView,
            sizeof(ap->views[VACM_VIEW_WRITE]));
    strlcpy(ap->views[VACM_VIEW_NOTIFY], notify,
            sizeof(ap->views[VACM_VIEW_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];
    size_t          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) {
        unsigned 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];
    /* Conveniently, the community-based security
       model values can also be used as bit flags */
    int             commversion = SNMP_SEC_MODEL_SNMPv1 |
                                  SNMP_SEC_MODEL_SNMPv2c;
#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;

    /*
     * 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)
#ifdef NETSNMP_TRANSPORT_UDP_DOMAIN
    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);
    }
#endif

#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
    if (parsetype == VACM_CREATE_SIMPLE_COMUNIX ||
        parsetype == VACM_CREATE_SIMPLE_COM) {
        if ( *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((unsigned char)(*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((unsigned char)(*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;
#ifndef NETSNMP_NO_WRITE_SUPPORT
    case SNMP_MSG_SET:
        viewtype = VACM_VIEW_WRITE;
        break;
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
    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;
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
    char            vacm_default_context[1] = "";
    const char     *contextName = vacm_default_context;
    const char     *pdu_community;
#endif
    const char     *sn = NULL;
    char           *vn;

    /*
     * 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 (0) {
#ifdef NETSNMP_TRANSPORT_UDP_DOMAIN
        } else 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);
#endif
#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 /* !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) */
      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;
    } 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() */


