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: stable/10/contrib/bsnmp/snmp_usm/usm_snmp.c 313222 2017-02-04 16:37:43Z ngie $
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		/*
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
454static int
455usm_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
479static void
480usm_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
497static struct usm_user *
498usm_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
510static struct usm_user *
511usm_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 */
534static int
535usm_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 */
546static int
547usm_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 */
558static void
559usm_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
566static void
567usm_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
607const char usm_comment[] = \
608"This module implements SNMP User-based Security Model defined in RFC 3414.";
609
610const 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