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
50static struct lmodule *usm_module;
51/* For the registration. */
52static const struct asn_oid oid_usm = OIDX_snmpUsmMIB;
53
54static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol;
55static const struct asn_oid oid_usmHMACMD5AuthProtocol =		\
56    OIDX_usmHMACMD5AuthProtocol;
57static const struct asn_oid oid_usmHMACSHAAuthProtocol =		\
58    OIDX_usmHMACSHAAuthProtocol;
59
60static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol;
61static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol;
62static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol;
63
64static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName;
65
66/* The registration. */
67static uint reg_usm;
68
69static int32_t usm_lock;
70
71static struct usm_user *	usm_get_user(const struct asn_oid *, uint);
72static struct usm_user *	usm_get_next_user(const struct asn_oid *, uint);
73static void	usm_append_userindex(struct asn_oid *, uint,
74    const struct usm_user *);
75static int	usm_user_index_decode(const struct asn_oid *, uint, uint8_t *,
76    uint32_t *, char *);
77
78int
79op_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
118int
119op_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
146int
147op_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
455static int
456usm_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
480static void
481usm_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
498static struct usm_user *
499usm_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
511static struct usm_user *
512usm_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 */
535static int
536usm_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 */
547static int
548usm_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 */
559static void
560usm_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
567static void
568usm_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
608static const char usm_comment[] =
609"This module implements SNMP User-based Security Model defined in RFC 3414.";
610
611extern const struct snmp_module config;
612const 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