xref: /freebsd/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
1 /*-
2  * Copyright (c) 2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Shteryana Sotirova Shopova under
6  * sponsorship from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 #include <sys/queue.h>
32 #include <sys/types.h>
33 
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <syslog.h>
41 
42 #include "asn1.h"
43 #include "snmp.h"
44 #include "snmpmod.h"
45 
46 #include "usm_tree.h"
47 #include "usm_oid.h"
48 
49 static struct lmodule *usm_module;
50 /* For the registration. */
51 static const struct asn_oid oid_usm = OIDX_snmpUsmMIB;
52 
53 static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol;
54 static const struct asn_oid oid_usmHMACMD5AuthProtocol =		\
55     OIDX_usmHMACMD5AuthProtocol;
56 static const struct asn_oid oid_usmHMACSHAAuthProtocol =		\
57     OIDX_usmHMACSHAAuthProtocol;
58 
59 static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol;
60 static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol;
61 static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol;
62 
63 static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName;
64 
65 /* The registration. */
66 static uint reg_usm;
67 
68 static int32_t usm_lock;
69 
70 static struct usm_user *	usm_get_user(const struct asn_oid *, uint);
71 static struct usm_user *	usm_get_next_user(const struct asn_oid *, uint);
72 static void	usm_append_userindex(struct asn_oid *, uint,
73     const struct usm_user *);
74 static int	usm_user_index_decode(const struct asn_oid *, uint, uint8_t *,
75     uint32_t *, char *);
76 
77 int
78 op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
79     uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op)
80 {
81 	struct snmpd_usmstat *usmstats;
82 
83 	if (op == SNMP_OP_SET)
84 		return (SNMP_ERR_NOT_WRITEABLE);
85 
86 	if ((usmstats = bsnmpd_get_usm_stats()) == NULL)
87 		return (SNMP_ERR_GENERR);
88 
89 	if (op == SNMP_OP_GET) {
90 		switch (val->var.subs[sub - 1]) {
91 		case LEAF_usmStatsUnsupportedSecLevels:
92 			val->v.uint32 = usmstats->unsupported_seclevels;
93 			break;
94 		case LEAF_usmStatsNotInTimeWindows:
95 			val->v.uint32 = usmstats->not_in_time_windows;
96 			break;
97 		case LEAF_usmStatsUnknownUserNames:
98 			val->v.uint32 = usmstats->unknown_users;
99 			break;
100 		case LEAF_usmStatsUnknownEngineIDs:
101 			val->v.uint32 = usmstats->unknown_engine_ids;
102 			break;
103 		case LEAF_usmStatsWrongDigests:
104 			val->v.uint32 = usmstats->wrong_digests;
105 			break;
106 		case LEAF_usmStatsDecryptionErrors:
107 			val->v.uint32 = usmstats->decrypt_errors;
108 			break;
109 		default:
110 			return (SNMP_ERR_NOSUCHNAME);
111 		}
112 		return (SNMP_ERR_NOERROR);
113 	}
114 	abort();
115 }
116 
117 int
118 op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
119     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
120 {
121 	if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock)
122 		return (SNMP_ERR_NOSUCHNAME);
123 
124 	switch (op) {
125 	case SNMP_OP_GET:
126 		if (++usm_lock == INT32_MAX)
127 			usm_lock = 0;
128 		val->v.integer = usm_lock;
129 		break;
130 	case SNMP_OP_GETNEXT:
131 		abort();
132 	case SNMP_OP_SET:
133 		if (val->v.integer != usm_lock)
134 			return (SNMP_ERR_INCONS_VALUE);
135 		break;
136 	case SNMP_OP_ROLLBACK:
137 		/* FALLTHROUGH */
138 	case SNMP_OP_COMMIT:
139 		break;
140 	}
141 
142 	return (SNMP_ERR_NOERROR);
143 }
144 
145 int
146 op_usm_users(struct snmp_context *ctx, struct snmp_value *val,
147     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
148 {
149 	uint32_t elen;
150 	struct usm_user *uuser, *clone;
151 	char uname[SNMP_ADM_STR32_SIZ];
152 	uint8_t eid[SNMP_ENGINE_ID_SIZ];
153 
154 	switch (op) {
155 	case SNMP_OP_GET:
156 		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
157 			return (SNMP_ERR_NOSUCHNAME);
158 		break;
159 
160 	case SNMP_OP_GETNEXT:
161 		if ((uuser = usm_get_next_user(&val->var, sub)) == NULL)
162 			return (SNMP_ERR_NOSUCHNAME);
163 		usm_append_userindex(&val->var, sub, uuser);
164 		break;
165 
166 	case SNMP_OP_SET:
167 		if ((uuser = usm_get_user(&val->var, sub)) == NULL &&
168 		    val->var.subs[sub - 1] != LEAF_usmUserStatus &&
169 		    val->var.subs[sub - 1] != LEAF_usmUserCloneFrom)
170 			return (SNMP_ERR_NOSUCHNAME);
171 
172 		/*
173 		 * XXX (ngie): need to investigate the MIB to determine how
174 		 * this is possible given some of the transitions below.
175 		 */
176 		if (community != COMM_INITIALIZE &&
177 		    uuser != NULL && uuser->type == StorageType_readOnly)
178 			return (SNMP_ERR_NOT_WRITEABLE);
179 
180 		switch (val->var.subs[sub - 1]) {
181 		case LEAF_usmUserSecurityName:
182 			return (SNMP_ERR_NOT_WRITEABLE);
183 
184 		case LEAF_usmUserCloneFrom:
185 			if (uuser != NULL || usm_user_index_decode(&val->var,
186 			    sub, eid, &elen, uname) < 0 ||
187 			    !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid)))
188 				return (SNMP_ERR_WRONG_VALUE);
189 			if ((clone = usm_get_user(&val->v.oid, sub)) == NULL)
190 				return (SNMP_ERR_INCONS_VALUE);
191 			if ((uuser = usm_new_user(eid, elen, uname)) == NULL)
192 				return (SNMP_ERR_GENERR);
193 			uuser->status = RowStatus_notReady;
194 			if (community != COMM_INITIALIZE)
195 				uuser->type = StorageType_volatile;
196 			else
197 				uuser->type = StorageType_readOnly;
198 
199 			uuser->suser.auth_proto = clone->suser.auth_proto;
200 			uuser->suser.priv_proto = clone->suser.priv_proto;
201 			memcpy(uuser->suser.auth_key, clone->suser.auth_key,
202 			    sizeof(uuser->suser.auth_key));
203 			memcpy(uuser->suser.priv_key, clone->suser.priv_key,
204 			    sizeof(uuser->suser.priv_key));
205 			ctx->scratch->int1 = RowStatus_createAndWait;
206 			break;
207 
208 		case LEAF_usmUserAuthProtocol:
209 			ctx->scratch->int1 = uuser->suser.auth_proto;
210 			if (asn_compare_oid(&oid_usmNoAuthProtocol,
211 			    &val->v.oid) == 0)
212 				uuser->suser.auth_proto = SNMP_AUTH_NOAUTH;
213 			else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol,
214 			    &val->v.oid) == 0)
215 				uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5;
216 			else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol,
217 			    &val->v.oid) == 0)
218 				uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA;
219 			else
220 				return (SNMP_ERR_WRONG_VALUE);
221 			break;
222 
223 		case LEAF_usmUserAuthKeyChange:
224 		case LEAF_usmUserOwnAuthKeyChange:
225 			if (val->var.subs[sub - 1] ==
226 			    LEAF_usmUserOwnAuthKeyChange &&
227 			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
228 			    usm_user->suser.sec_name) != 0))
229 				return (SNMP_ERR_NO_ACCESS);
230 			if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ)
231 				return (SNMP_ERR_INCONS_VALUE);
232 			ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ);
233 			if (ctx->scratch->ptr1 == NULL)
234 				return (SNMP_ERR_GENERR);
235 			memcpy(ctx->scratch->ptr1, uuser->suser.auth_key,
236 			    SNMP_AUTH_KEY_SIZ);
237 			memcpy(uuser->suser.auth_key, val->v.octetstring.octets,
238 			    val->v.octetstring.len);
239 			break;
240 
241 		case LEAF_usmUserPrivProtocol:
242 			ctx->scratch->int1 = uuser->suser.priv_proto;
243 			if (asn_compare_oid(&oid_usmNoPrivProtocol,
244 			    &val->v.oid) == 0)
245 				uuser->suser.priv_proto = SNMP_PRIV_NOPRIV;
246 			else if (asn_compare_oid(&oid_usmDESPrivProtocol,
247 			    &val->v.oid) == 0)
248 				uuser->suser.priv_proto = SNMP_PRIV_DES;
249 			else if (asn_compare_oid(&oid_usmAesCfb128Protocol,
250 			    &val->v.oid) == 0)
251 				uuser->suser.priv_proto = SNMP_PRIV_AES;
252 			else
253 				return (SNMP_ERR_WRONG_VALUE);
254 			break;
255 
256 		case LEAF_usmUserPrivKeyChange:
257 		case LEAF_usmUserOwnPrivKeyChange:
258 			if (val->var.subs[sub - 1] ==
259 			    LEAF_usmUserOwnPrivKeyChange &&
260 			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
261 			    usm_user->suser.sec_name) != 0))
262 				return (SNMP_ERR_NO_ACCESS);
263 			if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ)
264 				return (SNMP_ERR_INCONS_VALUE);
265 			ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ);
266 			if (ctx->scratch->ptr1 == NULL)
267 				return (SNMP_ERR_GENERR);
268 			memcpy(ctx->scratch->ptr1, uuser->suser.priv_key,
269 			    sizeof(uuser->suser.priv_key));
270 			memcpy(uuser->suser.priv_key, val->v.octetstring.octets,
271 			    val->v.octetstring.len);
272 			break;
273 
274 		case LEAF_usmUserPublic:
275 			if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ)
276 				return (SNMP_ERR_INCONS_VALUE);
277 			if (uuser->user_public_len > 0) {
278 				ctx->scratch->ptr2 =
279 				    malloc(uuser->user_public_len);
280 				if (ctx->scratch->ptr2 == NULL)
281 					return (SNMP_ERR_GENERR);
282 				memcpy(ctx->scratch->ptr2, uuser->user_public,
283 			 	   uuser->user_public_len);
284 				ctx->scratch->int2 = uuser->user_public_len;
285 			}
286 			if (val->v.octetstring.len > 0) {
287 				memcpy(uuser->user_public,
288 				    val->v.octetstring.octets,
289 				    val->v.octetstring.len);
290 				uuser->user_public_len = val->v.octetstring.len;
291 			} else {
292 				memset(uuser->user_public, 0,
293 				    sizeof(uuser->user_public));
294 				uuser->user_public_len = 0;
295 			}
296 			break;
297 
298 		case LEAF_usmUserStorageType:
299 			return (SNMP_ERR_INCONS_VALUE);
300 
301 		case LEAF_usmUserStatus:
302 			if (uuser == NULL) {
303 				if (val->v.integer != RowStatus_createAndWait ||
304 				    usm_user_index_decode(&val->var, sub, eid,
305 				    &elen, uname) < 0)
306 					return (SNMP_ERR_INCONS_VALUE);
307 				uuser = usm_new_user(eid, elen, uname);
308 				if (uuser == NULL)
309 					return (SNMP_ERR_GENERR);
310 				uuser->status = RowStatus_notReady;
311 				if (community != COMM_INITIALIZE)
312 					uuser->type = StorageType_volatile;
313 				else
314 					uuser->type = StorageType_readOnly;
315 			} else if (val->v.integer != RowStatus_active &&
316 			    val->v.integer != RowStatus_destroy)
317 				return (SNMP_ERR_INCONS_VALUE);
318 
319 			uuser->status = val->v.integer;
320 			break;
321 		}
322 		return (SNMP_ERR_NOERROR);
323 
324 	case SNMP_OP_COMMIT:
325 		switch (val->var.subs[sub - 1]) {
326 		case LEAF_usmUserAuthKeyChange:
327 		case LEAF_usmUserOwnAuthKeyChange:
328 		case LEAF_usmUserPrivKeyChange:
329 		case LEAF_usmUserOwnPrivKeyChange:
330 			free(ctx->scratch->ptr1);
331 			break;
332 		case LEAF_usmUserPublic:
333 			if (ctx->scratch->ptr2 != NULL)
334 				free(ctx->scratch->ptr2);
335 			break;
336 		case LEAF_usmUserStatus:
337 			if (val->v.integer != RowStatus_destroy)
338 				break;
339 			if ((uuser = usm_get_user(&val->var, sub)) == NULL)
340 				return (SNMP_ERR_GENERR);
341 			usm_delete_user(uuser);
342 			break;
343 		default:
344 			break;
345 		}
346 		return (SNMP_ERR_NOERROR);
347 
348 	case SNMP_OP_ROLLBACK:
349 		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
350 			return (SNMP_ERR_GENERR);
351 		switch (val->var.subs[sub - 1]) {
352 		case LEAF_usmUserAuthProtocol:
353 			uuser->suser.auth_proto = ctx->scratch->int1;
354 			break;
355 		case LEAF_usmUserAuthKeyChange:
356 		case LEAF_usmUserOwnAuthKeyChange:
357 			memcpy(uuser->suser.auth_key, ctx->scratch->ptr1,
358 			    sizeof(uuser->suser.auth_key));
359 			free(ctx->scratch->ptr1);
360 			break;
361 		case LEAF_usmUserPrivProtocol:
362 			uuser->suser.priv_proto = ctx->scratch->int1;
363 			break;
364 		case LEAF_usmUserPrivKeyChange:
365 		case LEAF_usmUserOwnPrivKeyChange:
366 			memcpy(uuser->suser.priv_key, ctx->scratch->ptr1,
367 			    sizeof(uuser->suser.priv_key));
368 			free(ctx->scratch->ptr1);
369 			break;
370 		case LEAF_usmUserPublic:
371 			if (ctx->scratch->ptr2 != NULL) {
372 				memcpy(uuser->user_public, ctx->scratch->ptr2,
373 			 	   ctx->scratch->int2);
374 				uuser->user_public_len = ctx->scratch->int2;
375 				free(ctx->scratch->ptr2);
376 			} else {
377 				memset(uuser->user_public, 0,
378 				    sizeof(uuser->user_public));
379 				uuser->user_public_len = 0;
380 			}
381 			break;
382 		case LEAF_usmUserCloneFrom:
383 		case LEAF_usmUserStatus:
384 			if (ctx->scratch->int1 == RowStatus_createAndWait)
385 				usm_delete_user(uuser);
386 			break;
387 		default:
388 			break;
389 		}
390 		return (SNMP_ERR_NOERROR);
391 
392 	default:
393 		abort();
394 	}
395 
396 	switch (val->var.subs[sub - 1]) {
397 	case LEAF_usmUserSecurityName:
398 		return (string_get(val, uuser->suser.sec_name, -1));
399 	case LEAF_usmUserCloneFrom:
400 		memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero));
401 		break;
402 	case LEAF_usmUserAuthProtocol:
403 		switch (uuser->suser.auth_proto) {
404 		case SNMP_AUTH_HMAC_MD5:
405 			memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol,
406 			    sizeof(oid_usmHMACMD5AuthProtocol));
407 			break;
408 		case SNMP_AUTH_HMAC_SHA:
409 			memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol,
410 			    sizeof(oid_usmHMACSHAAuthProtocol));
411 			break;
412 		default:
413 			memcpy(&val->v.oid, &oid_usmNoAuthProtocol,
414 			    sizeof(oid_usmNoAuthProtocol));
415 			break;
416 		}
417 		break;
418 	case LEAF_usmUserAuthKeyChange:
419 	case LEAF_usmUserOwnAuthKeyChange:
420 		return (string_get(val, (char *)uuser->suser.auth_key, 0));
421 	case LEAF_usmUserPrivProtocol:
422 		switch (uuser->suser.priv_proto) {
423 		case SNMP_PRIV_DES:
424 			memcpy(&val->v.oid, &oid_usmDESPrivProtocol,
425 			    sizeof(oid_usmDESPrivProtocol));
426 			break;
427 		case SNMP_PRIV_AES:
428 			memcpy(&val->v.oid, &oid_usmAesCfb128Protocol,
429 			    sizeof(oid_usmAesCfb128Protocol));
430 			break;
431 		default:
432 			memcpy(&val->v.oid, &oid_usmNoPrivProtocol,
433 			    sizeof(oid_usmNoPrivProtocol));
434 			break;
435 		}
436 		break;
437 	case LEAF_usmUserPrivKeyChange:
438 	case LEAF_usmUserOwnPrivKeyChange:
439 		return (string_get(val, (char *)uuser->suser.priv_key, 0));
440 	case LEAF_usmUserPublic:
441 		return (string_get(val, uuser->user_public,
442 		    uuser->user_public_len));
443 	case LEAF_usmUserStorageType:
444 		val->v.integer = uuser->type;
445 		break;
446 	case LEAF_usmUserStatus:
447 		val->v.integer = uuser->status;
448 		break;
449 	}
450 
451 	return (SNMP_ERR_NOERROR);
452 }
453 
454 static int
455 usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine,
456     uint32_t *elen, char *uname)
457 {
458 	uint32_t i, nlen;
459 	int uname_off;
460 
461 	if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ)
462 		return (-1);
463 
464 	for (i = 0; i < oid->subs[sub]; i++)
465 		engine[i] = oid->subs[sub + i + 1];
466 	*elen = i;
467 
468 	uname_off = sub + oid->subs[sub] + 1;
469 	if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ)
470 		return (-1);
471 
472 	for (i = 0; i < nlen; i++)
473 		uname[i] = oid->subs[uname_off + i + 1];
474 	uname[nlen] = '\0';
475 
476 	return (0);
477 }
478 
479 static void
480 usm_append_userindex(struct asn_oid *oid, uint sub,
481     const struct usm_user *uuser)
482 {
483 	uint32_t i;
484 
485 	oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name);
486 	oid->len += 2;
487 	oid->subs[sub] = uuser->user_engine_len;
488 	for (i = 1; i < uuser->user_engine_len + 1; i++)
489 		oid->subs[sub + i] = uuser->user_engine_id[i - 1];
490 
491 	sub += uuser->user_engine_len + 1;
492 	oid->subs[sub] = strlen(uuser->suser.sec_name);
493 	for (i = 1; i <= oid->subs[sub]; i++)
494 		oid->subs[sub + i] = uuser->suser.sec_name[i - 1];
495 }
496 
497 static struct usm_user *
498 usm_get_user(const struct asn_oid *oid, uint sub)
499 {
500 	uint32_t enginelen;
501 	char username[SNMP_ADM_STR32_SIZ];
502 	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
503 
504 	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
505 		return (NULL);
506 
507 	return (usm_find_user(engineid, enginelen, username));
508 }
509 
510 static struct usm_user *
511 usm_get_next_user(const struct asn_oid *oid, uint sub)
512 {
513 	uint32_t enginelen;
514 	char username[SNMP_ADM_STR32_SIZ];
515 	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
516 	struct usm_user *uuser;
517 
518 	if (oid->len - sub == 0)
519 		return (usm_first_user());
520 
521 	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
522 		return (NULL);
523 
524 	if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL)
525 		return (usm_next_user(uuser));
526 
527 	return (NULL);
528 }
529 
530 /*
531  * USM snmp module initialization hook.
532  * Returns 0 on success, < 0 on error.
533  */
534 static int
535 usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
536 {
537 	usm_module = mod;
538 	usm_lock = random();
539 	bsnmpd_reset_usm_stats();
540 	return (0);
541 }
542 
543 /*
544  * USM snmp module finalization hook.
545  */
546 static int
547 usm_fini(void)
548 {
549 	usm_flush_users();
550 	or_unregister(reg_usm);
551 
552 	return (0);
553 }
554 
555 /*
556  * USM snmp module start operation.
557  */
558 static void
559 usm_start(void)
560 {
561 	reg_usm = or_register(&oid_usm,
562 	    "The MIB module for managing SNMP User-Based Security Model.",
563 	    usm_module);
564 }
565 
566 static void
567 usm_dump(void)
568 {
569 	struct usm_user *uuser;
570 	struct snmpd_usmstat *usmstats;
571 	const char *const authstr[] = {
572 		"noauth",
573 		"md5",
574 		"sha",
575 		NULL
576 	};
577 	const char *const privstr[] = {
578 		"nopriv",
579 		"des",
580 		"aes",
581 		NULL
582 	};
583 
584 	if ((usmstats = bsnmpd_get_usm_stats()) != NULL) {
585 		syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u",
586 		    usmstats->unsupported_seclevels);
587 		syslog(LOG_ERR, "NotInTimeWindows\t\t%u",
588 		    usmstats->not_in_time_windows);
589 		syslog(LOG_ERR, "UnknownUserNames\t\t%u",
590 		    usmstats->unknown_users);
591 		syslog(LOG_ERR, "UnknownEngineIDs\t\t%u",
592 		    usmstats->unknown_engine_ids);
593 		syslog(LOG_ERR, "WrongDigests\t\t%u",
594 		    usmstats->wrong_digests);
595 		syslog(LOG_ERR, "DecryptionErrors\t\t%u",
596 		    usmstats->decrypt_errors);
597 	}
598 
599 	syslog(LOG_ERR, "USM users");
600 	for (uuser = usm_first_user(); uuser != NULL;
601 	    (uuser = usm_next_user(uuser)))
602 		syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name,
603 		    authstr[uuser->suser.auth_proto],
604 		    privstr[uuser->suser.priv_proto]);
605 }
606 
607 const char usm_comment[] = \
608 "This module implements SNMP User-based Security Model defined in RFC 3414.";
609 
610 const struct snmp_module config = {
611 	.comment =	usm_comment,
612 	.init =		usm_init,
613 	.fini =		usm_fini,
614 	.start =	usm_start,
615 	.tree =		usm_ctree,
616 	.dump =		usm_dump,
617 	.tree_size =	usm_CTREE_SIZE,
618 };
619