HardenedBSD/contrib/bsnmp/snmp_vacm/vacm_snmp.c
2010-12-08 13:51:38 +00:00

1027 lines
27 KiB
C
Executable File

/*-
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Shteryana Sotirova Shopova under
* sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/queue.h>
#include <sys/types.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include "asn1.h"
#include "snmp.h"
#include "snmpmod.h"
#include "vacm_tree.h"
#include "vacm_oid.h"
static struct lmodule *vacm_module;
/* For the registration. */
static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB;
static uint reg_vacm;
static int32_t vacm_lock;
/*
* Internal datastructures and forward declarations.
*/
static void vacm_append_userindex(struct asn_oid *,
uint, const struct vacm_user *);
static int vacm_user_index_decode(const struct asn_oid *,
uint, int32_t *, char *);
static struct vacm_user *vacm_get_user(const struct asn_oid *,
uint);
static struct vacm_user *vacm_get_next_user(const struct asn_oid *,
uint);
static void vacm_append_access_rule_index(struct asn_oid *,
uint, const struct vacm_access *);
static int vacm_access_rule_index_decode(const struct asn_oid *,
uint, char *, char *, int32_t *, int32_t *);
static struct vacm_access * vacm_get_access_rule(const struct asn_oid *,
uint);
static struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *,
uint);
static int vacm_view_index_decode(const struct asn_oid *, uint,
char *, struct asn_oid *);
static void vacm_append_viewindex(struct asn_oid *, uint,
const struct vacm_view *);
static struct vacm_view *vacm_get_view(const struct asn_oid *, uint);
static struct vacm_view *vacm_get_next_view(const struct asn_oid *, uint);
static struct vacm_view *vacm_get_view_by_name(u_char *, u_int);
static struct vacm_context *vacm_get_context(const struct asn_oid *, uint);
static struct vacm_context *vacm_get_next_context(const struct asn_oid *,
uint);
static void vacm_append_ctxindex(struct asn_oid *, uint,
const struct vacm_context *);
int
op_vacm_context(struct snmp_context *ctx __unused, struct snmp_value *val,
uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
char cname[SNMP_ADM_STR32_SIZ];
size_t cnamelen;
struct vacm_context *vacm_ctx;
if (val->var.subs[sub - 1] != LEAF_vacmContextName)
abort();
switch (op) {
case SNMP_OP_GET:
if ((vacm_ctx = vacm_get_context(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
break;
case SNMP_OP_GETNEXT:
if ((vacm_ctx = vacm_get_next_context(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
vacm_append_ctxindex(&val->var, sub, vacm_ctx);
break;
case SNMP_OP_SET:
if ((vacm_ctx = vacm_get_context(&val->var, sub)) != NULL)
return (SNMP_ERR_WRONG_VALUE);
if (community != COMM_INITIALIZE)
return (SNMP_ERR_NOT_WRITEABLE);
if (val->var.subs[sub] >= SNMP_ADM_STR32_SIZ)
return (SNMP_ERR_WRONG_VALUE);
if (index_decode(&val->var, sub, iidx, &cname, &cnamelen))
return (SNMP_ERR_GENERR);
cname[cnamelen] = '\0';
if ((vacm_ctx = vacm_add_context(cname, reg_vacm)) == NULL)
return (SNMP_ERR_GENERR);
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
/* FALLTHROUGH*/
case SNMP_OP_ROLLBACK:
return (SNMP_ERR_NOERROR);
default:
abort();
}
return (string_get(val, vacm_ctx->ctxname, -1));
}
int
op_vacm_security_to_group(struct snmp_context *ctx, struct snmp_value *val,
uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
int32_t smodel;
char uname[SNMP_ADM_STR32_SIZ];
struct vacm_user *user;
switch (op) {
case SNMP_OP_GET:
if ((user = vacm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
break;
case SNMP_OP_GETNEXT:
if ((user = vacm_get_next_user(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
vacm_append_userindex(&val->var, sub, user);
break;
case SNMP_OP_SET:
if ((user = vacm_get_user(&val->var, sub)) == NULL &&
val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus)
return (SNMP_ERR_NOSUCHNAME);
if (user != NULL) {
if (community != COMM_INITIALIZE &&
user->type == StorageType_readOnly)
return (SNMP_ERR_NOT_WRITEABLE);
if (user->status == RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
}
switch (val->var.subs[sub - 1]) {
case LEAF_vacmGroupName:
ctx->scratch->ptr1 = user->group->groupname;
ctx->scratch->int1 = strlen(user->group->groupname);
return (vacm_user_set_group(user,
val->v.octetstring.octets,val->v.octetstring.len));
case LEAF_vacmSecurityToGroupStorageType:
return (SNMP_ERR_INCONS_VALUE);
case LEAF_vacmSecurityToGroupStatus:
if (user == NULL) {
if (val->v.integer != RowStatus_createAndGo ||
vacm_user_index_decode(&val->var, sub,
&smodel, uname) < 0)
return (SNMP_ERR_INCONS_VALUE);
user = vacm_new_user(smodel, uname);
if (user == NULL)
return (SNMP_ERR_GENERR);
user->status = RowStatus_destroy;
if (community != COMM_INITIALIZE)
user->type = StorageType_volatile;
else
user->type = StorageType_readOnly;
} else if (val->v.integer != RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
ctx->scratch->int1 = user->status;
user->status = val->v.integer;
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
if (val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus)
return (SNMP_ERR_NOERROR);
if ((user = vacm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->v.integer) {
case RowStatus_destroy:
return (vacm_delete_user(user));
case RowStatus_createAndGo:
user->status = RowStatus_active;
break;
default:
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_ROLLBACK:
if ((user = vacm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->var.subs[sub - 1]) {
case LEAF_vacmGroupName:
return (vacm_user_set_group(user, ctx->scratch->ptr1,
ctx->scratch->int1));
case LEAF_vacmSecurityToGroupStatus:
if (ctx->scratch->int1 == RowStatus_destroy)
return (vacm_delete_user(user));
user->status = ctx->scratch->int1;
break;
default:
break;
}
return (SNMP_ERR_NOERROR);
default:
abort();
}
switch (val->var.subs[sub - 1]) {
case LEAF_vacmGroupName:
return (string_get(val, user->group->groupname, -1));
case LEAF_vacmSecurityToGroupStorageType:
val->v.integer = user->type;
break;
case LEAF_vacmSecurityToGroupStatus:
val->v.integer = user->status;
break;
default:
abort();
}
return (SNMP_ERR_NOERROR);
}
int
op_vacm_access(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
uint32_t iidx __unused, enum snmp_op op)
{
int32_t smodel, slevel;
char gname[SNMP_ADM_STR32_SIZ], cprefix[SNMP_ADM_STR32_SIZ];
struct vacm_access *acl;
switch (op) {
case SNMP_OP_GET:
if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
break;
case SNMP_OP_GETNEXT:
if ((acl = vacm_get_next_access_rule(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
vacm_append_access_rule_index(&val->var, sub, acl);
break;
case SNMP_OP_SET:
if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL &&
val->var.subs[sub - 1] != LEAF_vacmAccessStatus)
return (SNMP_ERR_NOSUCHNAME);
if (acl != NULL && community != COMM_INITIALIZE &&
acl->type == StorageType_readOnly)
return (SNMP_ERR_NOT_WRITEABLE);
switch (val->var.subs[sub - 1]) {
case LEAF_vacmAccessContextMatch:
ctx->scratch->int1 = acl->ctx_match;
if (val->v.integer == vacmAccessContextMatch_exact)
acl->ctx_match = 1;
else if (val->v.integer == vacmAccessContextMatch_prefix)
acl->ctx_match = 0;
else
return (SNMP_ERR_WRONG_VALUE);
break;
case LEAF_vacmAccessReadViewName:
ctx->scratch->ptr1 = acl->read_view;
acl->read_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len);
if (acl->read_view == NULL) {
acl->read_view = ctx->scratch->ptr1;
return (SNMP_ERR_INCONS_VALUE);
}
return (SNMP_ERR_NOERROR);
case LEAF_vacmAccessWriteViewName:
ctx->scratch->ptr1 = acl->write_view;
if ((acl->write_view =
vacm_get_view_by_name(val->v.octetstring.octets,
val->v.octetstring.len)) == NULL) {
acl->write_view = ctx->scratch->ptr1;
return (SNMP_ERR_INCONS_VALUE);
}
break;
case LEAF_vacmAccessNotifyViewName:
ctx->scratch->ptr1 = acl->notify_view;
if ((acl->notify_view =
vacm_get_view_by_name(val->v.octetstring.octets,
val->v.octetstring.len)) == NULL) {
acl->notify_view = ctx->scratch->ptr1;
return (SNMP_ERR_INCONS_VALUE);
}
break;
case LEAF_vacmAccessStorageType:
return (SNMP_ERR_INCONS_VALUE);
case LEAF_vacmAccessStatus:
if (acl == NULL) {
if (val->v.integer != RowStatus_createAndGo ||
vacm_access_rule_index_decode(&val->var,
sub, gname, cprefix, &smodel, &slevel) < 0)
return (SNMP_ERR_INCONS_VALUE);
if ((acl = vacm_new_access_rule(gname, cprefix,
smodel, slevel)) == NULL)
return (SNMP_ERR_GENERR);
acl->status = RowStatus_destroy;
if (community != COMM_INITIALIZE)
acl->type = StorageType_volatile;
else
acl->type = StorageType_readOnly;
} else if (val->v.integer != RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
ctx->scratch->int1 = acl->status;
acl->status = val->v.integer;
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
if (val->var.subs[sub - 1] != LEAF_vacmAccessStatus)
return (SNMP_ERR_NOERROR);
if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
if (val->v.integer == RowStatus_destroy)
return (vacm_delete_access_rule(acl));
else
acl->status = RowStatus_active;
return (SNMP_ERR_NOERROR);
case SNMP_OP_ROLLBACK:
if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->var.subs[sub - 1]) {
case LEAF_vacmAccessContextMatch:
acl->ctx_match = ctx->scratch->int1;
break;
case LEAF_vacmAccessReadViewName:
acl->read_view = ctx->scratch->ptr1;
break;
case LEAF_vacmAccessWriteViewName:
acl->write_view = ctx->scratch->ptr1;
break;
case LEAF_vacmAccessNotifyViewName:
acl->notify_view = ctx->scratch->ptr1;
break;
case LEAF_vacmAccessStatus:
if (ctx->scratch->int1 == RowStatus_destroy)
return (vacm_delete_access_rule(acl));
default:
break;
}
return (SNMP_ERR_NOERROR);
default:
abort();
}
switch (val->var.subs[sub - 1]) {
case LEAF_vacmAccessContextMatch:
return (string_get(val, acl->ctx_prefix, -1));
case LEAF_vacmAccessReadViewName:
if (acl->read_view != NULL)
return (string_get(val, acl->read_view->viewname, -1));
else
return (string_get(val, NULL, 0));
case LEAF_vacmAccessWriteViewName:
if (acl->write_view != NULL)
return (string_get(val, acl->write_view->viewname, -1));
else
return (string_get(val, NULL, 0));
case LEAF_vacmAccessNotifyViewName:
if (acl->notify_view != NULL)
return (string_get(val, acl->notify_view->viewname, -1));
else
return (string_get(val, NULL, 0));
case LEAF_vacmAccessStorageType:
val->v.integer = acl->type;
break;
case LEAF_vacmAccessStatus:
val->v.integer = acl->status;
break;
default:
abort();
}
return (SNMP_ERR_NOERROR);
}
int
op_vacm_view_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
if (val->var.subs[sub - 1] != LEAF_vacmViewSpinLock)
return (SNMP_ERR_NOSUCHNAME);
switch (op) {
case SNMP_OP_GET:
if (++vacm_lock == INT32_MAX)
vacm_lock = 0;
val->v.integer = vacm_lock;
break;
case SNMP_OP_GETNEXT:
abort();
case SNMP_OP_SET:
if (val->v.integer != vacm_lock)
return (SNMP_ERR_INCONS_VALUE);
break;
case SNMP_OP_ROLLBACK:
/* FALLTHROUGH */
case SNMP_OP_COMMIT:
break;
}
return (SNMP_ERR_NOERROR);
}
int
op_vacm_view(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
uint32_t iidx __unused, enum snmp_op op)
{
char vname[SNMP_ADM_STR32_SIZ];
struct asn_oid oid;
struct vacm_view *view;
switch (op) {
case SNMP_OP_GET:
if ((view = vacm_get_view(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
break;
case SNMP_OP_GETNEXT:
if ((view = vacm_get_next_view(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
vacm_append_viewindex(&val->var, sub, view);
break;
case SNMP_OP_SET:
if ((view = vacm_get_view(&val->var, sub)) == NULL &&
val->var.subs[sub - 1] != LEAF_vacmViewTreeFamilyStatus)
return (SNMP_ERR_NOSUCHNAME);
if (view != NULL) {
if (community != COMM_INITIALIZE &&
view->type == StorageType_readOnly)
return (SNMP_ERR_NOT_WRITEABLE);
if (view->status == RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
}
switch (val->var.subs[sub - 1]) {
case LEAF_vacmViewTreeFamilyMask:
if (val->v.octetstring.len > sizeof(view->mask))
ctx->scratch->ptr1 = malloc(sizeof(view->mask));
if (ctx->scratch->ptr1 == NULL)
return (SNMP_ERR_GENERR);
memset(ctx->scratch->ptr1, 0, sizeof(view->mask));
memcpy(ctx->scratch->ptr1, view->mask,
sizeof(view->mask));
memset(view->mask, 0, sizeof(view->mask));
memcpy(view->mask, val->v.octetstring.octets,
val->v.octetstring.len);
break;
case LEAF_vacmViewTreeFamilyType:
ctx->scratch->int1 = view->exclude;
if (val->v.integer == vacmViewTreeFamilyType_included)
view->exclude = 0;
else if (val->v.integer == vacmViewTreeFamilyType_excluded)
view->exclude = 1;
else
return (SNMP_ERR_WRONG_VALUE);
break;
case LEAF_vacmViewTreeFamilyStorageType:
return (SNMP_ERR_INCONS_VALUE);
case LEAF_vacmViewTreeFamilyStatus:
if (view == NULL) {
if (val->v.integer != RowStatus_createAndGo ||
vacm_view_index_decode(&val->var, sub, vname,
&oid) < 0)
return (SNMP_ERR_INCONS_VALUE);
if ((view = vacm_new_view(vname, &oid)) == NULL)
return (SNMP_ERR_GENERR);
view->status = RowStatus_destroy;
if (community != COMM_INITIALIZE)
view->type = StorageType_volatile;
else
view->type = StorageType_readOnly;
} else if (val->v.integer != RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
ctx->scratch->int1 = view->status;
view->status = val->v.integer;
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
switch (val->var.subs[sub - 1]) {
case LEAF_vacmViewTreeFamilyMask:
free(ctx->scratch->ptr1);
break;
case LEAF_vacmViewTreeFamilyStatus:
if ((view = vacm_get_view(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->v.integer) {
case RowStatus_destroy:
return (vacm_delete_view(view));
case RowStatus_createAndGo:
view->status = RowStatus_active;
break;
default:
/* NOTREACHED*/
return (SNMP_ERR_GENERR);
}
default:
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_ROLLBACK:
if ((view = vacm_get_view(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->var.subs[sub - 1]) {
case LEAF_vacmViewTreeFamilyMask:
memcpy(view->mask, ctx->scratch->ptr1,
sizeof(view->mask));
free(ctx->scratch->ptr1);
break;
case LEAF_vacmViewTreeFamilyType:
view->exclude = ctx->scratch->int1;
break;
case LEAF_vacmViewTreeFamilyStatus:
if (ctx->scratch->int1 == RowStatus_destroy)
return (vacm_delete_view(view));
break;
default:
break;
}
return (SNMP_ERR_NOERROR);
default:
abort();
}
switch (val->var.subs[sub - 1]) {
case LEAF_vacmViewTreeFamilyMask:
return (string_get(val, view->mask, sizeof(view->mask)));
case LEAF_vacmViewTreeFamilyType:
if (view->exclude)
val->v.integer = vacmViewTreeFamilyType_excluded;
else
val->v.integer = vacmViewTreeFamilyType_included;
break;
case LEAF_vacmViewTreeFamilyStorageType:
val->v.integer = view->type;
break;
case LEAF_vacmViewTreeFamilyStatus:
val->v.integer = view->status;
break;
default:
abort();
}
return (SNMP_ERR_NOERROR);
}
static void
vacm_append_userindex(struct asn_oid *oid, uint sub,
const struct vacm_user *user)
{
uint32_t i;
oid->len = sub + strlen(user->secname) + 2;
oid->subs[sub++] = user->sec_model;
oid->subs[sub] = strlen(user->secname);
for (i = 1; i <= strlen(user->secname); i++)
oid->subs[sub + i] = user->secname[i - 1];
}
static int
vacm_user_index_decode(const struct asn_oid *oid, uint sub,
int32_t *smodel, char *uname)
{
uint32_t i;
*smodel = oid->subs[sub++];
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (-1);
for (i = 0; i < oid->subs[sub]; i++)
uname[i] = oid->subs[sub + i + 1];
uname[i] = '\0';
return (0);
}
static struct vacm_user *
vacm_get_user(const struct asn_oid *oid, uint sub)
{
int32_t smodel;
char uname[SNMP_ADM_STR32_SIZ];
struct vacm_user *user;
if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0)
return (NULL);
for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user))
if (strcmp(uname, user->secname) == 0 &&
user->sec_model == smodel)
return (user);
return (NULL);
}
static struct vacm_user *
vacm_get_next_user(const struct asn_oid *oid, uint sub)
{
int32_t smodel;
char uname[SNMP_ADM_STR32_SIZ];
struct vacm_user *user;
if (oid->len - sub == 0)
return (vacm_first_user());
if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0)
return (NULL);
for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user))
if (strcmp(uname, user->secname) == 0 &&
user->sec_model == smodel)
return (vacm_next_user(user));
return (NULL);
}
static void
vacm_append_access_rule_index(struct asn_oid *oid, uint sub,
const struct vacm_access *acl)
{
uint32_t i;
oid->len = sub + strlen(acl->group->groupname) +
strlen(acl->ctx_prefix) + 4;
oid->subs[sub] = strlen(acl->group->groupname);
for (i = 1; i <= strlen(acl->group->groupname); i++)
oid->subs[sub + i] = acl->group->groupname[i - 1];
sub += strlen(acl->group->groupname) + 1;
oid->subs[sub] = strlen(acl->ctx_prefix);
for (i = 1; i <= strlen(acl->ctx_prefix); i++)
oid->subs[sub + i] = acl->ctx_prefix[i - 1];
sub += strlen(acl->ctx_prefix) + 1;
oid->subs[sub++] = acl->sec_model;
oid->subs[sub] = acl->sec_level;
}
static int
vacm_access_rule_index_decode(const struct asn_oid *oid, uint sub, char *gname,
char *cprefix, int32_t *smodel, int32_t *slevel)
{
uint32_t i;
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (-1);
for (i = 0; i < oid->subs[sub]; i++)
gname[i] = oid->subs[sub + i + 1];
gname[i] = '\0';
sub += strlen(gname) + 1;
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (-1);
for (i = 0; i < oid->subs[sub]; i++)
cprefix[i] = oid->subs[sub + i + 1];
cprefix[i] = '\0';
sub += strlen(cprefix) + 1;
*smodel = oid->subs[sub++];
*slevel = oid->subs[sub];
return (0);
}
struct vacm_access *
vacm_get_access_rule(const struct asn_oid *oid, uint sub)
{
int32_t smodel, slevel;
char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ];
struct vacm_access *acl;
if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel,
&slevel) < 0)
return (NULL);
for (acl = vacm_first_access_rule(); acl != NULL;
acl = vacm_next_access_rule(acl))
if (strcmp(gname, acl->group->groupname) == 0 &&
strcmp(prefix, acl->ctx_prefix) == 0 &&
smodel == acl->sec_model && slevel == acl->sec_level)
return (acl);
return (NULL);
}
struct vacm_access *
vacm_get_next_access_rule(const struct asn_oid *oid __unused, uint sub __unused)
{
int32_t smodel, slevel;
char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ];
struct vacm_access *acl;
if (oid->len - sub == 0)
return (vacm_first_access_rule());
if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel,
&slevel) < 0)
return (NULL);
for (acl = vacm_first_access_rule(); acl != NULL;
acl = vacm_next_access_rule(acl))
if (strcmp(gname, acl->group->groupname) == 0 &&
strcmp(prefix, acl->ctx_prefix) == 0 &&
smodel == acl->sec_model && slevel == acl->sec_model)
return (vacm_next_access_rule(acl));
return (NULL);
}
static int
vacm_view_index_decode(const struct asn_oid *oid, uint sub, char *vname,
struct asn_oid *view_oid)
{
uint32_t i;
int viod_off;
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (-1);
for (i = 0; i < oid->subs[sub]; i++)
vname[i] = oid->subs[sub + i + 1];
vname[i] = '\0';
viod_off = sub + oid->subs[sub] + 1;
if ((view_oid->len = oid->subs[viod_off]) > ASN_MAXOIDLEN)
return (-1);
memcpy(&view_oid->subs[0], &oid->subs[viod_off + 1],
view_oid->len * sizeof(view_oid->subs[0]));
return (0);
}
static void
vacm_append_viewindex(struct asn_oid *oid, uint sub, const struct vacm_view *view)
{
uint32_t i;
oid->len = sub + strlen(view->viewname) + 1;
oid->subs[sub] = strlen(view->viewname);
for (i = 1; i <= strlen(view->viewname); i++)
oid->subs[sub + i] = view->viewname[i - 1];
sub += strlen(view->viewname) + 1;
oid->subs[sub] = view->subtree.len;
oid->len++;
asn_append_oid(oid, &view->subtree);
}
struct vacm_view *
vacm_get_view(const struct asn_oid *oid, uint sub)
{
char vname[SNMP_ADM_STR32_SIZ];
struct asn_oid subtree;
struct vacm_view *view;
if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0)
return (NULL);
for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
if (strcmp(vname, view->viewname) == 0 &&
asn_compare_oid(&subtree, &view->subtree)== 0)
return (view);
return (NULL);
}
struct vacm_view *
vacm_get_next_view(const struct asn_oid *oid, uint sub)
{
char vname[SNMP_ADM_STR32_SIZ];
struct asn_oid subtree;
struct vacm_view *view;
if (oid->len - sub == 0)
return (vacm_first_view());
if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0)
return (NULL);
for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
if (strcmp(vname, view->viewname) == 0 &&
asn_compare_oid(&subtree, &view->subtree)== 0)
return (vacm_next_view(view));
return (NULL);
}
static struct vacm_view *
vacm_get_view_by_name(u_char *octets, u_int len)
{
struct vacm_view *view;
for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
if (strlen(view->viewname) == len &&
memcmp(octets, view->viewname, len) == 0)
return (view);
return (NULL);
}
static struct vacm_context *
vacm_get_context(const struct asn_oid *oid, uint sub)
{
char cname[SNMP_ADM_STR32_SIZ];
size_t cnamelen;
u_int index_count;
struct vacm_context *vacm_ctx;
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (NULL);
index_count = 0;
index_count = SNMP_INDEX(index_count, 1);
if (index_decode(oid, sub, index_count, &cname, &cnamelen))
return (NULL);
for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL;
vacm_ctx = vacm_next_context(vacm_ctx))
if (strcmp(cname, vacm_ctx->ctxname) == 0)
return (vacm_ctx);
return (NULL);
}
static struct vacm_context *
vacm_get_next_context(const struct asn_oid *oid, uint sub)
{
char cname[SNMP_ADM_STR32_SIZ];
size_t cnamelen;
u_int index_count;
struct vacm_context *vacm_ctx;
if (oid->len - sub == 0)
return (vacm_first_context());
if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
return (NULL);
index_count = 0;
index_count = SNMP_INDEX(index_count, 1);
if (index_decode(oid, sub, index_count, &cname, &cnamelen))
return (NULL);
for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL;
vacm_ctx = vacm_next_context(vacm_ctx))
if (strcmp(cname, vacm_ctx->ctxname) == 0)
return (vacm_next_context(vacm_ctx));
return (NULL);
}
static void
vacm_append_ctxindex(struct asn_oid *oid, uint sub,
const struct vacm_context *ctx)
{
uint32_t i;
oid->len = sub + strlen(ctx->ctxname) + 1;
oid->subs[sub] = strlen(ctx->ctxname);
for (i = 1; i <= strlen(ctx->ctxname); i++)
oid->subs[sub + i] = ctx->ctxname[i - 1];
}
/*
* VACM snmp module initialization hook.
* Returns 0 on success, < 0 on error.
*/
static int
vacm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
{
vacm_module = mod;
vacm_lock = random();
vacm_groups_init();
/* XXX: TODO - initialize structures */
return (0);
}
/*
* VACM snmp module finalization hook.
*/
static int
vacm_fini(void)
{
/* XXX: TODO - cleanup */
vacm_flush_contexts(reg_vacm);
or_unregister(reg_vacm);
return (0);
}
/*
* VACM snmp module start operation.
*/
static void
vacm_start(void)
{
static char dflt_ctx[] = "";
reg_vacm = or_register(&oid_vacm,
"The MIB module for managing SNMP View-based Access Control Model.",
vacm_module);
(void)vacm_add_context(dflt_ctx, reg_vacm);
}
static void
vacm_dump(void)
{
struct vacm_context *vacmctx;
struct vacm_user *vuser;
struct vacm_access *vacl;
struct vacm_view *view;
static char oidbuf[ASN_OIDSTRLEN];
syslog(LOG_ERR, "\n");
syslog(LOG_ERR, "Context list:");
for (vacmctx = vacm_first_context(); vacmctx != NULL;
vacmctx = vacm_next_context(vacmctx))
syslog(LOG_ERR, "Context \"%s\", module id %d",
vacmctx->ctxname, vacmctx->regid);
syslog(LOG_ERR, "VACM users:");
for (vuser = vacm_first_user(); vuser != NULL;
vuser = vacm_next_user(vuser))
syslog(LOG_ERR, "Uname %s, Group %s, model %d", vuser->secname,
vuser->group!= NULL?vuser->group->groupname:"Unknown",
vuser->sec_model);
syslog(LOG_ERR, "VACM Access rules:");
for (vacl = vacm_first_access_rule(); vacl != NULL;
vacl = vacm_next_access_rule(vacl))
syslog(LOG_ERR, "Group %s, CtxPrefix %s, Model %d, Level %d, "
"RV %s, WR %s, NV %s", vacl->group!=NULL?
vacl->group->groupname:"Unknown", vacl->ctx_prefix,
vacl->sec_model, vacl->sec_level, vacl->read_view!=NULL?
vacl->read_view->viewname:"None", vacl->write_view!=NULL?
vacl->write_view->viewname:"None", vacl->notify_view!=NULL?
vacl->notify_view->viewname:"None");
syslog(LOG_ERR, "VACM Views:");
for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
syslog(LOG_ERR, "View %s, Tree %s - %s", view->viewname,
asn_oid2str_r(&view->subtree, oidbuf), view->exclude?
"excluded":"included");
}
const char vacm_comment[] = \
"This module implements SNMP View-based Access Control Model defined in RFC 3415.";
const struct snmp_module config = {
.comment = vacm_comment,
.init = vacm_init,
.fini = vacm_fini,
.start = vacm_start,
.tree = vacm_ctree,
.dump = vacm_dump,
.tree_size = vacm_CTREE_SIZE,
};