1216294Ssyrinx/*-
2216294Ssyrinx * Copyright (c) 2010 The FreeBSD Foundation
3216294Ssyrinx * All rights reserved.
4216294Ssyrinx *
5216294Ssyrinx * This software was developed by Shteryana Sotirova Shopova under
6216294Ssyrinx * sponsorship from the FreeBSD Foundation.
7216294Ssyrinx *
8216294Ssyrinx * Redistribution and use in source and binary forms, with or without
9216294Ssyrinx * modification, are permitted provided that the following conditions
10216294Ssyrinx * are met:
11216294Ssyrinx * 1. Redistributions of source code must retain the above copyright
12216294Ssyrinx *    notice, this list of conditions and the following disclaimer.
13216294Ssyrinx * 2. Redistributions in binary form must reproduce the above copyright
14216294Ssyrinx *    notice, this list of conditions and the following disclaimer in the
15216294Ssyrinx *    documentation and/or other materials provided with the distribution.
16216294Ssyrinx *
17216294Ssyrinx * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18216294Ssyrinx * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19216294Ssyrinx * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20216294Ssyrinx * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21216294Ssyrinx * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22216294Ssyrinx * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23216294Ssyrinx * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24216294Ssyrinx * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25216294Ssyrinx * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26216294Ssyrinx * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27216294Ssyrinx * SUCH DAMAGE.
28216294Ssyrinx *
29216294Ssyrinx * $FreeBSD$
30216294Ssyrinx */
31216294Ssyrinx#include <sys/queue.h>
32216294Ssyrinx#include <sys/types.h>
33216294Ssyrinx
34216294Ssyrinx#include <errno.h>
35216294Ssyrinx#include <stdarg.h>
36216294Ssyrinx#include <stdlib.h>
37216294Ssyrinx#include <stdio.h>
38216294Ssyrinx#include <stdint.h>
39216294Ssyrinx#include <string.h>
40216294Ssyrinx#include <syslog.h>
41216294Ssyrinx
42216294Ssyrinx#include "asn1.h"
43216294Ssyrinx#include "snmp.h"
44216294Ssyrinx#include "snmpmod.h"
45216294Ssyrinx
46216294Ssyrinx#include "usm_tree.h"
47216294Ssyrinx#include "usm_oid.h"
48216294Ssyrinx
49216294Ssyrinxstatic struct lmodule *usm_module;
50216294Ssyrinx/* For the registration. */
51216294Ssyrinxstatic const struct asn_oid oid_usm = OIDX_snmpUsmMIB;
52216294Ssyrinx
53216294Ssyrinxstatic const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol;
54216294Ssyrinxstatic const struct asn_oid oid_usmHMACMD5AuthProtocol =		\
55216294Ssyrinx    OIDX_usmHMACMD5AuthProtocol;
56216294Ssyrinxstatic const struct asn_oid oid_usmHMACSHAAuthProtocol =		\
57216294Ssyrinx    OIDX_usmHMACSHAAuthProtocol;
58216294Ssyrinx
59216294Ssyrinxstatic const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol;
60216294Ssyrinxstatic const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol;
61216294Ssyrinxstatic const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol;
62216294Ssyrinx
63216294Ssyrinxstatic const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName;
64216294Ssyrinx
65216294Ssyrinx/* The registration. */
66216294Ssyrinxstatic uint reg_usm;
67216294Ssyrinx
68216294Ssyrinxstatic int32_t usm_lock;
69216294Ssyrinx
70216294Ssyrinxstatic struct usm_user *	usm_get_user(const struct asn_oid *, uint);
71216294Ssyrinxstatic struct usm_user *	usm_get_next_user(const struct asn_oid *, uint);
72216294Ssyrinxstatic void	usm_append_userindex(struct asn_oid *, uint,
73216294Ssyrinx    const struct usm_user *);
74216294Ssyrinxstatic int	usm_user_index_decode(const struct asn_oid *, uint, uint8_t *,
75216294Ssyrinx    uint32_t *, char *);
76216294Ssyrinx
77216294Ssyrinxint
78216294Ssyrinxop_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
79216294Ssyrinx    uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op)
80216294Ssyrinx{
81216294Ssyrinx	struct snmpd_usmstat *usmstats;
82216294Ssyrinx
83216294Ssyrinx	if (op == SNMP_OP_SET)
84216294Ssyrinx		return (SNMP_ERR_NOT_WRITEABLE);
85216294Ssyrinx
86216294Ssyrinx	if ((usmstats = bsnmpd_get_usm_stats()) == NULL)
87216294Ssyrinx		return (SNMP_ERR_GENERR);
88216294Ssyrinx
89216294Ssyrinx	if (op == SNMP_OP_GET) {
90216294Ssyrinx		switch (val->var.subs[sub - 1]) {
91216294Ssyrinx		case LEAF_usmStatsUnsupportedSecLevels:
92216294Ssyrinx			val->v.uint32 = usmstats->unsupported_seclevels;
93216294Ssyrinx			break;
94216294Ssyrinx		case LEAF_usmStatsNotInTimeWindows:
95216294Ssyrinx			val->v.uint32 = usmstats->not_in_time_windows;
96216294Ssyrinx			break;
97216294Ssyrinx		case LEAF_usmStatsUnknownUserNames:
98216294Ssyrinx			val->v.uint32 = usmstats->unknown_users;
99216294Ssyrinx			break;
100216294Ssyrinx		case LEAF_usmStatsUnknownEngineIDs:
101216294Ssyrinx			val->v.uint32 = usmstats->unknown_engine_ids;
102216294Ssyrinx			break;
103216294Ssyrinx		case LEAF_usmStatsWrongDigests:
104216294Ssyrinx			val->v.uint32 = usmstats->wrong_digests;
105216294Ssyrinx			break;
106216294Ssyrinx		case LEAF_usmStatsDecryptionErrors:
107216294Ssyrinx			val->v.uint32 = usmstats->decrypt_errors;
108216294Ssyrinx			break;
109216294Ssyrinx		default:
110216294Ssyrinx			return (SNMP_ERR_NOSUCHNAME);
111216294Ssyrinx		}
112216294Ssyrinx		return (SNMP_ERR_NOERROR);
113216294Ssyrinx	}
114216294Ssyrinx	abort();
115216294Ssyrinx}
116216294Ssyrinx
117216294Ssyrinxint
118216294Ssyrinxop_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
119216294Ssyrinx    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
120216294Ssyrinx{
121216294Ssyrinx	if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock)
122216294Ssyrinx		return (SNMP_ERR_NOSUCHNAME);
123216294Ssyrinx
124216294Ssyrinx	switch (op) {
125216294Ssyrinx	case SNMP_OP_GET:
126216294Ssyrinx		if (++usm_lock == INT32_MAX)
127216294Ssyrinx			usm_lock = 0;
128216294Ssyrinx		val->v.integer = usm_lock;
129216294Ssyrinx		break;
130216294Ssyrinx	case SNMP_OP_GETNEXT:
131216294Ssyrinx		abort();
132216294Ssyrinx	case SNMP_OP_SET:
133216294Ssyrinx		if (val->v.integer != usm_lock)
134216294Ssyrinx			return (SNMP_ERR_INCONS_VALUE);
135216294Ssyrinx		break;
136216294Ssyrinx	case SNMP_OP_ROLLBACK:
137216294Ssyrinx		/* FALLTHROUGH */
138216294Ssyrinx	case SNMP_OP_COMMIT:
139216294Ssyrinx		break;
140216294Ssyrinx	}
141216294Ssyrinx
142216294Ssyrinx	return (SNMP_ERR_NOERROR);
143216294Ssyrinx}
144216294Ssyrinx
145216294Ssyrinxint
146216294Ssyrinxop_usm_users(struct snmp_context *ctx, struct snmp_value *val,
147216294Ssyrinx    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
148216294Ssyrinx{
149216294Ssyrinx	uint32_t elen;
150216294Ssyrinx	struct usm_user *uuser, *clone;
151216294Ssyrinx	char uname[SNMP_ADM_STR32_SIZ];
152216294Ssyrinx	uint8_t eid[SNMP_ENGINE_ID_SIZ];
153216294Ssyrinx
154216294Ssyrinx	switch (op) {
155216294Ssyrinx	case SNMP_OP_GET:
156216294Ssyrinx		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
157216294Ssyrinx			return (SNMP_ERR_NOSUCHNAME);
158216294Ssyrinx		break;
159216294Ssyrinx
160216294Ssyrinx	case SNMP_OP_GETNEXT:
161216294Ssyrinx		if ((uuser = usm_get_next_user(&val->var, sub)) == NULL)
162216294Ssyrinx			return (SNMP_ERR_NOSUCHNAME);
163216294Ssyrinx		usm_append_userindex(&val->var, sub, uuser);
164216294Ssyrinx		break;
165216294Ssyrinx
166216294Ssyrinx	case SNMP_OP_SET:
167216294Ssyrinx		if ((uuser = usm_get_user(&val->var, sub)) == NULL &&
168216294Ssyrinx		    val->var.subs[sub - 1] != LEAF_usmUserStatus &&
169216294Ssyrinx		    val->var.subs[sub - 1] != LEAF_usmUserCloneFrom)
170216294Ssyrinx				return (SNMP_ERR_NOSUCHNAME);
171216294Ssyrinx
172216294Ssyrinx		if (community != COMM_INITIALIZE &&
173216294Ssyrinx		    uuser->type == StorageType_readOnly)
174216294Ssyrinx			return (SNMP_ERR_NOT_WRITEABLE);
175216294Ssyrinx
176216294Ssyrinx		switch (val->var.subs[sub - 1]) {
177216294Ssyrinx		case LEAF_usmUserSecurityName:
178216294Ssyrinx			return (SNMP_ERR_NOT_WRITEABLE);
179216294Ssyrinx
180216294Ssyrinx		case LEAF_usmUserCloneFrom:
181216294Ssyrinx			if (uuser != NULL || usm_user_index_decode(&val->var,
182216294Ssyrinx			    sub, eid, &elen, uname) < 0 ||
183216294Ssyrinx			    !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid)))
184216294Ssyrinx				return (SNMP_ERR_WRONG_VALUE);
185216294Ssyrinx			if ((clone = usm_get_user(&val->v.oid, sub)) == NULL)
186216294Ssyrinx				return (SNMP_ERR_INCONS_VALUE);
187216294Ssyrinx			if ((uuser = usm_new_user(eid, elen, uname)) == NULL)
188216294Ssyrinx				return (SNMP_ERR_GENERR);
189216294Ssyrinx			uuser->status = RowStatus_notReady;
190216294Ssyrinx			if (community != COMM_INITIALIZE)
191216294Ssyrinx				uuser->type = StorageType_volatile;
192216294Ssyrinx			else
193216294Ssyrinx				uuser->type = StorageType_readOnly;
194216294Ssyrinx
195216294Ssyrinx			uuser->suser.auth_proto = clone->suser.auth_proto;
196216294Ssyrinx			uuser->suser.priv_proto = clone->suser.priv_proto;
197216294Ssyrinx			memcpy(uuser->suser.auth_key, clone->suser.auth_key,
198216294Ssyrinx			    sizeof(uuser->suser.auth_key));
199216294Ssyrinx			memcpy(uuser->suser.priv_key, clone->suser.priv_key,
200216294Ssyrinx			    sizeof(uuser->suser.priv_key));
201216294Ssyrinx			ctx->scratch->int1 = RowStatus_createAndWait;
202216294Ssyrinx			break;
203216294Ssyrinx
204216294Ssyrinx		case LEAF_usmUserAuthProtocol:
205216294Ssyrinx			ctx->scratch->int1 = uuser->suser.auth_proto;
206216294Ssyrinx			if (asn_compare_oid(&oid_usmNoAuthProtocol,
207216294Ssyrinx			    &val->v.oid) == 0)
208216294Ssyrinx				uuser->suser.auth_proto = SNMP_AUTH_NOAUTH;
209216294Ssyrinx			else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol,
210216294Ssyrinx			    &val->v.oid) == 0)
211216294Ssyrinx				uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5;
212216294Ssyrinx			else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol,
213216294Ssyrinx			    &val->v.oid) == 0)
214216294Ssyrinx				uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA;
215216294Ssyrinx			else
216216294Ssyrinx				return (SNMP_ERR_WRONG_VALUE);
217216294Ssyrinx			break;
218216294Ssyrinx
219216294Ssyrinx		case LEAF_usmUserAuthKeyChange:
220216294Ssyrinx		case LEAF_usmUserOwnAuthKeyChange:
221216294Ssyrinx			if (val->var.subs[sub - 1] ==
222216294Ssyrinx			    LEAF_usmUserOwnAuthKeyChange &&
223216294Ssyrinx			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
224216294Ssyrinx			    usm_user->suser.sec_name) != 0))
225216294Ssyrinx				return (SNMP_ERR_NO_ACCESS);
226216294Ssyrinx			if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ)
227216294Ssyrinx				return (SNMP_ERR_INCONS_VALUE);
228216294Ssyrinx			ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ);
229216294Ssyrinx			if (ctx->scratch->ptr1 == NULL)
230216294Ssyrinx				return (SNMP_ERR_GENERR);
231216294Ssyrinx			memcpy(ctx->scratch->ptr1, uuser->suser.auth_key,
232216294Ssyrinx			    SNMP_AUTH_KEY_SIZ);
233216294Ssyrinx			memcpy(uuser->suser.auth_key, val->v.octetstring.octets,
234216294Ssyrinx			    val->v.octetstring.len);
235216294Ssyrinx			break;
236216294Ssyrinx
237216294Ssyrinx		case LEAF_usmUserPrivProtocol:
238216294Ssyrinx			ctx->scratch->int1 = uuser->suser.priv_proto;
239216294Ssyrinx			if (asn_compare_oid(&oid_usmNoPrivProtocol,
240216294Ssyrinx			    &val->v.oid) == 0)
241216294Ssyrinx				uuser->suser.priv_proto = SNMP_PRIV_NOPRIV;
242216294Ssyrinx			else if (asn_compare_oid(&oid_usmDESPrivProtocol,
243216294Ssyrinx			    &val->v.oid) == 0)
244216294Ssyrinx				uuser->suser.priv_proto = SNMP_PRIV_DES;
245216294Ssyrinx			else if (asn_compare_oid(&oid_usmAesCfb128Protocol,
246216294Ssyrinx			    &val->v.oid) == 0)
247216294Ssyrinx				uuser->suser.priv_proto = SNMP_PRIV_AES;
248216294Ssyrinx			else
249216294Ssyrinx				return (SNMP_ERR_WRONG_VALUE);
250216294Ssyrinx			break;
251216294Ssyrinx
252216294Ssyrinx		case LEAF_usmUserPrivKeyChange:
253216294Ssyrinx		case LEAF_usmUserOwnPrivKeyChange:
254216294Ssyrinx			if (val->var.subs[sub - 1] ==
255216294Ssyrinx			    LEAF_usmUserOwnPrivKeyChange &&
256216294Ssyrinx			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
257216294Ssyrinx			    usm_user->suser.sec_name) != 0))
258216294Ssyrinx				return (SNMP_ERR_NO_ACCESS);
259216294Ssyrinx			if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ)
260216294Ssyrinx				return (SNMP_ERR_INCONS_VALUE);
261216294Ssyrinx			ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ);
262216294Ssyrinx			if (ctx->scratch->ptr1 == NULL)
263216294Ssyrinx				return (SNMP_ERR_GENERR);
264216294Ssyrinx			memcpy(ctx->scratch->ptr1, uuser->suser.priv_key,
265216294Ssyrinx			    SNMP_PRIV_KEY_SIZ);
266216294Ssyrinx			memcpy(uuser->suser.priv_key, val->v.octetstring.octets,
267216294Ssyrinx			    val->v.octetstring.len);
268216294Ssyrinx			break;
269216294Ssyrinx
270216294Ssyrinx		case LEAF_usmUserPublic:
271216294Ssyrinx			if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ)
272216294Ssyrinx				return (SNMP_ERR_INCONS_VALUE);
273216294Ssyrinx			if (uuser->user_public_len > 0) {
274216294Ssyrinx				ctx->scratch->ptr2 =
275216294Ssyrinx				    malloc(uuser->user_public_len);
276216294Ssyrinx				if (ctx->scratch->ptr2 == NULL)
277216294Ssyrinx					return (SNMP_ERR_GENERR);
278216294Ssyrinx				memcpy(ctx->scratch->ptr2, uuser->user_public,
279216294Ssyrinx			 	   uuser->user_public_len);
280216294Ssyrinx				ctx->scratch->int2 = uuser->user_public_len;
281216294Ssyrinx			}
282216294Ssyrinx			if (val->v.octetstring.len > 0) {
283216294Ssyrinx				memcpy(uuser->user_public,
284216294Ssyrinx				    val->v.octetstring.octets,
285216294Ssyrinx				    val->v.octetstring.len);
286216294Ssyrinx				uuser->user_public_len = val->v.octetstring.len;
287216294Ssyrinx			} else {
288216294Ssyrinx				memset(uuser->user_public, 0,
289216294Ssyrinx				    SNMP_ADM_STR32_SIZ);
290216294Ssyrinx				uuser->user_public_len = 0;
291216294Ssyrinx			}
292216294Ssyrinx			break;
293216294Ssyrinx
294216294Ssyrinx		case LEAF_usmUserStorageType:
295216294Ssyrinx			return (SNMP_ERR_INCONS_VALUE);
296216294Ssyrinx
297216294Ssyrinx		case LEAF_usmUserStatus:
298216294Ssyrinx			if (uuser == NULL) {
299216294Ssyrinx				if (val->v.integer != RowStatus_createAndWait ||
300216294Ssyrinx				    usm_user_index_decode(&val->var, sub, eid,
301216294Ssyrinx				    &elen, uname) < 0)
302216294Ssyrinx					return (SNMP_ERR_INCONS_VALUE);
303216294Ssyrinx				uuser = usm_new_user(eid, elen, uname);
304216294Ssyrinx				if (uuser == NULL)
305216294Ssyrinx					return (SNMP_ERR_GENERR);
306216294Ssyrinx				uuser->status = RowStatus_notReady;
307216294Ssyrinx				if (community != COMM_INITIALIZE)
308216294Ssyrinx					uuser->type = StorageType_volatile;
309216294Ssyrinx				else
310216294Ssyrinx					uuser->type = StorageType_readOnly;
311216294Ssyrinx			} else if (val->v.integer != RowStatus_active &&
312216294Ssyrinx			    val->v.integer != RowStatus_destroy)
313216294Ssyrinx				return (SNMP_ERR_INCONS_VALUE);
314216294Ssyrinx
315216294Ssyrinx			uuser->status = val->v.integer;
316216294Ssyrinx			break;
317216294Ssyrinx		}
318216294Ssyrinx		return (SNMP_ERR_NOERROR);
319216294Ssyrinx
320216294Ssyrinx	case SNMP_OP_COMMIT:
321216294Ssyrinx		switch (val->var.subs[sub - 1]) {
322216294Ssyrinx		case LEAF_usmUserAuthKeyChange:
323216294Ssyrinx		case LEAF_usmUserOwnAuthKeyChange:
324216294Ssyrinx		case LEAF_usmUserPrivKeyChange:
325216294Ssyrinx		case LEAF_usmUserOwnPrivKeyChange:
326216294Ssyrinx			free(ctx->scratch->ptr1);
327216294Ssyrinx			break;
328216294Ssyrinx		case LEAF_usmUserPublic:
329216294Ssyrinx			if (ctx->scratch->ptr2 != NULL)
330216294Ssyrinx				free(ctx->scratch->ptr2);
331216294Ssyrinx			break;
332216294Ssyrinx		case LEAF_usmUserStatus:
333216294Ssyrinx			if (val->v.integer != RowStatus_destroy)
334216294Ssyrinx				break;
335216294Ssyrinx			if ((uuser = usm_get_user(&val->var, sub)) == NULL)
336216294Ssyrinx				return (SNMP_ERR_GENERR);
337216294Ssyrinx			usm_delete_user(uuser);
338216294Ssyrinx			break;
339216294Ssyrinx		default:
340216294Ssyrinx			break;
341216294Ssyrinx		}
342216294Ssyrinx		return (SNMP_ERR_NOERROR);
343216294Ssyrinx
344216294Ssyrinx	case SNMP_OP_ROLLBACK:
345216294Ssyrinx		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
346216294Ssyrinx			return (SNMP_ERR_GENERR);
347216294Ssyrinx		switch (val->var.subs[sub - 1]) {
348216294Ssyrinx		case LEAF_usmUserAuthProtocol:
349216294Ssyrinx			uuser->suser.auth_proto = ctx->scratch->int1;
350216294Ssyrinx			break;
351216294Ssyrinx		case LEAF_usmUserAuthKeyChange:
352216294Ssyrinx		case LEAF_usmUserOwnAuthKeyChange:
353216294Ssyrinx			memcpy(uuser->suser.auth_key, ctx->scratch->ptr1,
354216294Ssyrinx			    SNMP_AUTH_KEY_SIZ);
355216294Ssyrinx			free(ctx->scratch->ptr1);
356216294Ssyrinx			break;
357216294Ssyrinx		case LEAF_usmUserPrivProtocol:
358216294Ssyrinx			uuser->suser.priv_proto = ctx->scratch->int1;
359216294Ssyrinx			break;
360216294Ssyrinx		case LEAF_usmUserPrivKeyChange:
361216294Ssyrinx		case LEAF_usmUserOwnPrivKeyChange:
362216294Ssyrinx			memcpy(uuser->suser.priv_key, ctx->scratch->ptr1,
363216294Ssyrinx			    SNMP_AUTH_KEY_SIZ);
364216294Ssyrinx			free(ctx->scratch->ptr1);
365216294Ssyrinx			break;
366216294Ssyrinx		case LEAF_usmUserPublic:
367216294Ssyrinx			if (ctx->scratch->ptr2 != NULL) {
368216294Ssyrinx				memcpy(uuser->user_public, ctx->scratch->ptr2,
369216294Ssyrinx			 	   ctx->scratch->int2);
370216294Ssyrinx				uuser->user_public_len = ctx->scratch->int2;
371216294Ssyrinx				free(ctx->scratch->ptr2);
372216294Ssyrinx			} else {
373216294Ssyrinx				memset(uuser->user_public, 0,
374216294Ssyrinx				    SNMP_ADM_STR32_SIZ);
375216294Ssyrinx				uuser->user_public_len = 0;
376216294Ssyrinx			}
377216294Ssyrinx			break;
378216294Ssyrinx		case LEAF_usmUserCloneFrom:
379216294Ssyrinx		case LEAF_usmUserStatus:
380216294Ssyrinx			if (ctx->scratch->int1 == RowStatus_createAndWait)
381216294Ssyrinx				usm_delete_user(uuser);
382216294Ssyrinx			break;
383216294Ssyrinx		default:
384216294Ssyrinx			break;
385216294Ssyrinx		}
386216294Ssyrinx		return (SNMP_ERR_NOERROR);
387216294Ssyrinx
388216294Ssyrinx	default:
389216294Ssyrinx		abort();
390216294Ssyrinx	}
391216294Ssyrinx
392216294Ssyrinx	switch (val->var.subs[sub - 1]) {
393216294Ssyrinx	case LEAF_usmUserSecurityName:
394216294Ssyrinx		return (string_get(val, uuser->suser.sec_name, -1));
395216294Ssyrinx	case LEAF_usmUserCloneFrom:
396216294Ssyrinx		memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero));
397216294Ssyrinx		break;
398216294Ssyrinx	case LEAF_usmUserAuthProtocol:
399216294Ssyrinx		switch (uuser->suser.auth_proto) {
400216294Ssyrinx		case SNMP_AUTH_HMAC_MD5:
401216294Ssyrinx			memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol,
402216294Ssyrinx			    sizeof(oid_usmHMACMD5AuthProtocol));
403216294Ssyrinx			break;
404216294Ssyrinx		case SNMP_AUTH_HMAC_SHA:
405216294Ssyrinx			memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol,
406216294Ssyrinx			    sizeof(oid_usmHMACSHAAuthProtocol));
407216294Ssyrinx			break;
408216294Ssyrinx		default:
409216294Ssyrinx			memcpy(&val->v.oid, &oid_usmNoAuthProtocol,
410216294Ssyrinx			    sizeof(oid_usmNoAuthProtocol));
411216294Ssyrinx			break;
412216294Ssyrinx		}
413216294Ssyrinx		break;
414216294Ssyrinx	case LEAF_usmUserAuthKeyChange:
415216294Ssyrinx	case LEAF_usmUserOwnAuthKeyChange:
416216294Ssyrinx		return (string_get(val, (char *)uuser->suser.auth_key, 0));
417216294Ssyrinx	case LEAF_usmUserPrivProtocol:
418216294Ssyrinx		switch (uuser->suser.priv_proto) {
419216294Ssyrinx		case SNMP_PRIV_DES:
420216294Ssyrinx			memcpy(&val->v.oid, &oid_usmDESPrivProtocol,
421216294Ssyrinx			    sizeof(oid_usmDESPrivProtocol));
422216294Ssyrinx			break;
423216294Ssyrinx		case SNMP_PRIV_AES:
424216294Ssyrinx			memcpy(&val->v.oid, &oid_usmAesCfb128Protocol,
425216294Ssyrinx			    sizeof(oid_usmAesCfb128Protocol));
426216294Ssyrinx			break;
427216294Ssyrinx		default:
428216294Ssyrinx			memcpy(&val->v.oid, &oid_usmNoPrivProtocol,
429216294Ssyrinx			    sizeof(oid_usmNoPrivProtocol));
430216294Ssyrinx			break;
431216294Ssyrinx		}
432216294Ssyrinx		break;
433216294Ssyrinx	case LEAF_usmUserPrivKeyChange:
434216294Ssyrinx	case LEAF_usmUserOwnPrivKeyChange:
435216294Ssyrinx		return (string_get(val, (char *)uuser->suser.priv_key, 0));
436216294Ssyrinx	case LEAF_usmUserPublic:
437216294Ssyrinx		return (string_get(val, uuser->user_public,
438216294Ssyrinx		    uuser->user_public_len));
439216294Ssyrinx	case LEAF_usmUserStorageType:
440216294Ssyrinx		val->v.integer = uuser->type;
441216294Ssyrinx		break;
442216294Ssyrinx	case LEAF_usmUserStatus:
443216294Ssyrinx		val->v.integer = uuser->status;
444216294Ssyrinx		break;
445216294Ssyrinx	}
446216294Ssyrinx
447216294Ssyrinx	return (SNMP_ERR_NOERROR);
448216294Ssyrinx}
449216294Ssyrinx
450216294Ssyrinxstatic int
451216294Ssyrinxusm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine,
452216294Ssyrinx    uint32_t *elen, char *uname)
453216294Ssyrinx{
454216294Ssyrinx	uint32_t i, nlen;
455216294Ssyrinx	int uname_off;
456216294Ssyrinx
457216294Ssyrinx	if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ)
458216294Ssyrinx		return (-1);
459216294Ssyrinx
460216294Ssyrinx	for (i = 0; i < oid->subs[sub]; i++)
461216294Ssyrinx		engine[i] = oid->subs[sub + i + 1];
462216294Ssyrinx	*elen = i;
463216294Ssyrinx
464216294Ssyrinx	uname_off = sub + oid->subs[sub] + 1;
465216294Ssyrinx	if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ)
466216294Ssyrinx		return (-1);
467216294Ssyrinx
468216294Ssyrinx	for (i = 0; i < nlen; i++)
469216294Ssyrinx		uname[i] = oid->subs[uname_off + i + 1];
470216294Ssyrinx	uname[nlen] = '\0';
471216294Ssyrinx
472216294Ssyrinx	return (0);
473216294Ssyrinx}
474216294Ssyrinx
475216294Ssyrinxstatic void
476216294Ssyrinxusm_append_userindex(struct asn_oid *oid, uint sub,
477216294Ssyrinx    const struct usm_user *uuser)
478216294Ssyrinx{
479216294Ssyrinx	uint32_t i;
480216294Ssyrinx
481216294Ssyrinx	oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name);
482216294Ssyrinx	oid->len += 2;
483216294Ssyrinx	oid->subs[sub] = uuser->user_engine_len;
484216294Ssyrinx	for (i = 1; i < uuser->user_engine_len + 1; i++)
485216294Ssyrinx		oid->subs[sub + i] = uuser->user_engine_id[i - 1];
486216294Ssyrinx
487216294Ssyrinx	sub += uuser->user_engine_len + 1;
488216294Ssyrinx	oid->subs[sub] = strlen(uuser->suser.sec_name);
489216294Ssyrinx	for (i = 1; i <= oid->subs[sub]; i++)
490216294Ssyrinx		oid->subs[sub + i] = uuser->suser.sec_name[i - 1];
491216294Ssyrinx}
492216294Ssyrinx
493216294Ssyrinxstatic struct usm_user *
494216294Ssyrinxusm_get_user(const struct asn_oid *oid, uint sub)
495216294Ssyrinx{
496216294Ssyrinx	uint32_t enginelen;
497216294Ssyrinx	char username[SNMP_ADM_STR32_SIZ];
498216294Ssyrinx	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
499216294Ssyrinx
500216294Ssyrinx	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
501216294Ssyrinx		return (NULL);
502216294Ssyrinx
503216294Ssyrinx	return (usm_find_user(engineid, enginelen, username));
504216294Ssyrinx}
505216294Ssyrinx
506216294Ssyrinxstatic struct usm_user *
507216294Ssyrinxusm_get_next_user(const struct asn_oid *oid, uint sub)
508216294Ssyrinx{
509216294Ssyrinx	uint32_t enginelen;
510216294Ssyrinx	char username[SNMP_ADM_STR32_SIZ];
511216294Ssyrinx	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
512216294Ssyrinx	struct usm_user *uuser;
513216294Ssyrinx
514216294Ssyrinx	if (oid->len - sub == 0)
515216294Ssyrinx		return (usm_first_user());
516216294Ssyrinx
517216294Ssyrinx	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
518216294Ssyrinx		return (NULL);
519216294Ssyrinx
520216294Ssyrinx	if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL)
521216294Ssyrinx		return (usm_next_user(uuser));
522216294Ssyrinx
523216294Ssyrinx	return (NULL);
524216294Ssyrinx}
525216294Ssyrinx
526216294Ssyrinx/*
527216294Ssyrinx * USM snmp module initialization hook.
528216294Ssyrinx * Returns 0 on success, < 0 on error.
529216294Ssyrinx */
530216294Ssyrinxstatic int
531216294Ssyrinxusm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
532216294Ssyrinx{
533216294Ssyrinx	usm_module = mod;
534216294Ssyrinx	usm_lock = random();
535216294Ssyrinx	bsnmpd_reset_usm_stats();
536216294Ssyrinx	return (0);
537216294Ssyrinx}
538216294Ssyrinx
539216294Ssyrinx/*
540216294Ssyrinx * USM snmp module finalization hook.
541216294Ssyrinx */
542216294Ssyrinxstatic int
543216294Ssyrinxusm_fini(void)
544216294Ssyrinx{
545216294Ssyrinx	usm_flush_users();
546216294Ssyrinx	or_unregister(reg_usm);
547216294Ssyrinx
548216294Ssyrinx	return (0);
549216294Ssyrinx}
550216294Ssyrinx
551216294Ssyrinx/*
552216294Ssyrinx * USM snmp module start operation.
553216294Ssyrinx */
554216294Ssyrinxstatic void
555216294Ssyrinxusm_start(void)
556216294Ssyrinx{
557216294Ssyrinx	reg_usm = or_register(&oid_usm,
558216294Ssyrinx	    "The MIB module for managing SNMP User-Based Security Model.",
559216294Ssyrinx	    usm_module);
560216294Ssyrinx}
561216294Ssyrinx
562216294Ssyrinxstatic void
563216294Ssyrinxusm_dump(void)
564216294Ssyrinx{
565216294Ssyrinx	struct usm_user *uuser;
566216294Ssyrinx	struct snmpd_usmstat *usmstats;
567216294Ssyrinx	const char *const authstr[] = {
568216294Ssyrinx		"noauth",
569216294Ssyrinx		"md5",
570216294Ssyrinx		"sha",
571216294Ssyrinx		NULL
572216294Ssyrinx	};
573216294Ssyrinx	const char *const privstr[] = {
574216294Ssyrinx		"nopriv",
575216294Ssyrinx		"des",
576216294Ssyrinx		"aes",
577216294Ssyrinx		NULL
578216294Ssyrinx	};
579216294Ssyrinx
580216294Ssyrinx	if ((usmstats = bsnmpd_get_usm_stats()) != NULL) {
581216294Ssyrinx		syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u",
582216294Ssyrinx		    usmstats->unsupported_seclevels);
583216294Ssyrinx		syslog(LOG_ERR, "NotInTimeWindows\t\t%u",
584216294Ssyrinx		    usmstats->not_in_time_windows);
585216294Ssyrinx		syslog(LOG_ERR, "UnknownUserNames\t\t%u",
586216294Ssyrinx		    usmstats->unknown_users);
587216294Ssyrinx		syslog(LOG_ERR, "UnknownEngineIDs\t\t%u",
588216294Ssyrinx		    usmstats->unknown_engine_ids);
589216294Ssyrinx		syslog(LOG_ERR, "WrongDigests\t\t%u",
590216294Ssyrinx		    usmstats->wrong_digests);
591216294Ssyrinx		syslog(LOG_ERR, "DecryptionErrors\t\t%u",
592216294Ssyrinx		    usmstats->decrypt_errors);
593216294Ssyrinx	}
594216294Ssyrinx
595216294Ssyrinx	syslog(LOG_ERR, "USM users");
596216294Ssyrinx	for (uuser = usm_first_user(); uuser != NULL;
597216294Ssyrinx	    (uuser = usm_next_user(uuser)))
598216294Ssyrinx		syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name,
599216294Ssyrinx		    authstr[uuser->suser.auth_proto],
600216294Ssyrinx		    privstr[uuser->suser.priv_proto]);
601216294Ssyrinx}
602216294Ssyrinx
603216294Ssyrinxconst char usm_comment[] = \
604216294Ssyrinx"This module implements SNMP User-based Security Model defined in RFC 3414.";
605216294Ssyrinx
606216294Ssyrinxconst struct snmp_module config = {
607216294Ssyrinx	.comment =	usm_comment,
608216294Ssyrinx	.init =		usm_init,
609216294Ssyrinx	.fini =		usm_fini,
610216294Ssyrinx	.start =	usm_start,
611216294Ssyrinx	.tree =		usm_ctree,
612216294Ssyrinx	.dump =		usm_dump,
613216294Ssyrinx	.tree_size =	usm_CTREE_SIZE,
614216294Ssyrinx};
615