]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/snmp_usm/usm_snmp.c
MFV: r363292
[FreeBSD/FreeBSD.git] / contrib / bsnmp / snmp_usm / usm_snmp.c
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 };