]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/snmp_vacm/vacm_snmp.c
Fix igb interfaces failing to switch to inactive state.
[FreeBSD/FreeBSD.git] / contrib / bsnmp / snmp_vacm / vacm_snmp.c
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 };