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
50static struct lmodule *vacm_module;
51/* For the registration. */
52static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB;
53
54static uint reg_vacm;
55
56static int32_t vacm_lock;
57
58/*
59 * Internal datastructures and forward declarations.
60 */
61static void		vacm_append_userindex(struct asn_oid *,
62    uint, const struct vacm_user *);
63static int		vacm_user_index_decode(const struct asn_oid *,
64    uint, int32_t *, char *);
65static struct vacm_user *vacm_get_user(const struct asn_oid *,
66    uint);
67static struct vacm_user *vacm_get_next_user(const struct asn_oid *,
68    uint);
69static void		vacm_append_access_rule_index(struct asn_oid *,
70    uint, const struct vacm_access *);
71static int		vacm_access_rule_index_decode(const struct asn_oid *,
72    uint, char *, char *, int32_t *, int32_t *);
73static struct vacm_access *	vacm_get_access_rule(const struct asn_oid *,
74    uint);
75static struct vacm_access *	vacm_get_next_access_rule(const struct asn_oid *,
76    uint);
77static int		vacm_view_index_decode(const struct asn_oid *, uint,
78    char *, struct asn_oid *);
79static void		vacm_append_viewindex(struct asn_oid *, uint,
80    const struct vacm_view *);
81static struct vacm_view	*vacm_get_view(const struct asn_oid *, uint);
82static struct vacm_view	*vacm_get_next_view(const struct asn_oid *, uint);
83static struct vacm_view *vacm_get_view_by_name(u_char *, u_int);
84static struct vacm_context	*vacm_get_context(const struct asn_oid *, uint);
85static struct vacm_context	*vacm_get_next_context(const struct asn_oid *,
86    uint);
87static void			vacm_append_ctxindex(struct asn_oid *, uint,
88    const struct vacm_context *);
89
90int
91op_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
138int
139op_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
262int
263op_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
426int
427op_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
457int
458op_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
612static void
613vacm_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
625static int
626vacm_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
643static struct vacm_user *
644vacm_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
661static struct vacm_user *
662vacm_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
682static void
683vacm_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
704static int
705vacm_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
732struct vacm_access *
733vacm_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
753struct vacm_access *
754vacm_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
777static int
778vacm_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
801static void
802vacm_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
817struct vacm_view *
818vacm_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
835struct vacm_view *
836vacm_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
856static struct vacm_view *
857vacm_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
869static struct vacm_context *
870vacm_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
893static struct vacm_context *
894vacm_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
920static void
921vacm_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 */
936static int
937vacm_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 */
950static int
951vacm_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 */
963static void
964vacm_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
975static void
976vacm_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
1016static const char vacm_comment[] = \
1017"This module implements SNMP View-based Access Control Model defined in RFC 3415.";
1018
1019extern const struct snmp_module config;
1020const 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