PageRenderTime 75ms CodeModel.GetById 14ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bsnmp/snmp_usm/usm_snmp.c

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