summaryrefslogtreecommitdiffstats
path: root/contrib/bsnmp/snmpd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bsnmp/snmpd/main.c')
-rw-r--r--contrib/bsnmp/snmpd/main.c923
1 files changed, 896 insertions, 27 deletions
diff --git a/contrib/bsnmp/snmpd/main.c b/contrib/bsnmp/snmpd/main.c
index e3660c1..e008bf7 100644
--- a/contrib/bsnmp/snmpd/main.c
+++ b/contrib/bsnmp/snmpd/main.c
@@ -30,6 +30,8 @@
*
* SNMPd main stuff.
*/
+
+#include <sys/queue.h>
#include <sys/param.h>
#include <sys/un.h>
#include <sys/ucred.h>
@@ -60,6 +62,7 @@
#define PATH_PID "/var/run/%s.pid"
#define PATH_CONFIG "/etc/%s.config"
+#define PATH_ENGINE "/var/%s.engine"
uint64_t this_tick; /* start of processing of current packet (absolute) */
uint64_t start_tick; /* start of processing */
@@ -87,6 +90,11 @@ struct snmpd snmpd = {
};
struct snmpd_stats snmpd_stats;
+struct snmpd_usmstat snmpd_usmstats;
+
+/* snmpEngine */
+struct snmp_engine snmpd_engine;
+
/* snmpSerialNo */
int32_t snmp_serial_no;
@@ -102,6 +110,29 @@ static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start);
/* list of all known communities */
struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list);
+/* list of all known USM users */
+struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist);
+
+/* A list of all VACM users configured, including v1, v2c and v3 */
+struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist);
+
+/* A list of all VACM groups */
+struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist);
+
+static struct vacm_group vacm_default_group = {
+ .groupname = "",
+};
+
+/* The list of configured access entries */
+struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist);
+
+/* The list of configured views */
+struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist);
+
+/* The list of configured contexts */
+struct vacm_contextlist vacm_contextlist =
+ SLIST_HEAD_INITIALIZER(vacm_contextlist);
+
/* list of all installed object resources */
struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list);
@@ -128,9 +159,13 @@ static int nprogargs;
u_int community;
static struct community *comm;
+/* current USM user */
+struct usm_user *usm_user;
+
/* file names */
static char config_file[MAXPATHLEN + 1];
static char pid_file[MAXPATHLEN + 1];
+char engine_file[MAXPATHLEN + 1];
#ifndef USE_LIBBEGEMOT
/* event context */
@@ -154,6 +189,12 @@ static const struct asn_oid
const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }};
+const struct asn_oid oid_usmUnknownEngineIDs =
+ { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}};
+
+const struct asn_oid oid_usmNotInTimeWindows =
+ { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}};
+
/* request id generator for traps */
u_int trap_reqid;
@@ -161,13 +202,15 @@ u_int trap_reqid;
static const char usgtxt[] = "\
Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
Open Communication Systems (FhG Fokus). All rights reserved.\n\
-usage: snmpd [-dh] [-c file] [-D options] [-I path] [-l prefix]\n\
- [-m variable=value] [-p file]\n\
+Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\
+usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\
+ [-l prefix] [-m variable=value] [-p file]\n\
options:\n\
-d don't daemonize\n\
-h print this info\n\
-c file specify configuration file\n\
-D options debugging options\n\
+ -e file specify engine id file\n\
-I path system include path\n\
-l prefix default basename for pid and config file\n\
-m var=val define variable\n\
@@ -243,7 +286,191 @@ snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen,
}
/*
- * SNMP input. Start: decode the PDU, find the community.
+ * Check USM PDU header credentials against local SNMP Engine & users.
+ */
+static enum snmp_code
+snmp_pdu_auth_user(struct snmp_pdu *pdu)
+{
+ uint64_t etime;
+ usm_user = NULL;
+
+ /* un-authenticated snmpEngineId discovery */
+ if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) {
+ pdu->engine.engine_len = snmpd_engine.engine_len;
+ memcpy(pdu->engine.engine_id, snmpd_engine.engine_id,
+ snmpd_engine.engine_len);
+ pdu->engine.engine_boots = snmpd_engine.engine_boots;
+ pdu->engine.engine_time = snmpd_engine.engine_time;
+ pdu->flags |= SNMP_MSG_AUTODISCOVER;
+ return (SNMP_CODE_OK);
+ }
+
+ if ((usm_user = usm_find_user(pdu->engine.engine_id,
+ pdu->engine.engine_len, pdu->user.sec_name)) == NULL ||
+ usm_user->status != 1 /* active */)
+ return (SNMP_CODE_BADUSER);
+
+ if (usm_user->user_engine_len != snmpd_engine.engine_len ||
+ memcmp(usm_user->user_engine_id, snmpd_engine.engine_id,
+ snmpd_engine.engine_len) != 0)
+ return (SNMP_CODE_BADENGINE);
+
+ pdu->user.priv_proto = usm_user->suser.priv_proto;
+ memcpy(pdu->user.priv_key, usm_user->suser.priv_key,
+ sizeof(pdu->user.priv_key));
+
+ /* authenticated snmpEngineId discovery */
+ if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
+ etime = (get_ticks() - start_tick) / 100ULL;
+ if (etime < INT32_MAX)
+ snmpd_engine.engine_time = etime;
+ else {
+ start_tick = get_ticks();
+ set_snmpd_engine();
+ snmpd_engine.engine_time = start_tick;
+ }
+
+ pdu->user.auth_proto = usm_user->suser.auth_proto;
+ memcpy(pdu->user.auth_key, usm_user->suser.auth_key,
+ sizeof(pdu->user.auth_key));
+
+ if (pdu->engine.engine_boots == 0 &&
+ pdu->engine.engine_time == 0) {
+ pdu->flags |= SNMP_MSG_AUTODISCOVER;
+ return (SNMP_CODE_OK);
+ }
+
+ if (pdu->engine.engine_boots != snmpd_engine.engine_boots ||
+ abs(pdu->engine.engine_time - snmpd_engine.engine_time) >
+ SNMP_TIME_WINDOW)
+ return (SNMP_CODE_NOTINTIME);
+ }
+
+ if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 &&
+ (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) ||
+ ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 &&
+ usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) ||
+ ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 &&
+ usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV))
+ return (SNMP_CODE_BADSECLEVEL);
+
+ return (SNMP_CODE_OK);
+}
+
+/*
+ * Check whether access to each of var bindings in the PDU is allowed based
+ * on the user credentials against the configured User groups & VACM views.
+ */
+static enum snmp_code
+snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip)
+{
+ const char *uname;
+ int32_t suboid, smodel;
+ uint32_t i;
+ struct vacm_user *vuser;
+ struct vacm_access *acl;
+ struct vacm_context *vacmctx;
+ struct vacm_view *view;
+
+ /*
+ * At least a default context exists if the snmpd_vacm(3) module is
+ * running.
+ */
+ if (SLIST_EMPTY(&vacm_contextlist) ||
+ (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0)
+ return (SNMP_CODE_OK);
+
+ switch (pdu->version) {
+ case SNMP_V1:
+ if ((uname = comm_string(community)) == NULL)
+ return (SNMP_CODE_FAILED);
+ smodel = SNMP_SECMODEL_SNMPv1;
+ break;
+
+ case SNMP_V2c:
+ if ((uname = comm_string(community)) == NULL)
+ return (SNMP_CODE_FAILED);
+ smodel = SNMP_SECMODEL_SNMPv2c;
+ break;
+
+ case SNMP_V3:
+ uname = pdu->user.sec_name;
+ if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM)
+ return (SNMP_CODE_FAILED);
+ /* Compare the PDU context engine id against the agent's */
+ if (pdu->context_engine_len != snmpd_engine.engine_len ||
+ memcmp(pdu->context_engine, snmpd_engine.engine_id,
+ snmpd_engine.engine_len) != 0)
+ return (SNMP_CODE_FAILED);
+ break;
+
+ default:
+ abort();
+ }
+
+ SLIST_FOREACH(vuser, &vacm_userlist, vvu)
+ if (strcmp(uname, vuser->secname) == 0 &&
+ vuser->sec_model == smodel)
+ break;
+
+ if (vuser == NULL || vuser->group == NULL)
+ return (SNMP_CODE_FAILED);
+
+ /* XXX: shteryana - recheck */
+ TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) {
+ if (acl->group != vuser->group)
+ continue;
+ SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl)
+ if (memcmp(vacmctx->ctxname, acl->ctx_prefix,
+ acl->ctx_match) == 0)
+ goto match;
+ }
+
+ return (SNMP_CODE_FAILED);
+
+match:
+
+ switch (pdu->type) {
+ case SNMP_PDU_GET:
+ case SNMP_PDU_GETNEXT:
+ case SNMP_PDU_GETBULK:
+ if ((view = acl->read_view) == NULL)
+ return (SNMP_CODE_FAILED);
+ break;
+
+ case SNMP_PDU_SET:
+ if ((view = acl->write_view) == NULL)
+ return (SNMP_CODE_FAILED);
+ break;
+
+ case SNMP_PDU_TRAP:
+ case SNMP_PDU_INFORM:
+ case SNMP_PDU_TRAP2:
+ case SNMP_PDU_REPORT:
+ if ((view = acl->notify_view) == NULL)
+ return (SNMP_CODE_FAILED);
+ break;
+ case SNMP_PDU_RESPONSE:
+ /* NOTREACHED */
+ return (SNMP_CODE_FAILED);
+ default:
+ abort();
+ }
+
+ for (i = 0; i < pdu->nbindings; i++) {
+ /* XXX - view->mask*/
+ suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var);
+ if ((!suboid && !view->exclude) || (suboid && view->exclude)) {
+ *ip = i + 1;
+ return (SNMP_CODE_FAILED);
+ }
+ }
+
+ return (SNMP_CODE_OK);
+}
+
+/*
+ * SNMP input. Start: decode the PDU, find the user or community.
*/
enum snmpd_input_err
snmp_input_start(const u_char *buf, size_t len, const char *source,
@@ -254,6 +481,9 @@ snmp_input_start(const u_char *buf, size_t len, const char *source,
enum snmpd_input_err ret;
int sret;
+ /* update uptime */
+ this_tick = get_ticks();
+
b.asn_cptr = buf;
b.asn_len = len;
@@ -269,11 +499,27 @@ snmp_input_start(const u_char *buf, size_t len, const char *source,
}
b.asn_len = *pdulen = (size_t)sret;
- code = snmp_pdu_decode(&b, pdu, ip);
+ memset(pdu, 0, sizeof(*pdu));
+ if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK)
+ goto decoded;
- snmpd_stats.inPkts++;
+ if (pdu->version == SNMP_V3) {
+ if (pdu->security_model != SNMP_SECMODEL_USM) {
+ code = SNMP_CODE_FAILED;
+ goto decoded;
+ }
+ if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK)
+ goto decoded;
+ if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK)
+ goto decoded;
+ }
+ code = snmp_pdu_decode_scoped(&b, pdu, ip);
ret = SNMPD_INPUT_OK;
+
+decoded:
+ snmpd_stats.inPkts++;
+
switch (code) {
case SNMP_CODE_FAILED:
@@ -300,6 +546,30 @@ snmp_input_start(const u_char *buf, size_t len, const char *source,
ret = SNMPD_INPUT_VALBADENC;
break;
+ case SNMP_CODE_BADSECLEVEL:
+ snmpd_usmstats.unsupported_seclevels++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_NOTINTIME:
+ snmpd_usmstats.not_in_time_windows++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_BADUSER:
+ snmpd_usmstats.unknown_users++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_BADENGINE:
+ snmpd_usmstats.unknown_engine_ids++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_BADDIGEST:
+ snmpd_usmstats.wrong_digests++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_EDECRYPT:
+ snmpd_usmstats.decrypt_errors++;
+ return (SNMPD_INPUT_FAILED);
+
case SNMP_CODE_OK:
switch (pdu->version) {
@@ -313,6 +583,11 @@ snmp_input_start(const u_char *buf, size_t len, const char *source,
goto bad_vers;
break;
+ case SNMP_V3:
+ if (!(snmpd.version_enable & VERS_ENABLE_V3))
+ goto bad_vers;
+ break;
+
case SNMP_Verr:
goto bad_vers;
}
@@ -325,25 +600,47 @@ snmp_input_start(const u_char *buf, size_t len, const char *source,
}
/*
- * Look, whether we know the community
+ * Look, whether we know the community or user
*/
- TAILQ_FOREACH(comm, &community_list, link)
- if (comm->string != NULL &&
- strcmp(comm->string, pdu->community) == 0)
- break;
- if (comm == NULL) {
- snmpd_stats.inBadCommunityNames++;
- snmp_pdu_free(pdu);
- if (snmpd.auth_traps)
- snmp_send_trap(&oid_authenticationFailure,
- (struct snmp_value *)NULL);
- ret = SNMPD_INPUT_BAD_COMM;
- } else
- community = comm->value;
+ if (pdu->version != SNMP_V3) {
+ TAILQ_FOREACH(comm, &community_list, link)
+ if (comm->string != NULL &&
+ strcmp(comm->string, pdu->community) == 0)
+ break;
- /* update uptime */
- this_tick = get_ticks();
+ if (comm == NULL) {
+ snmpd_stats.inBadCommunityNames++;
+ snmp_pdu_free(pdu);
+ if (snmpd.auth_traps)
+ snmp_send_trap(&oid_authenticationFailure,
+ (struct snmp_value *)NULL);
+ ret = SNMPD_INPUT_BAD_COMM;
+ } else
+ community = comm->value;
+ } else if (pdu->nbindings == 0) {
+ /* RFC 3414 - snmpEngineID Discovery */
+ if (strlen(pdu->user.sec_name) == 0) {
+ asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
+ &oid_usmUnknownEngineIDs);
+ pdu->context_engine_len = snmpd_engine.engine_len;
+ memcpy(pdu->context_engine, snmpd_engine.engine_id,
+ snmpd_engine.engine_len);
+ } else if (pdu->engine.engine_boots == 0 &&
+ pdu->engine.engine_time == 0) {
+ asn_append_oid(&(pdu->bindings[pdu->nbindings++].var),
+ &oid_usmNotInTimeWindows);
+ pdu->engine.engine_boots = snmpd_engine.engine_boots;
+ pdu->engine.engine_time = snmpd_engine.engine_time;
+ }
+ } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH &&
+ (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) {
+ snmpd_usmstats.not_in_time_windows++;
+ ret = SNMP_CODE_FAILED;
+ }
+
+ if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK)
+ ret = SNMP_CODE_FAILED;
return (ret);
}
@@ -960,7 +1257,8 @@ snmpd_input(struct port_input *pi, struct tport *tport)
* If that is a module community and the module has a proxy function,
* the hand it over to the module.
*/
- if (comm->owner != NULL && comm->owner->config->proxy != NULL) {
+ if (comm != NULL && comm->owner != NULL &&
+ comm->owner->config->proxy != NULL) {
perr = (*comm->owner->config->proxy)(&pdu, tport->transport,
&tport->index, pi->peer, pi->peerlen, ierr, vi,
!pi->cred || pi->priv);
@@ -1016,9 +1314,10 @@ snmpd_input(struct port_input *pi, struct tport *tport)
/*
* Check community
*/
- if ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) ||
+ if (pdu.version < SNMP_V3 &&
+ ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) ||
(community != COMM_WRITE &&
- (pdu.type == SNMP_PDU_SET || community != COMM_READ))) {
+ (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) {
snmpd_stats.inBadCommunityUses++;
snmp_pdu_free(&pdu);
snmp_input_consume(pi);
@@ -1337,7 +1636,7 @@ main(int argc, char *argv[])
struct tport *p;
const char *prefix = "snmpd";
struct lmodule *m;
- char *value, *option;
+ char *value = NULL, *option; /* XXX */
struct transport *t;
#define DBG_DUMP 0
@@ -1355,7 +1654,7 @@ main(int argc, char *argv[])
snmp_debug = snmp_debug_func;
asn_error = asn_error_func;
- while ((opt = getopt(argc, argv, "c:dD:hI:l:m:p:")) != EOF)
+ while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF)
switch (opt) {
case 'c':
@@ -1401,6 +1700,9 @@ main(int argc, char *argv[])
}
break;
+ case 'e':
+ strlcpy(engine_file, optarg, sizeof(engine_file));
+ break;
case 'h':
fprintf(stderr, "%s", usgtxt);
exit(0);
@@ -1471,6 +1773,7 @@ main(int argc, char *argv[])
snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix);
init_actvals();
+ init_snmpd_engine();
this_tick = get_ticks();
start_tick = this_tick;
@@ -1497,6 +1800,9 @@ main(int argc, char *argv[])
evSetDebug(evctx, 10, stderr);
#endif
+ if (engine_file[0] == '\0')
+ snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix);
+
if (read_config(config_file, NULL)) {
syslog(LOG_ERR, "error in config file");
exit(1);
@@ -1601,7 +1907,7 @@ main(int argc, char *argv[])
}
uint64_t
-get_ticks()
+get_ticks(void)
{
struct timeval tv;
uint64_t ret;
@@ -2374,3 +2680,566 @@ or_unregister(u_int idx)
return;
}
}
+
+/*
+ * RFC 3414 User-based Security Model support
+ */
+
+struct snmpd_usmstat *
+bsnmpd_get_usm_stats(void)
+{
+ return (&snmpd_usmstats);
+}
+
+void
+bsnmpd_reset_usm_stats(void)
+{
+ memset(&snmpd_usmstats, 0, sizeof(&snmpd_usmstats));
+}
+
+struct usm_user *
+usm_first_user(void)
+{
+ return (SLIST_FIRST(&usm_userlist));
+}
+
+struct usm_user *
+usm_next_user(struct usm_user *uuser)
+{
+ if (uuser == NULL)
+ return (NULL);
+
+ return (SLIST_NEXT(uuser, up));
+}
+
+struct usm_user *
+usm_find_user(uint8_t *engine, uint32_t elen, char *uname)
+{
+ struct usm_user *uuser;
+
+ SLIST_FOREACH(uuser, &usm_userlist, up)
+ if (uuser->user_engine_len == elen &&
+ memcmp(uuser->user_engine_id, engine, elen) == 0 &&
+ strlen(uuser->suser.sec_name) == strlen(uname) &&
+ strcmp(uuser->suser.sec_name, uname) == 0)
+ break;
+
+ return (uuser);
+}
+
+static int
+usm_compare_user(struct usm_user *u1, struct usm_user *u2)
+{
+ uint32_t i;
+
+ if (u1->user_engine_len < u2->user_engine_len)
+ return (-1);
+ if (u1->user_engine_len > u2->user_engine_len)
+ return (1);
+
+ for (i = 0; i < u1->user_engine_len; i++) {
+ if (u1->user_engine_id[i] < u2->user_engine_id[i])
+ return (-1);
+ if (u1->user_engine_id[i] > u2->user_engine_id[i])
+ return (1);
+ }
+
+ if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name))
+ return (-1);
+ if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name))
+ return (1);
+
+ for (i = 0; i < strlen(u1->suser.sec_name); i++) {
+ if (u1->suser.sec_name[i] < u2->suser.sec_name[i])
+ return (-1);
+ if (u1->suser.sec_name[i] > u2->suser.sec_name[i])
+ return (1);
+ }
+
+ return (0);
+}
+
+struct usm_user *
+usm_new_user(uint8_t *eid, uint32_t elen, char *uname)
+{
+ int cmp;
+ struct usm_user *uuser, *temp, *prev;
+
+ for (uuser = usm_first_user(); uuser != NULL;
+ (uuser = usm_next_user(uuser))) {
+ if (uuser->user_engine_len == elen &&
+ strlen(uname) == strlen(uuser->suser.sec_name) &&
+ strcmp(uname, uuser->suser.sec_name) == 0 &&
+ memcmp(eid, uuser->user_engine_id, elen) == 0)
+ return (NULL);
+ }
+
+ if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL)
+ return (NULL);
+
+ memset(uuser, 0, sizeof(struct usm_user));
+ strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ);
+ memcpy(uuser->user_engine_id, eid, elen);
+ uuser->user_engine_len = elen;
+
+ if ((prev = SLIST_FIRST(&usm_userlist)) == NULL ||
+ usm_compare_user(uuser, prev) < 0) {
+ SLIST_INSERT_HEAD(&usm_userlist, uuser, up);
+ return (uuser);
+ }
+
+ SLIST_FOREACH(temp, &usm_userlist, up) {
+ if ((cmp = usm_compare_user(uuser, temp)) <= 0)
+ break;
+ prev = temp;
+ }
+
+ if (temp == NULL || cmp < 0)
+ SLIST_INSERT_AFTER(prev, uuser, up);
+ else if (cmp > 0)
+ SLIST_INSERT_AFTER(temp, uuser, up);
+ else {
+ syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name);
+ free(uuser);
+ return (NULL);
+ }
+
+ return (uuser);
+}
+
+void
+usm_delete_user(struct usm_user *uuser)
+{
+ SLIST_REMOVE(&usm_userlist, uuser, usm_user, up);
+ free(uuser);
+}
+
+void
+usm_flush_users(void)
+{
+ struct usm_user *uuser;
+
+ while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) {
+ SLIST_REMOVE_HEAD(&usm_userlist, up);
+ free(uuser);
+ }
+
+ SLIST_INIT(&usm_userlist);
+}
+
+/*
+ * RFC 3415 View-based Access Control Model support
+ */
+struct vacm_user *
+vacm_first_user(void)
+{
+ return (SLIST_FIRST(&vacm_userlist));
+}
+
+struct vacm_user *
+vacm_next_user(struct vacm_user *vuser)
+{
+ if (vuser == NULL)
+ return (NULL);
+
+ return (SLIST_NEXT(vuser, vvu));
+}
+
+static int
+vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2)
+{
+ uint32_t i;
+
+ if (v1->sec_model < v2->sec_model)
+ return (-1);
+ if (v1->sec_model > v2->sec_model)
+ return (1);
+
+ if (strlen(v1->secname) < strlen(v2->secname))
+ return (-1);
+ if (strlen(v1->secname) > strlen(v2->secname))
+ return (1);
+
+ for (i = 0; i < strlen(v1->secname); i++) {
+ if (v1->secname[i] < v2->secname[i])
+ return (-1);
+ if (v1->secname[i] > v2->secname[i])
+ return (1);
+ }
+
+ return (0);
+}
+
+struct vacm_user *
+vacm_new_user(int32_t smodel, char *uname)
+{
+ int cmp;
+ struct vacm_user *user, *temp, *prev;
+
+ SLIST_FOREACH(user, &vacm_userlist, vvu)
+ if (strcmp(uname, user->secname) == 0 &&
+ smodel == user->sec_model)
+ return (NULL);
+
+ if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL)
+ return (NULL);
+
+ memset(user, 0, sizeof(*user));
+ user->group = &vacm_default_group;
+ SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg);
+ user->sec_model = smodel;
+ strlcpy(user->secname, uname, sizeof(user->secname));
+
+ if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL ||
+ vacm_compare_user(user, prev) < 0) {
+ SLIST_INSERT_HEAD(&vacm_userlist, user, vvu);
+ return (user);
+ }
+
+ SLIST_FOREACH(temp, &vacm_userlist, vvu) {
+ if ((cmp = vacm_compare_user(user, temp)) <= 0)
+ break;
+ prev = temp;
+ }
+
+ if (temp == NULL || cmp < 0)
+ SLIST_INSERT_AFTER(prev, user, vvu);
+ else if (cmp > 0)
+ SLIST_INSERT_AFTER(temp, user, vvu);
+ else {
+ syslog(LOG_ERR, "User %s exists", user->secname);
+ free(user);
+ return (NULL);
+ }
+
+ return (user);
+}
+
+int
+vacm_delete_user(struct vacm_user *user)
+{
+ if (user->group != NULL && user->group != &vacm_default_group) {
+ SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
+ if (SLIST_EMPTY(&user->group->group_users)) {
+ SLIST_REMOVE(&vacm_grouplist, user->group,
+ vacm_group, vge);
+ free(user->group);
+ }
+ }
+
+ SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu);
+ free(user);
+
+ return (0);
+}
+
+int
+vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len)
+{
+ struct vacm_group *group;
+
+ if (len >= SNMP_ADM_STR32_SIZ)
+ return (-1);
+
+ SLIST_FOREACH(group, &vacm_grouplist, vge)
+ if (strlen(group->groupname) == len &&
+ memcmp(octets, group->groupname, len) == 0)
+ break;
+
+ if (group == NULL) {
+ if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL)
+ return (-1);
+ memset(group, 0, sizeof(*group));
+ memcpy(group->groupname, octets, len);
+ group->groupname[len] = '\0';
+ SLIST_INSERT_HEAD(&vacm_grouplist, group, vge);
+ }
+
+ SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg);
+ SLIST_INSERT_HEAD(&group->group_users, user, vvg);
+ user->group = group;
+
+ return (0);
+}
+
+void
+vacm_groups_init(void)
+{
+ SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge);
+}
+
+struct vacm_access *
+vacm_first_access_rule(void)
+{
+ return (TAILQ_FIRST(&vacm_accesslist));
+}
+
+struct vacm_access *
+vacm_next_access_rule(struct vacm_access *acl)
+{
+ if (acl == NULL)
+ return (NULL);
+
+ return (TAILQ_NEXT(acl, vva));
+}
+
+static int
+vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2)
+{
+ uint32_t i;
+
+ if (strlen(v1->group->groupname) < strlen(v2->group->groupname))
+ return (-1);
+ if (strlen(v1->group->groupname) > strlen(v2->group->groupname))
+ return (1);
+
+ for (i = 0; i < strlen(v1->group->groupname); i++) {
+ if (v1->group->groupname[i] < v2->group->groupname[i])
+ return (-1);
+ if (v1->group->groupname[i] > v2->group->groupname[i])
+ return (1);
+ }
+
+ if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix))
+ return (-1);
+ if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix))
+ return (1);
+
+ for (i = 0; i < strlen(v1->ctx_prefix); i++) {
+ if (v1->ctx_prefix[i] < v2->ctx_prefix[i])
+ return (-1);
+ if (v1->ctx_prefix[i] > v2->ctx_prefix[i])
+ return (1);
+ }
+
+ if (v1->sec_model < v2->sec_model)
+ return (-1);
+ if (v1->sec_model > v2->sec_model)
+ return (1);
+
+ if (v1->sec_level < v2->sec_level)
+ return (-1);
+ if (v1->sec_level > v2->sec_level)
+ return (1);
+
+ return (0);
+}
+
+struct vacm_access *
+vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel)
+{
+ struct vacm_group *group;
+ struct vacm_access *acl, *temp;
+
+ TAILQ_FOREACH(acl, &vacm_accesslist, vva) {
+ if (acl->group == NULL)
+ continue;
+ if (strcmp(gname, acl->group->groupname) == 0 &&
+ strcmp(cprefix, acl->ctx_prefix) == 0 &&
+ acl->sec_model == smodel && acl->sec_level == slevel)
+ return (NULL);
+ }
+
+ /* Make sure the group exists */
+ SLIST_FOREACH(group, &vacm_grouplist, vge)
+ if (strcmp(gname, group->groupname) == 0)
+ break;
+
+ if (group == NULL)
+ return (NULL);
+
+ if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL)
+ return (NULL);
+
+ memset(acl, 0, sizeof(*acl));
+ acl->group = group;
+ strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix));
+ acl->sec_model = smodel;
+ acl->sec_level = slevel;
+
+ if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL ||
+ vacm_compare_access_rule(acl, temp) < 0) {
+ TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva);
+ return (acl);
+ }
+
+ TAILQ_FOREACH(temp, &vacm_accesslist, vva)
+ if (vacm_compare_access_rule(acl, temp) < 0) {
+ TAILQ_INSERT_BEFORE(temp, acl, vva);
+ return (acl);
+ }
+
+ TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva);
+
+ return (acl);
+}
+
+int
+vacm_delete_access_rule(struct vacm_access *acl)
+{
+ TAILQ_REMOVE(&vacm_accesslist, acl, vva);
+ free(acl);
+
+ return (0);
+}
+
+struct vacm_view *
+vacm_first_view(void)
+{
+ return (SLIST_FIRST(&vacm_viewlist));
+}
+
+struct vacm_view *
+vacm_next_view(struct vacm_view *view)
+{
+ if (view == NULL)
+ return (NULL);
+
+ return (SLIST_NEXT(view, vvl));
+}
+
+static int
+vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2)
+{
+ uint32_t i;
+
+ if (strlen(v1->viewname) < strlen(v2->viewname))
+ return (-1);
+ if (strlen(v1->viewname) > strlen(v2->viewname))
+ return (1);
+
+ for (i = 0; i < strlen(v1->viewname); i++) {
+ if (v1->viewname[i] < v2->viewname[i])
+ return (-1);
+ if (v1->viewname[i] > v2->viewname[i])
+ return (1);
+ }
+
+ return (asn_compare_oid(&v1->subtree, &v2->subtree));
+}
+
+struct vacm_view *
+vacm_new_view(char *vname, struct asn_oid *oid)
+{
+ int cmp;
+ struct vacm_view *view, *temp, *prev;
+
+ SLIST_FOREACH(view, &vacm_viewlist, vvl)
+ if (strcmp(vname, view->viewname) == 0)
+ return (NULL);
+
+ if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL)
+ return (NULL);
+
+ memset(view, 0, sizeof(*view));
+ strlcpy(view->viewname, vname, sizeof(view->viewname));
+ asn_append_oid(&view->subtree, oid);
+
+ if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL ||
+ vacm_compare_view(view, prev) < 0) {
+ SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl);
+ return (view);
+ }
+
+ SLIST_FOREACH(temp, &vacm_viewlist, vvl) {
+ if ((cmp = vacm_compare_view(view, temp)) <= 0)
+ break;
+ prev = temp;
+ }
+
+ if (temp == NULL || cmp < 0)
+ SLIST_INSERT_AFTER(prev, view, vvl);
+ else if (cmp > 0)
+ SLIST_INSERT_AFTER(temp, view, vvl);
+ else {
+ syslog(LOG_ERR, "View %s exists", view->viewname);
+ free(view);
+ return (NULL);
+ }
+
+ return (view);
+}
+
+int
+vacm_delete_view(struct vacm_view *view)
+{
+ SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl);
+ free(view);
+
+ return (0);
+}
+
+struct vacm_context *
+vacm_first_context(void)
+{
+ return (SLIST_FIRST(&vacm_contextlist));
+}
+
+struct vacm_context *
+vacm_next_context(struct vacm_context *vacmctx)
+{
+ if (vacmctx == NULL)
+ return (NULL);
+
+ return (SLIST_NEXT(vacmctx, vcl));
+}
+
+struct vacm_context *
+vacm_add_context(char *ctxname, int regid)
+{
+ int cmp;
+ struct vacm_context *ctx, *temp, *prev;
+
+ SLIST_FOREACH(ctx, &vacm_contextlist, vcl)
+ if (strcmp(ctxname, ctx->ctxname) == 0) {
+ syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
+ return (NULL);
+ }
+
+ if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL)
+ return (NULL);
+
+ memset(ctx, 0, sizeof(*ctx));
+ strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname));
+ ctx->regid = regid;
+
+ if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL ||
+ strlen(ctx->ctxname) < strlen(prev->ctxname) ||
+ strcmp(ctx->ctxname, prev->ctxname) < 0) {
+ SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl);
+ return (ctx);
+ }
+
+ SLIST_FOREACH(temp, &vacm_contextlist, vcl) {
+ if (strlen(ctx->ctxname) < strlen(temp->ctxname) ||
+ strcmp(ctx->ctxname, temp->ctxname) < 0) {
+ cmp = -1;
+ break;
+ }
+ prev = temp;
+ }
+
+ if (temp == NULL || cmp < 0)
+ SLIST_INSERT_AFTER(prev, ctx, vcl);
+ else if (cmp > 0)
+ SLIST_INSERT_AFTER(temp, ctx, vcl);
+ else {
+ syslog(LOG_ERR, "Context %s exists", ctx->ctxname);
+ free(ctx);
+ return (NULL);
+ }
+
+ return (ctx);
+}
+
+void
+vacm_flush_contexts(int regid)
+{
+ struct vacm_context *ctx, *temp;
+
+ SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp)
+ if (ctx->regid == regid) {
+ SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl);
+ free(ctx);
+ }
+}
OpenPOWER on IntegriCloud