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