1/*-
2 * Copyright (c) 2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Shteryana Sotirova Shopova under
6 * sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: stable/10/contrib/bsnmp/snmp_vacm/vacm_snmp.c 310903 2016-12-31 10:34:09Z ngie $
30 */
31#include <sys/queue.h>
32#include <sys/types.h>
33
34#include <errno.h>
35#include <stdarg.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <stdint.h>
39#include <string.h>
40#include <syslog.h>
41
42#include "asn1.h"
43#include "snmp.h"
44#include "snmpmod.h"
45
46#include "vacm_tree.h"
47#include "vacm_oid.h"
48
49static struct lmodule *vacm_module;
50/* For the registration. */
51static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB;
52
53static uint reg_vacm;
54
55static int32_t vacm_lock;
56
57/*
58 * Internal datastructures and forward declarations.
59 */
60static void		vacm_append_userindex(struct asn_oid *,
61    uint, const struct vacm_user *);
62static int		vacm_user_index_decode(const struct asn_oid *,
63    uint, int32_t *, char *);
64static struct vacm_user *vacm_get_user(const struct asn_oid *,
65    uint);
66static struct vacm_user *vacm_get_next_user(const struct asn_oid *,
67    uint);
68static void		vacm_append_access_rule_index(struct asn_oid *,
69    uint, const struct vacm_access *);
70static int		vacm_access_rule_index_decode(const struct asn_oid *,
71    uint, char *, char *, int32_t *, int32_t *);
72static struct vacm_access *	vacm_get_access_rule(const struct asn_oid *,
73    uint);
74static struct vacm_access *	vacm_get_next_access_rule(const struct asn_oid *,
75    uint);
76static int		vacm_view_index_decode(const struct asn_oid *, uint,
77    char *, struct asn_oid *);
78static void		vacm_append_viewindex(struct asn_oid *, uint,
79    const struct vacm_view *);
80static struct vacm_view	*vacm_get_view(const struct asn_oid *, uint);
81static struct vacm_view	*vacm_get_next_view(const struct asn_oid *, uint);
82static struct vacm_view *vacm_get_view_by_name(u_char *, u_int);
83static struct vacm_context	*vacm_get_context(const struct asn_oid *, uint);
84static struct vacm_context	*vacm_get_next_context(const struct asn_oid *,
85    uint);
86static void			vacm_append_ctxindex(struct asn_oid *, uint,
87    const struct vacm_context *);
88
89int
90op_vacm_context(struct snmp_context *ctx __unused, struct snmp_value *val,
91    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
92{
93	char cname[SNMP_ADM_STR32_SIZ];
94	size_t cnamelen;
95	struct vacm_context *vacm_ctx;
96
97	if (val->var.subs[sub - 1] != LEAF_vacmContextName)
98		abort();
99
100	switch (op) {
101	case SNMP_OP_GET:
102		if ((vacm_ctx = vacm_get_context(&val->var, sub)) == NULL)
103			return (SNMP_ERR_NOSUCHNAME);
104		break;
105
106	case SNMP_OP_GETNEXT:
107		if ((vacm_ctx = vacm_get_next_context(&val->var, sub)) == NULL)
108			return (SNMP_ERR_NOSUCHNAME);
109		vacm_append_ctxindex(&val->var, sub, vacm_ctx);
110		break;
111
112	case SNMP_OP_SET:
113		if ((vacm_ctx = vacm_get_context(&val->var, sub)) != NULL)
114			return (SNMP_ERR_WRONG_VALUE);
115		if (community != COMM_INITIALIZE)
116			return (SNMP_ERR_NOT_WRITEABLE);
117		if (val->var.subs[sub] >= SNMP_ADM_STR32_SIZ)
118			return (SNMP_ERR_WRONG_VALUE);
119		if (index_decode(&val->var, sub, iidx, &cname, &cnamelen))
120			return (SNMP_ERR_GENERR);
121		cname[cnamelen] = '\0';
122		if ((vacm_ctx = vacm_add_context(cname, reg_vacm)) == NULL)
123			return (SNMP_ERR_GENERR);
124		return (SNMP_ERR_NOERROR);
125
126	case SNMP_OP_COMMIT:
127		/* FALLTHROUGH*/
128	case SNMP_OP_ROLLBACK:
129		return (SNMP_ERR_NOERROR);
130	default:
131		abort();
132	}
133
134	return (string_get(val, vacm_ctx->ctxname, -1));
135}
136
137int
138op_vacm_security_to_group(struct snmp_context *ctx, struct snmp_value *val,
139    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
140{
141	int32_t smodel;
142	char uname[SNMP_ADM_STR32_SIZ];
143	struct vacm_user *user;
144
145	switch (op) {
146	case SNMP_OP_GET:
147		if ((user = vacm_get_user(&val->var, sub)) == NULL)
148			return (SNMP_ERR_NOSUCHNAME);
149		break;
150
151	case SNMP_OP_GETNEXT:
152		if ((user = vacm_get_next_user(&val->var, sub)) == NULL)
153			return (SNMP_ERR_NOSUCHNAME);
154		vacm_append_userindex(&val->var, sub, user);
155		break;
156
157	case SNMP_OP_SET:
158		if ((user = vacm_get_user(&val->var, sub)) == NULL &&
159		    val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus)
160			return (SNMP_ERR_NOSUCHNAME);
161
162		if (user != NULL) {
163			if (community != COMM_INITIALIZE &&
164			    user->type == StorageType_readOnly)
165				return (SNMP_ERR_NOT_WRITEABLE);
166			if (user->status == RowStatus_active &&
167			    val->v.integer != RowStatus_destroy)
168				return (SNMP_ERR_INCONS_VALUE);
169		}
170
171		switch (val->var.subs[sub - 1]) {
172		case LEAF_vacmGroupName:
173			ctx->scratch->ptr1 = user->group->groupname;
174			ctx->scratch->int1 = strlen(user->group->groupname);
175			return (vacm_user_set_group(user,
176			    val->v.octetstring.octets,val->v.octetstring.len));
177
178		case LEAF_vacmSecurityToGroupStorageType:
179			return (SNMP_ERR_INCONS_VALUE);
180
181		case LEAF_vacmSecurityToGroupStatus:
182			if (user == NULL) {
183				if (val->v.integer != RowStatus_createAndGo ||
184				    vacm_user_index_decode(&val->var, sub,
185				    &smodel, uname) < 0)
186					return (SNMP_ERR_INCONS_VALUE);
187				user = vacm_new_user(smodel, uname);
188				if (user == NULL)
189					return (SNMP_ERR_GENERR);
190				user->status = RowStatus_destroy;
191				if (community != COMM_INITIALIZE)
192					user->type = StorageType_volatile;
193				else
194					user->type = StorageType_readOnly;
195			} else if (val->v.integer != RowStatus_active &&
196			    val->v.integer != RowStatus_destroy)
197				return (SNMP_ERR_INCONS_VALUE);
198			ctx->scratch->int1 = user->status;
199			user->status = val->v.integer;
200			break;
201		}
202		return (SNMP_ERR_NOERROR);
203
204	case SNMP_OP_COMMIT:
205		if (val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus)
206			return (SNMP_ERR_NOERROR);
207		if ((user = vacm_get_user(&val->var, sub)) == NULL)
208			return (SNMP_ERR_GENERR);
209		switch (val->v.integer) {
210		case  RowStatus_destroy:
211			return (vacm_delete_user(user));
212
213		case RowStatus_createAndGo:
214			user->status = RowStatus_active;
215			break;
216
217		default:
218			break;
219		}
220		return (SNMP_ERR_NOERROR);
221
222	case SNMP_OP_ROLLBACK:
223		if ((user = vacm_get_user(&val->var, sub)) == NULL)
224			return (SNMP_ERR_GENERR);
225		switch (val->var.subs[sub - 1]) {
226		case LEAF_vacmGroupName:
227			return (vacm_user_set_group(user, ctx->scratch->ptr1,
228			    ctx->scratch->int1));
229
230		case LEAF_vacmSecurityToGroupStatus:
231			if (ctx->scratch->int1 == RowStatus_destroy)
232				return (vacm_delete_user(user));
233			user->status = ctx->scratch->int1;
234			break;
235
236		default:
237			break;
238		}
239		return (SNMP_ERR_NOERROR);
240
241	default:
242		abort();
243	}
244
245	switch (val->var.subs[sub - 1]) {
246	case LEAF_vacmGroupName:
247		return (string_get(val, user->group->groupname, -1));
248	case LEAF_vacmSecurityToGroupStorageType:
249		val->v.integer = user->type;
250		break;
251	case LEAF_vacmSecurityToGroupStatus:
252		val->v.integer = user->status;
253		break;
254	default:
255		abort();
256	}
257
258	return (SNMP_ERR_NOERROR);
259}
260
261int
262op_vacm_access(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
263    uint32_t iidx __unused, enum snmp_op op)
264{
265	int32_t smodel, slevel;
266	char gname[SNMP_ADM_STR32_SIZ], cprefix[SNMP_ADM_STR32_SIZ];
267	struct vacm_access *acl;
268
269	switch (op) {
270	case SNMP_OP_GET:
271		if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
272			return (SNMP_ERR_NOSUCHNAME);
273		break;
274
275	case SNMP_OP_GETNEXT:
276		if ((acl = vacm_get_next_access_rule(&val->var, sub)) == NULL)
277			return (SNMP_ERR_NOSUCHNAME);
278		vacm_append_access_rule_index(&val->var, sub, acl);
279		break;
280
281	case SNMP_OP_SET:
282		if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL &&
283		    val->var.subs[sub - 1] != LEAF_vacmAccessStatus)
284				return (SNMP_ERR_NOSUCHNAME);
285		if (acl != NULL && community != COMM_INITIALIZE &&
286		    acl->type == StorageType_readOnly)
287			return (SNMP_ERR_NOT_WRITEABLE);
288
289		switch (val->var.subs[sub - 1]) {
290		case LEAF_vacmAccessContextMatch:
291			ctx->scratch->int1 = acl->ctx_match;
292			if (val->v.integer == vacmAccessContextMatch_exact)
293				acl->ctx_match = 1;
294			else if (val->v.integer == vacmAccessContextMatch_prefix)
295				acl->ctx_match = 0;
296			else
297				return (SNMP_ERR_WRONG_VALUE);
298			break;
299
300		case LEAF_vacmAccessReadViewName:
301			ctx->scratch->ptr1 = acl->read_view;
302			acl->read_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len);
303			if (acl->read_view == NULL) {
304				acl->read_view = ctx->scratch->ptr1;
305				return (SNMP_ERR_INCONS_VALUE);
306			}
307			return (SNMP_ERR_NOERROR);
308
309		case LEAF_vacmAccessWriteViewName:
310			ctx->scratch->ptr1 = acl->write_view;
311			if ((acl->write_view =
312			    vacm_get_view_by_name(val->v.octetstring.octets,
313			    val->v.octetstring.len)) == NULL) {
314				acl->write_view = ctx->scratch->ptr1;
315				return (SNMP_ERR_INCONS_VALUE);
316			}
317			break;
318
319		case LEAF_vacmAccessNotifyViewName:
320			ctx->scratch->ptr1 = acl->notify_view;
321			if ((acl->notify_view =
322			    vacm_get_view_by_name(val->v.octetstring.octets,
323			    val->v.octetstring.len)) == NULL) {
324				acl->notify_view = ctx->scratch->ptr1;
325				return (SNMP_ERR_INCONS_VALUE);
326			}
327			break;
328
329		case LEAF_vacmAccessStorageType:
330			return (SNMP_ERR_INCONS_VALUE);
331
332		case LEAF_vacmAccessStatus:
333			if (acl == NULL) {
334				if (val->v.integer != RowStatus_createAndGo ||
335				    vacm_access_rule_index_decode(&val->var,
336				    sub, gname, cprefix, &smodel, &slevel) < 0)
337					return (SNMP_ERR_INCONS_VALUE);
338				if ((acl = vacm_new_access_rule(gname, cprefix,
339				    smodel, slevel)) == NULL)
340					return (SNMP_ERR_GENERR);
341				acl->status = RowStatus_destroy;
342				if (community != COMM_INITIALIZE)
343					acl->type = StorageType_volatile;
344				else
345					acl->type = StorageType_readOnly;
346			} else if (val->v.integer != RowStatus_active &&
347			    val->v.integer != RowStatus_destroy)
348				return (SNMP_ERR_INCONS_VALUE);
349			ctx->scratch->int1 = acl->status;
350			acl->status = val->v.integer;
351			break;
352		}
353		return (SNMP_ERR_NOERROR);
354
355	case SNMP_OP_COMMIT:
356		if (val->var.subs[sub - 1] != LEAF_vacmAccessStatus)
357			return (SNMP_ERR_NOERROR);
358		if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
359			return (SNMP_ERR_GENERR);
360		if (val->v.integer == RowStatus_destroy)
361			return (vacm_delete_access_rule(acl));
362		else
363			acl->status = RowStatus_active;
364		return (SNMP_ERR_NOERROR);
365
366	case SNMP_OP_ROLLBACK:
367		if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL)
368			return (SNMP_ERR_GENERR);
369		switch (val->var.subs[sub - 1]) {
370		case LEAF_vacmAccessContextMatch:
371			acl->ctx_match = ctx->scratch->int1;
372			break;
373		case LEAF_vacmAccessReadViewName:
374			acl->read_view = ctx->scratch->ptr1;
375			break;
376		case LEAF_vacmAccessWriteViewName:
377			acl->write_view = ctx->scratch->ptr1;
378			break;
379		case LEAF_vacmAccessNotifyViewName:
380			acl->notify_view = ctx->scratch->ptr1;
381			break;
382		case LEAF_vacmAccessStatus:
383			if (ctx->scratch->int1 == RowStatus_destroy)
384				return (vacm_delete_access_rule(acl));
385		default:
386			break;
387		}
388		return (SNMP_ERR_NOERROR);
389
390	default:
391		abort();
392	}
393
394	switch (val->var.subs[sub - 1]) {
395	case LEAF_vacmAccessContextMatch:
396		return (string_get(val, acl->ctx_prefix, -1));
397	case LEAF_vacmAccessReadViewName:
398		if (acl->read_view != NULL)
399			return (string_get(val, acl->read_view->viewname, -1));
400		else
401			return (string_get(val, NULL, 0));
402	case LEAF_vacmAccessWriteViewName:
403		if (acl->write_view != NULL)
404			return (string_get(val, acl->write_view->viewname, -1));
405		else
406			return (string_get(val, NULL, 0));
407	case LEAF_vacmAccessNotifyViewName:
408		if (acl->notify_view != NULL)
409			return (string_get(val, acl->notify_view->viewname, -1));
410		else
411			return (string_get(val, NULL, 0));
412	case LEAF_vacmAccessStorageType:
413		val->v.integer = acl->type;
414		break;
415	case LEAF_vacmAccessStatus:
416		val->v.integer = acl->status;
417		break;
418	default:
419		abort();
420	}
421
422	return (SNMP_ERR_NOERROR);
423}
424
425int
426op_vacm_view_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
427    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
428{
429	if (val->var.subs[sub - 1] != LEAF_vacmViewSpinLock)
430		return (SNMP_ERR_NOSUCHNAME);
431
432	switch (op) {
433	case SNMP_OP_GET:
434		if (++vacm_lock == INT32_MAX)
435			vacm_lock = 0;
436		val->v.integer = vacm_lock;
437		break;
438
439	case SNMP_OP_GETNEXT:
440		abort();
441
442	case SNMP_OP_SET:
443		if (val->v.integer != vacm_lock)
444			return (SNMP_ERR_INCONS_VALUE);
445		break;
446
447	case SNMP_OP_ROLLBACK:
448		/* FALLTHROUGH */
449	case SNMP_OP_COMMIT:
450		break;
451	}
452
453	return (SNMP_ERR_NOERROR);
454}
455
456int
457op_vacm_view(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
458    uint32_t iidx __unused, enum snmp_op op)
459{
460	char vname[SNMP_ADM_STR32_SIZ];
461	struct asn_oid oid;
462	struct vacm_view *view;
463
464	switch (op) {
465	case SNMP_OP_GET:
466		if ((view = vacm_get_view(&val->var, sub)) == NULL)
467			return (SNMP_ERR_NOSUCHNAME);
468		break;
469
470	case SNMP_OP_GETNEXT:
471		if ((view = vacm_get_next_view(&val->var, sub)) == NULL)
472			return (SNMP_ERR_NOSUCHNAME);
473		vacm_append_viewindex(&val->var, sub, view);
474		break;
475
476	case SNMP_OP_SET:
477		if ((view = vacm_get_view(&val->var, sub)) == NULL &&
478		    val->var.subs[sub - 1] != LEAF_vacmViewTreeFamilyStatus)
479				return (SNMP_ERR_NOSUCHNAME);
480
481		if (view != NULL) {
482			if (community != COMM_INITIALIZE &&
483			    view->type == StorageType_readOnly)
484				return (SNMP_ERR_NOT_WRITEABLE);
485			if (view->status == RowStatus_active &&
486			    val->v.integer != RowStatus_destroy)
487				return (SNMP_ERR_INCONS_VALUE);
488		}
489
490		switch (val->var.subs[sub - 1]) {
491		case LEAF_vacmViewTreeFamilyMask:
492			if (val->v.octetstring.len > sizeof(view->mask))
493			ctx->scratch->ptr1 = malloc(sizeof(view->mask));
494			if (ctx->scratch->ptr1 == NULL)
495				return (SNMP_ERR_GENERR);
496			memset(ctx->scratch->ptr1, 0, sizeof(view->mask));
497			memcpy(ctx->scratch->ptr1, view->mask,
498			    sizeof(view->mask));
499			memset(view->mask, 0, sizeof(view->mask));
500			memcpy(view->mask, val->v.octetstring.octets,
501			    val->v.octetstring.len);
502			break;
503
504		case LEAF_vacmViewTreeFamilyType:
505			ctx->scratch->int1 = view->exclude;
506			if (val->v.integer == vacmViewTreeFamilyType_included)
507				view->exclude = 0;
508			else if (val->v.integer == vacmViewTreeFamilyType_excluded)
509				view->exclude = 1;
510			else
511				return (SNMP_ERR_WRONG_VALUE);
512			break;
513
514		case LEAF_vacmViewTreeFamilyStorageType:
515			return (SNMP_ERR_INCONS_VALUE);
516
517		case LEAF_vacmViewTreeFamilyStatus:
518			if (view == NULL) {
519				if (val->v.integer != RowStatus_createAndGo ||
520				    vacm_view_index_decode(&val->var, sub, vname,
521				    &oid) < 0)
522					return (SNMP_ERR_INCONS_VALUE);
523				if ((view = vacm_new_view(vname, &oid)) == NULL)
524					return (SNMP_ERR_GENERR);
525				view->status = RowStatus_destroy;
526				if (community != COMM_INITIALIZE)
527					view->type = StorageType_volatile;
528				else
529					view->type = StorageType_readOnly;
530			} else if (val->v.integer != RowStatus_active &&
531			    val->v.integer != RowStatus_destroy)
532				return (SNMP_ERR_INCONS_VALUE);
533			ctx->scratch->int1 = view->status;
534			view->status = val->v.integer;
535			break;
536		}
537		return (SNMP_ERR_NOERROR);
538
539	case SNMP_OP_COMMIT:
540		switch (val->var.subs[sub - 1]) {
541		case LEAF_vacmViewTreeFamilyMask:
542			free(ctx->scratch->ptr1);
543			break;
544		case LEAF_vacmViewTreeFamilyStatus:
545			if ((view = vacm_get_view(&val->var, sub)) == NULL)
546				return (SNMP_ERR_GENERR);
547			switch (val->v.integer) {
548			case  RowStatus_destroy:
549				return (vacm_delete_view(view));
550
551			case RowStatus_createAndGo:
552				view->status = RowStatus_active;
553				break;
554
555			default:
556				/* NOTREACHED*/
557				return (SNMP_ERR_GENERR);
558			}
559		default:
560			break;
561		}
562		return (SNMP_ERR_NOERROR);
563
564	case SNMP_OP_ROLLBACK:
565		if ((view = vacm_get_view(&val->var, sub)) == NULL)
566			return (SNMP_ERR_GENERR);
567		switch (val->var.subs[sub - 1]) {
568		case LEAF_vacmViewTreeFamilyMask:
569			memcpy(view->mask, ctx->scratch->ptr1,
570			    sizeof(view->mask));
571			free(ctx->scratch->ptr1);
572			break;
573		case LEAF_vacmViewTreeFamilyType:
574			view->exclude = ctx->scratch->int1;
575			break;
576		case LEAF_vacmViewTreeFamilyStatus:
577			if (ctx->scratch->int1 == RowStatus_destroy)
578				return (vacm_delete_view(view));
579			break;
580		default:
581			break;
582		}
583		return (SNMP_ERR_NOERROR);
584
585	default:
586		abort();
587	}
588
589	switch (val->var.subs[sub - 1]) {
590	case LEAF_vacmViewTreeFamilyMask:
591		return (string_get(val, view->mask, sizeof(view->mask)));
592	case LEAF_vacmViewTreeFamilyType:
593		if (view->exclude)
594			val->v.integer = vacmViewTreeFamilyType_excluded;
595		else
596			val->v.integer = vacmViewTreeFamilyType_included;
597		break;
598	case LEAF_vacmViewTreeFamilyStorageType:
599		val->v.integer = view->type;
600		break;
601	case LEAF_vacmViewTreeFamilyStatus:
602		val->v.integer = view->status;
603		break;
604	default:
605		abort();
606	}
607
608	return (SNMP_ERR_NOERROR);
609}
610
611static void
612vacm_append_userindex(struct asn_oid *oid, uint sub,
613    const struct vacm_user *user)
614{
615	uint32_t i;
616
617	oid->len = sub + strlen(user->secname) + 2;
618	oid->subs[sub++] = user->sec_model;
619	oid->subs[sub] = strlen(user->secname);
620	for (i = 1; i <= strlen(user->secname); i++)
621		oid->subs[sub + i] = user->secname[i - 1];
622}
623
624static int
625vacm_user_index_decode(const struct asn_oid *oid, uint sub,
626    int32_t *smodel, char *uname)
627{
628	uint32_t i;
629
630	*smodel = oid->subs[sub++];
631
632	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
633		return (-1);
634
635	for (i = 0; i < oid->subs[sub]; i++)
636		uname[i] = oid->subs[sub + i + 1];
637	uname[i] = '\0';
638
639	return (0);
640}
641
642static struct vacm_user *
643vacm_get_user(const struct asn_oid *oid, uint sub)
644{
645	int32_t smodel;
646	char uname[SNMP_ADM_STR32_SIZ];
647	struct vacm_user *user;
648
649	if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0)
650		return (NULL);
651
652	for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user))
653		if (strcmp(uname, user->secname) == 0 &&
654		    user->sec_model == smodel)
655			return (user);
656
657	return (NULL);
658}
659
660static struct vacm_user *
661vacm_get_next_user(const struct asn_oid *oid, uint sub)
662{
663	int32_t smodel;
664	char uname[SNMP_ADM_STR32_SIZ];
665	struct vacm_user *user;
666
667	if (oid->len - sub == 0)
668		return (vacm_first_user());
669
670	if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0)
671		return (NULL);
672
673	for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user))
674		if (strcmp(uname, user->secname) == 0 &&
675		    user->sec_model == smodel)
676			return (vacm_next_user(user));
677
678	return (NULL);
679}
680
681static void
682vacm_append_access_rule_index(struct asn_oid *oid, uint sub,
683    const struct vacm_access *acl)
684{
685	uint32_t i;
686
687	oid->len = sub + strlen(acl->group->groupname) +
688	    strlen(acl->ctx_prefix) + 4;
689
690	oid->subs[sub] = strlen(acl->group->groupname);
691	for (i = 1; i <= strlen(acl->group->groupname); i++)
692		oid->subs[sub + i] = acl->group->groupname[i - 1];
693	sub += strlen(acl->group->groupname) + 1;
694
695	oid->subs[sub] = strlen(acl->ctx_prefix);
696	for (i = 1; i <= strlen(acl->ctx_prefix); i++)
697		oid->subs[sub + i] = acl->ctx_prefix[i - 1];
698	sub += strlen(acl->ctx_prefix) + 1;
699	oid->subs[sub++] = acl->sec_model;
700	oid->subs[sub] = acl->sec_level;
701}
702
703static int
704vacm_access_rule_index_decode(const struct asn_oid *oid, uint sub, char *gname,
705    char *cprefix, int32_t *smodel, int32_t *slevel)
706{
707	uint32_t i;
708
709	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
710		return (-1);
711
712	for (i = 0; i < oid->subs[sub]; i++)
713		gname[i] = oid->subs[sub + i + 1];
714	gname[i] = '\0';
715	sub += strlen(gname) + 1;
716
717	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
718		return (-1);
719
720	for (i = 0; i < oid->subs[sub]; i++)
721		cprefix[i] = oid->subs[sub + i + 1];
722	cprefix[i] = '\0';
723	sub += strlen(cprefix) + 1;
724
725	*smodel = oid->subs[sub++];
726	*slevel = oid->subs[sub];
727
728	return (0);
729}
730
731struct vacm_access *
732vacm_get_access_rule(const struct asn_oid *oid, uint sub)
733{
734	int32_t smodel, slevel;
735	char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ];
736	struct vacm_access *acl;
737
738	if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel,
739	    &slevel) < 0)
740		return (NULL);
741
742	for (acl = vacm_first_access_rule(); acl != NULL;
743	    acl = vacm_next_access_rule(acl))
744		if (strcmp(gname, acl->group->groupname) == 0 &&
745		    strcmp(prefix, acl->ctx_prefix) == 0 &&
746		    smodel == acl->sec_model && slevel == acl->sec_level)
747			return (acl);
748
749	return (NULL);
750}
751
752struct vacm_access *
753vacm_get_next_access_rule(const struct asn_oid *oid __unused, uint sub __unused)
754{
755	int32_t smodel, slevel;
756	char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ];
757	struct vacm_access *acl;
758
759	if (oid->len - sub == 0)
760		return (vacm_first_access_rule());
761
762	if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel,
763	    &slevel) < 0)
764		return (NULL);
765
766	for (acl = vacm_first_access_rule(); acl != NULL;
767	    acl = vacm_next_access_rule(acl))
768		if (strcmp(gname, acl->group->groupname) == 0 &&
769		    strcmp(prefix, acl->ctx_prefix) == 0 &&
770		    smodel == acl->sec_model && slevel == acl->sec_model)
771			return (vacm_next_access_rule(acl));
772
773	return (NULL);
774}
775
776static int
777vacm_view_index_decode(const struct asn_oid *oid, uint sub, char *vname,
778   struct asn_oid *view_oid)
779{
780	uint32_t i;
781	int viod_off;
782
783	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
784		return (-1);
785
786	for (i = 0; i < oid->subs[sub]; i++)
787		vname[i] = oid->subs[sub + i + 1];
788	vname[i] = '\0';
789
790	viod_off = sub + oid->subs[sub] + 1;
791	if ((view_oid->len = oid->subs[viod_off]) > ASN_MAXOIDLEN)
792		return (-1);
793
794	memcpy(&view_oid->subs[0], &oid->subs[viod_off + 1],
795	    view_oid->len * sizeof(view_oid->subs[0]));
796
797	return (0);
798}
799
800static void
801vacm_append_viewindex(struct asn_oid *oid, uint sub, const struct vacm_view *view)
802{
803	uint32_t i;
804
805	oid->len = sub + strlen(view->viewname) + 1;
806	oid->subs[sub] = strlen(view->viewname);
807	for (i = 1; i <= strlen(view->viewname); i++)
808		oid->subs[sub + i] = view->viewname[i - 1];
809
810	sub += strlen(view->viewname) + 1;
811	oid->subs[sub] = view->subtree.len;
812	oid->len++;
813	asn_append_oid(oid, &view->subtree);
814}
815
816struct vacm_view *
817vacm_get_view(const struct asn_oid *oid, uint sub)
818{
819	char vname[SNMP_ADM_STR32_SIZ];
820	struct asn_oid subtree;
821	struct vacm_view *view;
822
823	if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0)
824		return (NULL);
825
826	for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
827		if (strcmp(vname, view->viewname) == 0 &&
828		    asn_compare_oid(&subtree, &view->subtree)== 0)
829			return (view);
830
831	return (NULL);
832}
833
834struct vacm_view *
835vacm_get_next_view(const struct asn_oid *oid, uint sub)
836{
837	char vname[SNMP_ADM_STR32_SIZ];
838	struct asn_oid subtree;
839	struct vacm_view *view;
840
841	if (oid->len - sub == 0)
842		return (vacm_first_view());
843
844	if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0)
845		return (NULL);
846
847	for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
848		if (strcmp(vname, view->viewname) == 0 &&
849		    asn_compare_oid(&subtree, &view->subtree)== 0)
850			return (vacm_next_view(view));
851
852	return (NULL);
853}
854
855static struct vacm_view *
856vacm_get_view_by_name(u_char *octets, u_int len)
857{
858	struct vacm_view *view;
859
860	for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
861		if (strlen(view->viewname) == len &&
862		    memcmp(octets, view->viewname, len) == 0)
863			return (view);
864
865	return (NULL);
866}
867
868static struct vacm_context *
869vacm_get_context(const struct asn_oid *oid, uint sub)
870{
871	char cname[SNMP_ADM_STR32_SIZ];
872	size_t cnamelen;
873	u_int index_count;
874	struct vacm_context *vacm_ctx;
875
876	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
877		return (NULL);
878
879	index_count = 0;
880	index_count = SNMP_INDEX(index_count, 1);
881	if (index_decode(oid, sub, index_count, &cname, &cnamelen))
882		return (NULL);
883
884	for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL;
885	    vacm_ctx = vacm_next_context(vacm_ctx))
886		if (strcmp(cname, vacm_ctx->ctxname) == 0)
887			return (vacm_ctx);
888
889	return (NULL);
890}
891
892static struct vacm_context *
893vacm_get_next_context(const struct asn_oid *oid, uint sub)
894{
895	char cname[SNMP_ADM_STR32_SIZ];
896	size_t cnamelen;
897	u_int index_count;
898	struct vacm_context *vacm_ctx;
899
900	if (oid->len - sub == 0)
901		return (vacm_first_context());
902
903	if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ)
904		return (NULL);
905
906	index_count = 0;
907	index_count = SNMP_INDEX(index_count, 1);
908	if (index_decode(oid, sub, index_count, &cname, &cnamelen))
909		return (NULL);
910
911	for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL;
912	    vacm_ctx = vacm_next_context(vacm_ctx))
913		if (strcmp(cname, vacm_ctx->ctxname) == 0)
914			return (vacm_next_context(vacm_ctx));
915
916	return (NULL);
917}
918
919static void
920vacm_append_ctxindex(struct asn_oid *oid, uint sub,
921    const struct vacm_context *ctx)
922{
923	uint32_t i;
924
925	oid->len = sub + strlen(ctx->ctxname) + 1;
926	oid->subs[sub] = strlen(ctx->ctxname);
927	for (i = 1; i <= strlen(ctx->ctxname); i++)
928		oid->subs[sub + i] = ctx->ctxname[i - 1];
929}
930
931/*
932 * VACM snmp module initialization hook.
933 * Returns 0 on success, < 0 on error.
934 */
935static int
936vacm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
937{
938	vacm_module = mod;
939	vacm_lock = random();
940	vacm_groups_init();
941
942	/* XXX: TODO - initialize structures */
943	return (0);
944}
945
946/*
947 * VACM snmp module finalization hook.
948 */
949static int
950vacm_fini(void)
951{
952	/* XXX: TODO - cleanup */
953	vacm_flush_contexts(reg_vacm);
954	or_unregister(reg_vacm);
955
956	return (0);
957}
958
959/*
960 * VACM snmp module start operation.
961 */
962static void
963vacm_start(void)
964{
965	static char dflt_ctx[] = "";
966
967	reg_vacm = or_register(&oid_vacm,
968	    "The MIB module for managing SNMP View-based Access Control Model.",
969	    vacm_module);
970
971	(void)vacm_add_context(dflt_ctx, reg_vacm);
972}
973
974static void
975vacm_dump(void)
976{
977	struct vacm_context *vacmctx;
978	struct vacm_user *vuser;
979	struct vacm_access *vacl;
980	struct vacm_view *view;
981	static char oidbuf[ASN_OIDSTRLEN];
982
983	syslog(LOG_ERR, "\n");
984	syslog(LOG_ERR, "Context list:");
985	for (vacmctx = vacm_first_context(); vacmctx != NULL;
986	    vacmctx = vacm_next_context(vacmctx))
987		syslog(LOG_ERR, "Context \"%s\", module id %d",
988		    vacmctx->ctxname, vacmctx->regid);
989
990	syslog(LOG_ERR, "VACM users:");
991	for (vuser = vacm_first_user(); vuser != NULL;
992	    vuser = vacm_next_user(vuser))
993		syslog(LOG_ERR, "Uname %s, Group %s, model %d", vuser->secname,
994		    vuser->group!= NULL?vuser->group->groupname:"Unknown",
995		    vuser->sec_model);
996
997	syslog(LOG_ERR, "VACM Access rules:");
998	for (vacl = vacm_first_access_rule(); vacl != NULL;
999	    vacl = vacm_next_access_rule(vacl))
1000		syslog(LOG_ERR, "Group %s, CtxPrefix %s, Model %d, Level %d, "
1001		    "RV %s, WR %s, NV %s", vacl->group!=NULL?
1002		    vacl->group->groupname:"Unknown", vacl->ctx_prefix,
1003		    vacl->sec_model, vacl->sec_level, vacl->read_view!=NULL?
1004		    vacl->read_view->viewname:"None", vacl->write_view!=NULL?
1005		    vacl->write_view->viewname:"None", vacl->notify_view!=NULL?
1006		    vacl->notify_view->viewname:"None");
1007
1008	syslog(LOG_ERR, "VACM Views:");
1009	for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view))
1010		syslog(LOG_ERR, "View %s, Tree %s - %s", view->viewname,
1011		    asn_oid2str_r(&view->subtree, oidbuf), view->exclude?
1012		    "excluded":"included");
1013}
1014
1015const char vacm_comment[] = \
1016"This module implements SNMP View-based Access Control Model defined in RFC 3415.";
1017
1018const struct snmp_module config = {
1019	.comment =	vacm_comment,
1020	.init =		vacm_init,
1021	.fini =		vacm_fini,
1022	.start =	vacm_start,
1023	.tree =		vacm_ctree,
1024	.dump =		vacm_dump,
1025	.tree_size =	vacm_CTREE_SIZE,
1026};
1027