xref: /freebsd/contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision cdebaff820b2a4915a16cedfd511823d78aab171)
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$
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 
49 static struct lmodule *vacm_module;
50 /* For the registration. */
51 static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB;
52 
53 static uint reg_vacm;
54 
55 static int32_t vacm_lock;
56 
57 /*
58  * Internal datastructures and forward declarations.
59  */
60 static void		vacm_append_userindex(struct asn_oid *,
61     uint, const struct vacm_user *);
62 static int		vacm_user_index_decode(const struct asn_oid *,
63     uint, int32_t *, char *);
64 static struct vacm_user *vacm_get_user(const struct asn_oid *,
65     uint);
66 static struct vacm_user *vacm_get_next_user(const struct asn_oid *,
67     uint);
68 static void		vacm_append_access_rule_index(struct asn_oid *,
69     uint, const struct vacm_access *);
70 static int		vacm_access_rule_index_decode(const struct asn_oid *,
71     uint, char *, char *, int32_t *, int32_t *);
72 static struct vacm_access *	vacm_get_access_rule(const struct asn_oid *,
73     uint);
74 static struct vacm_access *	vacm_get_next_access_rule(const struct asn_oid *,
75     uint);
76 static int		vacm_view_index_decode(const struct asn_oid *, uint,
77     char *, struct asn_oid *);
78 static void		vacm_append_viewindex(struct asn_oid *, uint,
79     const struct vacm_view *);
80 static struct vacm_view	*vacm_get_view(const struct asn_oid *, uint);
81 static struct vacm_view	*vacm_get_next_view(const struct asn_oid *, uint);
82 static struct vacm_view *vacm_get_view_by_name(u_char *, u_int);
83 static struct vacm_context	*vacm_get_context(const struct asn_oid *, uint);
84 static struct vacm_context	*vacm_get_next_context(const struct asn_oid *,
85     uint);
86 static void			vacm_append_ctxindex(struct asn_oid *, uint,
87     const struct vacm_context *);
88 
89 int
90 op_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 
137 int
138 op_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 
261 int
262 op_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 
425 int
426 op_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 
456 int
457 op_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 
611 static void
612 vacm_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 
624 static int
625 vacm_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 
642 static struct vacm_user *
643 vacm_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 
660 static struct vacm_user *
661 vacm_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 
681 static void
682 vacm_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 
703 static int
704 vacm_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 
731 struct vacm_access *
732 vacm_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 
752 struct vacm_access *
753 vacm_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 
776 static int
777 vacm_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 
800 static void
801 vacm_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 
816 struct vacm_view *
817 vacm_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 
834 struct vacm_view *
835 vacm_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 
855 static struct vacm_view *
856 vacm_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 
868 static struct vacm_context *
869 vacm_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 
892 static struct vacm_context *
893 vacm_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 
919 static void
920 vacm_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  */
935 static int
936 vacm_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  */
949 static int
950 vacm_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  */
962 static void
963 vacm_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 
974 static void
975 vacm_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 
1015 const char vacm_comment[] = \
1016 "This module implements SNMP View-based Access Control Model defined in RFC 3415.";
1017 
1018 const 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