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