xref: /freebsd/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 4530e0c3e78d0616367d37273d6c1f47f627839b)
1135f7de5SShteryana Shopova /*-
2*0bf56da3SHartmut Brandt  * Copyright (c) 2010,2018 The FreeBSD Foundation
3135f7de5SShteryana Shopova  *
4135f7de5SShteryana Shopova  * This software was developed by Shteryana Sotirova Shopova under
5135f7de5SShteryana Shopova  * sponsorship from the FreeBSD Foundation.
6135f7de5SShteryana Shopova  *
7135f7de5SShteryana Shopova  * Redistribution and use in source and binary forms, with or without
8135f7de5SShteryana Shopova  * modification, are permitted provided that the following conditions
9135f7de5SShteryana Shopova  * are met:
10135f7de5SShteryana Shopova  * 1. Redistributions of source code must retain the above copyright
11135f7de5SShteryana Shopova  *    notice, this list of conditions and the following disclaimer.
12135f7de5SShteryana Shopova  * 2. Redistributions in binary form must reproduce the above copyright
13135f7de5SShteryana Shopova  *    notice, this list of conditions and the following disclaimer in the
14135f7de5SShteryana Shopova  *    documentation and/or other materials provided with the distribution.
15135f7de5SShteryana Shopova  *
16135f7de5SShteryana Shopova  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17135f7de5SShteryana Shopova  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18135f7de5SShteryana Shopova  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19135f7de5SShteryana Shopova  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20135f7de5SShteryana Shopova  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21135f7de5SShteryana Shopova  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22135f7de5SShteryana Shopova  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23135f7de5SShteryana Shopova  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24135f7de5SShteryana Shopova  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25135f7de5SShteryana Shopova  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26135f7de5SShteryana Shopova  * SUCH DAMAGE.
27135f7de5SShteryana Shopova  *
28135f7de5SShteryana Shopova  * $FreeBSD$
29135f7de5SShteryana Shopova  */
30135f7de5SShteryana Shopova #include <sys/queue.h>
31135f7de5SShteryana Shopova #include <sys/types.h>
32135f7de5SShteryana Shopova 
33135f7de5SShteryana Shopova #include <errno.h>
34135f7de5SShteryana Shopova #include <stdarg.h>
35135f7de5SShteryana Shopova #include <stdlib.h>
36135f7de5SShteryana Shopova #include <stdio.h>
37135f7de5SShteryana Shopova #include <stdint.h>
38135f7de5SShteryana Shopova #include <string.h>
39135f7de5SShteryana Shopova #include <syslog.h>
40135f7de5SShteryana Shopova 
41135f7de5SShteryana Shopova #include "asn1.h"
42135f7de5SShteryana Shopova #include "snmp.h"
43135f7de5SShteryana Shopova #include "snmpmod.h"
44135f7de5SShteryana Shopova 
458e9b3e70SHartmut Brandt #define	SNMPTREE_TYPES
46135f7de5SShteryana Shopova #include "usm_tree.h"
47135f7de5SShteryana Shopova #include "usm_oid.h"
48135f7de5SShteryana Shopova 
49135f7de5SShteryana Shopova static struct lmodule *usm_module;
50135f7de5SShteryana Shopova /* For the registration. */
51135f7de5SShteryana Shopova static const struct asn_oid oid_usm = OIDX_snmpUsmMIB;
52135f7de5SShteryana Shopova 
53135f7de5SShteryana Shopova static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol;
54135f7de5SShteryana Shopova static const struct asn_oid oid_usmHMACMD5AuthProtocol =		\
55135f7de5SShteryana Shopova     OIDX_usmHMACMD5AuthProtocol;
56135f7de5SShteryana Shopova static const struct asn_oid oid_usmHMACSHAAuthProtocol =		\
57135f7de5SShteryana Shopova     OIDX_usmHMACSHAAuthProtocol;
58135f7de5SShteryana Shopova 
59135f7de5SShteryana Shopova static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol;
60135f7de5SShteryana Shopova static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol;
61135f7de5SShteryana Shopova static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol;
62135f7de5SShteryana Shopova 
63135f7de5SShteryana Shopova static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName;
64135f7de5SShteryana Shopova 
65135f7de5SShteryana Shopova /* The registration. */
66135f7de5SShteryana Shopova static uint reg_usm;
67135f7de5SShteryana Shopova 
68135f7de5SShteryana Shopova static int32_t usm_lock;
69135f7de5SShteryana Shopova 
70135f7de5SShteryana Shopova static struct usm_user *	usm_get_user(const struct asn_oid *, uint);
71135f7de5SShteryana Shopova static struct usm_user *	usm_get_next_user(const struct asn_oid *, uint);
72135f7de5SShteryana Shopova static void	usm_append_userindex(struct asn_oid *, uint,
73135f7de5SShteryana Shopova     const struct usm_user *);
74135f7de5SShteryana Shopova static int	usm_user_index_decode(const struct asn_oid *, uint, uint8_t *,
75135f7de5SShteryana Shopova     uint32_t *, char *);
76135f7de5SShteryana Shopova 
77135f7de5SShteryana Shopova int
op_usm_stats(struct snmp_context * ctx __unused,struct snmp_value * val,uint32_t sub __unused,uint32_t iidx __unused,enum snmp_op op)78135f7de5SShteryana Shopova op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
79135f7de5SShteryana Shopova     uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op)
80135f7de5SShteryana Shopova {
81135f7de5SShteryana Shopova 	struct snmpd_usmstat *usmstats;
82135f7de5SShteryana Shopova 
83135f7de5SShteryana Shopova 	if (op == SNMP_OP_SET)
84135f7de5SShteryana Shopova 		return (SNMP_ERR_NOT_WRITEABLE);
85135f7de5SShteryana Shopova 
86135f7de5SShteryana Shopova 	if ((usmstats = bsnmpd_get_usm_stats()) == NULL)
87135f7de5SShteryana Shopova 		return (SNMP_ERR_GENERR);
88135f7de5SShteryana Shopova 
89135f7de5SShteryana Shopova 	if (op == SNMP_OP_GET) {
90135f7de5SShteryana Shopova 		switch (val->var.subs[sub - 1]) {
91135f7de5SShteryana Shopova 		case LEAF_usmStatsUnsupportedSecLevels:
92135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->unsupported_seclevels;
93135f7de5SShteryana Shopova 			break;
94135f7de5SShteryana Shopova 		case LEAF_usmStatsNotInTimeWindows:
95135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->not_in_time_windows;
96135f7de5SShteryana Shopova 			break;
97135f7de5SShteryana Shopova 		case LEAF_usmStatsUnknownUserNames:
98135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->unknown_users;
99135f7de5SShteryana Shopova 			break;
100135f7de5SShteryana Shopova 		case LEAF_usmStatsUnknownEngineIDs:
101135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->unknown_engine_ids;
102135f7de5SShteryana Shopova 			break;
103135f7de5SShteryana Shopova 		case LEAF_usmStatsWrongDigests:
104135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->wrong_digests;
105135f7de5SShteryana Shopova 			break;
106135f7de5SShteryana Shopova 		case LEAF_usmStatsDecryptionErrors:
107135f7de5SShteryana Shopova 			val->v.uint32 = usmstats->decrypt_errors;
108135f7de5SShteryana Shopova 			break;
109135f7de5SShteryana Shopova 		default:
110135f7de5SShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
111135f7de5SShteryana Shopova 		}
112135f7de5SShteryana Shopova 		return (SNMP_ERR_NOERROR);
113135f7de5SShteryana Shopova 	}
114135f7de5SShteryana Shopova 	abort();
115135f7de5SShteryana Shopova }
116135f7de5SShteryana Shopova 
117135f7de5SShteryana Shopova int
op_usm_lock(struct snmp_context * ctx __unused,struct snmp_value * val,uint32_t sub,uint32_t iidx __unused,enum snmp_op op)118135f7de5SShteryana Shopova op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
119135f7de5SShteryana Shopova     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
120135f7de5SShteryana Shopova {
121135f7de5SShteryana Shopova 	if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock)
122135f7de5SShteryana Shopova 		return (SNMP_ERR_NOSUCHNAME);
123135f7de5SShteryana Shopova 
124135f7de5SShteryana Shopova 	switch (op) {
125135f7de5SShteryana Shopova 	case SNMP_OP_GET:
126135f7de5SShteryana Shopova 		if (++usm_lock == INT32_MAX)
127135f7de5SShteryana Shopova 			usm_lock = 0;
128135f7de5SShteryana Shopova 		val->v.integer = usm_lock;
129135f7de5SShteryana Shopova 		break;
130135f7de5SShteryana Shopova 	case SNMP_OP_GETNEXT:
131135f7de5SShteryana Shopova 		abort();
132135f7de5SShteryana Shopova 	case SNMP_OP_SET:
133135f7de5SShteryana Shopova 		if (val->v.integer != usm_lock)
134135f7de5SShteryana Shopova 			return (SNMP_ERR_INCONS_VALUE);
135135f7de5SShteryana Shopova 		break;
136135f7de5SShteryana Shopova 	case SNMP_OP_ROLLBACK:
137135f7de5SShteryana Shopova 		/* FALLTHROUGH */
138135f7de5SShteryana Shopova 	case SNMP_OP_COMMIT:
139135f7de5SShteryana Shopova 		break;
140135f7de5SShteryana Shopova 	}
141135f7de5SShteryana Shopova 
142135f7de5SShteryana Shopova 	return (SNMP_ERR_NOERROR);
143135f7de5SShteryana Shopova }
144135f7de5SShteryana Shopova 
145135f7de5SShteryana Shopova int
op_usm_users(struct snmp_context * ctx,struct snmp_value * val,uint32_t sub,uint32_t iidx __unused,enum snmp_op op)146135f7de5SShteryana Shopova op_usm_users(struct snmp_context *ctx, struct snmp_value *val,
147135f7de5SShteryana Shopova     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
148135f7de5SShteryana Shopova {
149135f7de5SShteryana Shopova 	uint32_t elen;
150135f7de5SShteryana Shopova 	struct usm_user *uuser, *clone;
151135f7de5SShteryana Shopova 	char uname[SNMP_ADM_STR32_SIZ];
152135f7de5SShteryana Shopova 	uint8_t eid[SNMP_ENGINE_ID_SIZ];
153135f7de5SShteryana Shopova 
154135f7de5SShteryana Shopova 	switch (op) {
155135f7de5SShteryana Shopova 	case SNMP_OP_GET:
156135f7de5SShteryana Shopova 		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
157135f7de5SShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
158135f7de5SShteryana Shopova 		break;
159135f7de5SShteryana Shopova 
160135f7de5SShteryana Shopova 	case SNMP_OP_GETNEXT:
161135f7de5SShteryana Shopova 		if ((uuser = usm_get_next_user(&val->var, sub)) == NULL)
162135f7de5SShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
163135f7de5SShteryana Shopova 		usm_append_userindex(&val->var, sub, uuser);
164135f7de5SShteryana Shopova 		break;
165135f7de5SShteryana Shopova 
166135f7de5SShteryana Shopova 	case SNMP_OP_SET:
167135f7de5SShteryana Shopova 		if ((uuser = usm_get_user(&val->var, sub)) == NULL &&
168135f7de5SShteryana Shopova 		    val->var.subs[sub - 1] != LEAF_usmUserStatus &&
169135f7de5SShteryana Shopova 		    val->var.subs[sub - 1] != LEAF_usmUserCloneFrom)
170135f7de5SShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
171135f7de5SShteryana Shopova 
17260c2226fSEnji Cooper 		/*
17360c2226fSEnji Cooper 		 * XXX (ngie): need to investigate the MIB to determine how
17460c2226fSEnji Cooper 		 * this is possible given some of the transitions below.
17560c2226fSEnji Cooper 		 */
176135f7de5SShteryana Shopova 		if (community != COMM_INITIALIZE &&
17760c2226fSEnji Cooper 		    uuser != NULL && uuser->type == StorageType_readOnly)
178135f7de5SShteryana Shopova 			return (SNMP_ERR_NOT_WRITEABLE);
179135f7de5SShteryana Shopova 
180135f7de5SShteryana Shopova 		switch (val->var.subs[sub - 1]) {
181135f7de5SShteryana Shopova 		case LEAF_usmUserSecurityName:
182135f7de5SShteryana Shopova 			return (SNMP_ERR_NOT_WRITEABLE);
183135f7de5SShteryana Shopova 
184135f7de5SShteryana Shopova 		case LEAF_usmUserCloneFrom:
185135f7de5SShteryana Shopova 			if (uuser != NULL || usm_user_index_decode(&val->var,
186135f7de5SShteryana Shopova 			    sub, eid, &elen, uname) < 0 ||
187135f7de5SShteryana Shopova 			    !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid)))
188135f7de5SShteryana Shopova 				return (SNMP_ERR_WRONG_VALUE);
189135f7de5SShteryana Shopova 			if ((clone = usm_get_user(&val->v.oid, sub)) == NULL)
190135f7de5SShteryana Shopova 				return (SNMP_ERR_INCONS_VALUE);
191135f7de5SShteryana Shopova 			if ((uuser = usm_new_user(eid, elen, uname)) == NULL)
192135f7de5SShteryana Shopova 				return (SNMP_ERR_GENERR);
193135f7de5SShteryana Shopova 			uuser->status = RowStatus_notReady;
194135f7de5SShteryana Shopova 			if (community != COMM_INITIALIZE)
195135f7de5SShteryana Shopova 				uuser->type = StorageType_volatile;
196135f7de5SShteryana Shopova 			else
197135f7de5SShteryana Shopova 				uuser->type = StorageType_readOnly;
198135f7de5SShteryana Shopova 
199135f7de5SShteryana Shopova 			uuser->suser.auth_proto = clone->suser.auth_proto;
200135f7de5SShteryana Shopova 			uuser->suser.priv_proto = clone->suser.priv_proto;
201135f7de5SShteryana Shopova 			memcpy(uuser->suser.auth_key, clone->suser.auth_key,
202135f7de5SShteryana Shopova 			    sizeof(uuser->suser.auth_key));
203135f7de5SShteryana Shopova 			memcpy(uuser->suser.priv_key, clone->suser.priv_key,
204135f7de5SShteryana Shopova 			    sizeof(uuser->suser.priv_key));
205135f7de5SShteryana Shopova 			ctx->scratch->int1 = RowStatus_createAndWait;
206135f7de5SShteryana Shopova 			break;
207135f7de5SShteryana Shopova 
208135f7de5SShteryana Shopova 		case LEAF_usmUserAuthProtocol:
209135f7de5SShteryana Shopova 			ctx->scratch->int1 = uuser->suser.auth_proto;
210135f7de5SShteryana Shopova 			if (asn_compare_oid(&oid_usmNoAuthProtocol,
211135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
212135f7de5SShteryana Shopova 				uuser->suser.auth_proto = SNMP_AUTH_NOAUTH;
213135f7de5SShteryana Shopova 			else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol,
214135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
215135f7de5SShteryana Shopova 				uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5;
216135f7de5SShteryana Shopova 			else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol,
217135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
218135f7de5SShteryana Shopova 				uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA;
219135f7de5SShteryana Shopova 			else
220135f7de5SShteryana Shopova 				return (SNMP_ERR_WRONG_VALUE);
221135f7de5SShteryana Shopova 			break;
222135f7de5SShteryana Shopova 
223135f7de5SShteryana Shopova 		case LEAF_usmUserAuthKeyChange:
224135f7de5SShteryana Shopova 		case LEAF_usmUserOwnAuthKeyChange:
225135f7de5SShteryana Shopova 			if (val->var.subs[sub - 1] ==
226135f7de5SShteryana Shopova 			    LEAF_usmUserOwnAuthKeyChange &&
227135f7de5SShteryana Shopova 			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
228135f7de5SShteryana Shopova 			    usm_user->suser.sec_name) != 0))
229135f7de5SShteryana Shopova 				return (SNMP_ERR_NO_ACCESS);
230135f7de5SShteryana Shopova 			if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ)
231135f7de5SShteryana Shopova 				return (SNMP_ERR_INCONS_VALUE);
232135f7de5SShteryana Shopova 			ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ);
233135f7de5SShteryana Shopova 			if (ctx->scratch->ptr1 == NULL)
234135f7de5SShteryana Shopova 				return (SNMP_ERR_GENERR);
235135f7de5SShteryana Shopova 			memcpy(ctx->scratch->ptr1, uuser->suser.auth_key,
236135f7de5SShteryana Shopova 			    SNMP_AUTH_KEY_SIZ);
237135f7de5SShteryana Shopova 			memcpy(uuser->suser.auth_key, val->v.octetstring.octets,
238135f7de5SShteryana Shopova 			    val->v.octetstring.len);
239135f7de5SShteryana Shopova 			break;
240135f7de5SShteryana Shopova 
241135f7de5SShteryana Shopova 		case LEAF_usmUserPrivProtocol:
242135f7de5SShteryana Shopova 			ctx->scratch->int1 = uuser->suser.priv_proto;
243135f7de5SShteryana Shopova 			if (asn_compare_oid(&oid_usmNoPrivProtocol,
244135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
245135f7de5SShteryana Shopova 				uuser->suser.priv_proto = SNMP_PRIV_NOPRIV;
246135f7de5SShteryana Shopova 			else if (asn_compare_oid(&oid_usmDESPrivProtocol,
247135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
248135f7de5SShteryana Shopova 				uuser->suser.priv_proto = SNMP_PRIV_DES;
249135f7de5SShteryana Shopova 			else if (asn_compare_oid(&oid_usmAesCfb128Protocol,
250135f7de5SShteryana Shopova 			    &val->v.oid) == 0)
251135f7de5SShteryana Shopova 				uuser->suser.priv_proto = SNMP_PRIV_AES;
252135f7de5SShteryana Shopova 			else
253135f7de5SShteryana Shopova 				return (SNMP_ERR_WRONG_VALUE);
254135f7de5SShteryana Shopova 			break;
255135f7de5SShteryana Shopova 
256135f7de5SShteryana Shopova 		case LEAF_usmUserPrivKeyChange:
257135f7de5SShteryana Shopova 		case LEAF_usmUserOwnPrivKeyChange:
258135f7de5SShteryana Shopova 			if (val->var.subs[sub - 1] ==
259135f7de5SShteryana Shopova 			    LEAF_usmUserOwnPrivKeyChange &&
260135f7de5SShteryana Shopova 			    (usm_user == NULL || strcmp(uuser->suser.sec_name,
261135f7de5SShteryana Shopova 			    usm_user->suser.sec_name) != 0))
262135f7de5SShteryana Shopova 				return (SNMP_ERR_NO_ACCESS);
263135f7de5SShteryana Shopova 			if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ)
264135f7de5SShteryana Shopova 				return (SNMP_ERR_INCONS_VALUE);
265135f7de5SShteryana Shopova 			ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ);
266135f7de5SShteryana Shopova 			if (ctx->scratch->ptr1 == NULL)
267135f7de5SShteryana Shopova 				return (SNMP_ERR_GENERR);
268135f7de5SShteryana Shopova 			memcpy(ctx->scratch->ptr1, uuser->suser.priv_key,
269a7ff0456SEnji Cooper 			    sizeof(uuser->suser.priv_key));
270135f7de5SShteryana Shopova 			memcpy(uuser->suser.priv_key, val->v.octetstring.octets,
271135f7de5SShteryana Shopova 			    val->v.octetstring.len);
272135f7de5SShteryana Shopova 			break;
273135f7de5SShteryana Shopova 
274135f7de5SShteryana Shopova 		case LEAF_usmUserPublic:
275135f7de5SShteryana Shopova 			if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ)
276135f7de5SShteryana Shopova 				return (SNMP_ERR_INCONS_VALUE);
277135f7de5SShteryana Shopova 			if (uuser->user_public_len > 0) {
278135f7de5SShteryana Shopova 				ctx->scratch->ptr2 =
279135f7de5SShteryana Shopova 				    malloc(uuser->user_public_len);
280135f7de5SShteryana Shopova 				if (ctx->scratch->ptr2 == NULL)
281135f7de5SShteryana Shopova 					return (SNMP_ERR_GENERR);
282135f7de5SShteryana Shopova 				memcpy(ctx->scratch->ptr2, uuser->user_public,
283135f7de5SShteryana Shopova 			 	   uuser->user_public_len);
284135f7de5SShteryana Shopova 				ctx->scratch->int2 = uuser->user_public_len;
285135f7de5SShteryana Shopova 			}
286135f7de5SShteryana Shopova 			if (val->v.octetstring.len > 0) {
287135f7de5SShteryana Shopova 				memcpy(uuser->user_public,
288135f7de5SShteryana Shopova 				    val->v.octetstring.octets,
289135f7de5SShteryana Shopova 				    val->v.octetstring.len);
290135f7de5SShteryana Shopova 				uuser->user_public_len = val->v.octetstring.len;
291135f7de5SShteryana Shopova 			} else {
292135f7de5SShteryana Shopova 				memset(uuser->user_public, 0,
293a7ff0456SEnji Cooper 				    sizeof(uuser->user_public));
294135f7de5SShteryana Shopova 				uuser->user_public_len = 0;
295135f7de5SShteryana Shopova 			}
296135f7de5SShteryana Shopova 			break;
297135f7de5SShteryana Shopova 
298135f7de5SShteryana Shopova 		case LEAF_usmUserStorageType:
299135f7de5SShteryana Shopova 			return (SNMP_ERR_INCONS_VALUE);
300135f7de5SShteryana Shopova 
301135f7de5SShteryana Shopova 		case LEAF_usmUserStatus:
302135f7de5SShteryana Shopova 			if (uuser == NULL) {
303135f7de5SShteryana Shopova 				if (val->v.integer != RowStatus_createAndWait ||
304135f7de5SShteryana Shopova 				    usm_user_index_decode(&val->var, sub, eid,
305135f7de5SShteryana Shopova 				    &elen, uname) < 0)
306135f7de5SShteryana Shopova 					return (SNMP_ERR_INCONS_VALUE);
307135f7de5SShteryana Shopova 				uuser = usm_new_user(eid, elen, uname);
308135f7de5SShteryana Shopova 				if (uuser == NULL)
309135f7de5SShteryana Shopova 					return (SNMP_ERR_GENERR);
310135f7de5SShteryana Shopova 				uuser->status = RowStatus_notReady;
311135f7de5SShteryana Shopova 				if (community != COMM_INITIALIZE)
312135f7de5SShteryana Shopova 					uuser->type = StorageType_volatile;
313135f7de5SShteryana Shopova 				else
314135f7de5SShteryana Shopova 					uuser->type = StorageType_readOnly;
315135f7de5SShteryana Shopova 			} else if (val->v.integer != RowStatus_active &&
316135f7de5SShteryana Shopova 			    val->v.integer != RowStatus_destroy)
317135f7de5SShteryana Shopova 				return (SNMP_ERR_INCONS_VALUE);
318135f7de5SShteryana Shopova 
319135f7de5SShteryana Shopova 			uuser->status = val->v.integer;
320135f7de5SShteryana Shopova 			break;
321135f7de5SShteryana Shopova 		}
322135f7de5SShteryana Shopova 		return (SNMP_ERR_NOERROR);
323135f7de5SShteryana Shopova 
324135f7de5SShteryana Shopova 	case SNMP_OP_COMMIT:
325135f7de5SShteryana Shopova 		switch (val->var.subs[sub - 1]) {
326135f7de5SShteryana Shopova 		case LEAF_usmUserAuthKeyChange:
327135f7de5SShteryana Shopova 		case LEAF_usmUserOwnAuthKeyChange:
328135f7de5SShteryana Shopova 		case LEAF_usmUserPrivKeyChange:
329135f7de5SShteryana Shopova 		case LEAF_usmUserOwnPrivKeyChange:
330135f7de5SShteryana Shopova 			free(ctx->scratch->ptr1);
331135f7de5SShteryana Shopova 			break;
332135f7de5SShteryana Shopova 		case LEAF_usmUserPublic:
333135f7de5SShteryana Shopova 			if (ctx->scratch->ptr2 != NULL)
334135f7de5SShteryana Shopova 				free(ctx->scratch->ptr2);
335135f7de5SShteryana Shopova 			break;
336135f7de5SShteryana Shopova 		case LEAF_usmUserStatus:
337135f7de5SShteryana Shopova 			if (val->v.integer != RowStatus_destroy)
338135f7de5SShteryana Shopova 				break;
339135f7de5SShteryana Shopova 			if ((uuser = usm_get_user(&val->var, sub)) == NULL)
340135f7de5SShteryana Shopova 				return (SNMP_ERR_GENERR);
341135f7de5SShteryana Shopova 			usm_delete_user(uuser);
342135f7de5SShteryana Shopova 			break;
343135f7de5SShteryana Shopova 		default:
344135f7de5SShteryana Shopova 			break;
345135f7de5SShteryana Shopova 		}
346135f7de5SShteryana Shopova 		return (SNMP_ERR_NOERROR);
347135f7de5SShteryana Shopova 
348135f7de5SShteryana Shopova 	case SNMP_OP_ROLLBACK:
349135f7de5SShteryana Shopova 		if ((uuser = usm_get_user(&val->var, sub)) == NULL)
350135f7de5SShteryana Shopova 			return (SNMP_ERR_GENERR);
351135f7de5SShteryana Shopova 		switch (val->var.subs[sub - 1]) {
352135f7de5SShteryana Shopova 		case LEAF_usmUserAuthProtocol:
353135f7de5SShteryana Shopova 			uuser->suser.auth_proto = ctx->scratch->int1;
354135f7de5SShteryana Shopova 			break;
355135f7de5SShteryana Shopova 		case LEAF_usmUserAuthKeyChange:
356135f7de5SShteryana Shopova 		case LEAF_usmUserOwnAuthKeyChange:
357135f7de5SShteryana Shopova 			memcpy(uuser->suser.auth_key, ctx->scratch->ptr1,
358a7ff0456SEnji Cooper 			    sizeof(uuser->suser.auth_key));
359135f7de5SShteryana Shopova 			free(ctx->scratch->ptr1);
360135f7de5SShteryana Shopova 			break;
361135f7de5SShteryana Shopova 		case LEAF_usmUserPrivProtocol:
362135f7de5SShteryana Shopova 			uuser->suser.priv_proto = ctx->scratch->int1;
363135f7de5SShteryana Shopova 			break;
364135f7de5SShteryana Shopova 		case LEAF_usmUserPrivKeyChange:
365135f7de5SShteryana Shopova 		case LEAF_usmUserOwnPrivKeyChange:
366135f7de5SShteryana Shopova 			memcpy(uuser->suser.priv_key, ctx->scratch->ptr1,
367a7ff0456SEnji Cooper 			    sizeof(uuser->suser.priv_key));
368135f7de5SShteryana Shopova 			free(ctx->scratch->ptr1);
369135f7de5SShteryana Shopova 			break;
370135f7de5SShteryana Shopova 		case LEAF_usmUserPublic:
371135f7de5SShteryana Shopova 			if (ctx->scratch->ptr2 != NULL) {
372135f7de5SShteryana Shopova 				memcpy(uuser->user_public, ctx->scratch->ptr2,
373135f7de5SShteryana Shopova 			 	   ctx->scratch->int2);
374135f7de5SShteryana Shopova 				uuser->user_public_len = ctx->scratch->int2;
375135f7de5SShteryana Shopova 				free(ctx->scratch->ptr2);
376135f7de5SShteryana Shopova 			} else {
377135f7de5SShteryana Shopova 				memset(uuser->user_public, 0,
378a7ff0456SEnji Cooper 				    sizeof(uuser->user_public));
379135f7de5SShteryana Shopova 				uuser->user_public_len = 0;
380135f7de5SShteryana Shopova 			}
381135f7de5SShteryana Shopova 			break;
382135f7de5SShteryana Shopova 		case LEAF_usmUserCloneFrom:
383135f7de5SShteryana Shopova 		case LEAF_usmUserStatus:
384135f7de5SShteryana Shopova 			if (ctx->scratch->int1 == RowStatus_createAndWait)
385135f7de5SShteryana Shopova 				usm_delete_user(uuser);
386135f7de5SShteryana Shopova 			break;
387135f7de5SShteryana Shopova 		default:
388135f7de5SShteryana Shopova 			break;
389135f7de5SShteryana Shopova 		}
390135f7de5SShteryana Shopova 		return (SNMP_ERR_NOERROR);
391135f7de5SShteryana Shopova 
392135f7de5SShteryana Shopova 	default:
393135f7de5SShteryana Shopova 		abort();
394135f7de5SShteryana Shopova 	}
395135f7de5SShteryana Shopova 
396135f7de5SShteryana Shopova 	switch (val->var.subs[sub - 1]) {
397135f7de5SShteryana Shopova 	case LEAF_usmUserSecurityName:
398135f7de5SShteryana Shopova 		return (string_get(val, uuser->suser.sec_name, -1));
399135f7de5SShteryana Shopova 	case LEAF_usmUserCloneFrom:
400135f7de5SShteryana Shopova 		memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero));
401135f7de5SShteryana Shopova 		break;
402135f7de5SShteryana Shopova 	case LEAF_usmUserAuthProtocol:
403135f7de5SShteryana Shopova 		switch (uuser->suser.auth_proto) {
404135f7de5SShteryana Shopova 		case SNMP_AUTH_HMAC_MD5:
405135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol,
406135f7de5SShteryana Shopova 			    sizeof(oid_usmHMACMD5AuthProtocol));
407135f7de5SShteryana Shopova 			break;
408135f7de5SShteryana Shopova 		case SNMP_AUTH_HMAC_SHA:
409135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol,
410135f7de5SShteryana Shopova 			    sizeof(oid_usmHMACSHAAuthProtocol));
411135f7de5SShteryana Shopova 			break;
412135f7de5SShteryana Shopova 		default:
413135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmNoAuthProtocol,
414135f7de5SShteryana Shopova 			    sizeof(oid_usmNoAuthProtocol));
415135f7de5SShteryana Shopova 			break;
416135f7de5SShteryana Shopova 		}
417135f7de5SShteryana Shopova 		break;
418135f7de5SShteryana Shopova 	case LEAF_usmUserAuthKeyChange:
419135f7de5SShteryana Shopova 	case LEAF_usmUserOwnAuthKeyChange:
420135f7de5SShteryana Shopova 		return (string_get(val, (char *)uuser->suser.auth_key, 0));
421135f7de5SShteryana Shopova 	case LEAF_usmUserPrivProtocol:
422135f7de5SShteryana Shopova 		switch (uuser->suser.priv_proto) {
423135f7de5SShteryana Shopova 		case SNMP_PRIV_DES:
424135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmDESPrivProtocol,
425135f7de5SShteryana Shopova 			    sizeof(oid_usmDESPrivProtocol));
426135f7de5SShteryana Shopova 			break;
427135f7de5SShteryana Shopova 		case SNMP_PRIV_AES:
428135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmAesCfb128Protocol,
429135f7de5SShteryana Shopova 			    sizeof(oid_usmAesCfb128Protocol));
430135f7de5SShteryana Shopova 			break;
431135f7de5SShteryana Shopova 		default:
432135f7de5SShteryana Shopova 			memcpy(&val->v.oid, &oid_usmNoPrivProtocol,
433135f7de5SShteryana Shopova 			    sizeof(oid_usmNoPrivProtocol));
434135f7de5SShteryana Shopova 			break;
435135f7de5SShteryana Shopova 		}
436135f7de5SShteryana Shopova 		break;
437135f7de5SShteryana Shopova 	case LEAF_usmUserPrivKeyChange:
438135f7de5SShteryana Shopova 	case LEAF_usmUserOwnPrivKeyChange:
439135f7de5SShteryana Shopova 		return (string_get(val, (char *)uuser->suser.priv_key, 0));
440135f7de5SShteryana Shopova 	case LEAF_usmUserPublic:
441135f7de5SShteryana Shopova 		return (string_get(val, uuser->user_public,
442135f7de5SShteryana Shopova 		    uuser->user_public_len));
443135f7de5SShteryana Shopova 	case LEAF_usmUserStorageType:
444135f7de5SShteryana Shopova 		val->v.integer = uuser->type;
445135f7de5SShteryana Shopova 		break;
446135f7de5SShteryana Shopova 	case LEAF_usmUserStatus:
447135f7de5SShteryana Shopova 		val->v.integer = uuser->status;
448135f7de5SShteryana Shopova 		break;
449135f7de5SShteryana Shopova 	}
450135f7de5SShteryana Shopova 
451135f7de5SShteryana Shopova 	return (SNMP_ERR_NOERROR);
452135f7de5SShteryana Shopova }
453135f7de5SShteryana Shopova 
454135f7de5SShteryana Shopova static int
usm_user_index_decode(const struct asn_oid * oid,uint sub,uint8_t * engine,uint32_t * elen,char * uname)455135f7de5SShteryana Shopova usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine,
456135f7de5SShteryana Shopova     uint32_t *elen, char *uname)
457135f7de5SShteryana Shopova {
458135f7de5SShteryana Shopova 	uint32_t i, nlen;
459135f7de5SShteryana Shopova 	int uname_off;
460135f7de5SShteryana Shopova 
461135f7de5SShteryana Shopova 	if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ)
462135f7de5SShteryana Shopova 		return (-1);
463135f7de5SShteryana Shopova 
464135f7de5SShteryana Shopova 	for (i = 0; i < oid->subs[sub]; i++)
465135f7de5SShteryana Shopova 		engine[i] = oid->subs[sub + i + 1];
466135f7de5SShteryana Shopova 	*elen = i;
467135f7de5SShteryana Shopova 
468135f7de5SShteryana Shopova 	uname_off = sub + oid->subs[sub] + 1;
469135f7de5SShteryana Shopova 	if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ)
470135f7de5SShteryana Shopova 		return (-1);
471135f7de5SShteryana Shopova 
472135f7de5SShteryana Shopova 	for (i = 0; i < nlen; i++)
473135f7de5SShteryana Shopova 		uname[i] = oid->subs[uname_off + i + 1];
474135f7de5SShteryana Shopova 	uname[nlen] = '\0';
475135f7de5SShteryana Shopova 
476135f7de5SShteryana Shopova 	return (0);
477135f7de5SShteryana Shopova }
478135f7de5SShteryana Shopova 
479135f7de5SShteryana Shopova static void
usm_append_userindex(struct asn_oid * oid,uint sub,const struct usm_user * uuser)480135f7de5SShteryana Shopova usm_append_userindex(struct asn_oid *oid, uint sub,
481135f7de5SShteryana Shopova     const struct usm_user *uuser)
482135f7de5SShteryana Shopova {
483135f7de5SShteryana Shopova 	uint32_t i;
484135f7de5SShteryana Shopova 
485135f7de5SShteryana Shopova 	oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name);
486135f7de5SShteryana Shopova 	oid->len += 2;
487135f7de5SShteryana Shopova 	oid->subs[sub] = uuser->user_engine_len;
488135f7de5SShteryana Shopova 	for (i = 1; i < uuser->user_engine_len + 1; i++)
489135f7de5SShteryana Shopova 		oid->subs[sub + i] = uuser->user_engine_id[i - 1];
490135f7de5SShteryana Shopova 
491135f7de5SShteryana Shopova 	sub += uuser->user_engine_len + 1;
492135f7de5SShteryana Shopova 	oid->subs[sub] = strlen(uuser->suser.sec_name);
493135f7de5SShteryana Shopova 	for (i = 1; i <= oid->subs[sub]; i++)
494135f7de5SShteryana Shopova 		oid->subs[sub + i] = uuser->suser.sec_name[i - 1];
495135f7de5SShteryana Shopova }
496135f7de5SShteryana Shopova 
497135f7de5SShteryana Shopova static struct usm_user *
usm_get_user(const struct asn_oid * oid,uint sub)498135f7de5SShteryana Shopova usm_get_user(const struct asn_oid *oid, uint sub)
499135f7de5SShteryana Shopova {
500135f7de5SShteryana Shopova 	uint32_t enginelen;
501135f7de5SShteryana Shopova 	char username[SNMP_ADM_STR32_SIZ];
502135f7de5SShteryana Shopova 	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
503135f7de5SShteryana Shopova 
504135f7de5SShteryana Shopova 	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
505135f7de5SShteryana Shopova 		return (NULL);
506135f7de5SShteryana Shopova 
507135f7de5SShteryana Shopova 	return (usm_find_user(engineid, enginelen, username));
508135f7de5SShteryana Shopova }
509135f7de5SShteryana Shopova 
510135f7de5SShteryana Shopova static struct usm_user *
usm_get_next_user(const struct asn_oid * oid,uint sub)511135f7de5SShteryana Shopova usm_get_next_user(const struct asn_oid *oid, uint sub)
512135f7de5SShteryana Shopova {
513135f7de5SShteryana Shopova 	uint32_t enginelen;
514135f7de5SShteryana Shopova 	char username[SNMP_ADM_STR32_SIZ];
515135f7de5SShteryana Shopova 	uint8_t engineid[SNMP_ENGINE_ID_SIZ];
516135f7de5SShteryana Shopova 	struct usm_user *uuser;
517135f7de5SShteryana Shopova 
518135f7de5SShteryana Shopova 	if (oid->len - sub == 0)
519135f7de5SShteryana Shopova 		return (usm_first_user());
520135f7de5SShteryana Shopova 
521135f7de5SShteryana Shopova 	if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
522135f7de5SShteryana Shopova 		return (NULL);
523135f7de5SShteryana Shopova 
524135f7de5SShteryana Shopova 	if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL)
525135f7de5SShteryana Shopova 		return (usm_next_user(uuser));
526135f7de5SShteryana Shopova 
527135f7de5SShteryana Shopova 	return (NULL);
528135f7de5SShteryana Shopova }
529135f7de5SShteryana Shopova 
530135f7de5SShteryana Shopova /*
531135f7de5SShteryana Shopova  * USM snmp module initialization hook.
532135f7de5SShteryana Shopova  * Returns 0 on success, < 0 on error.
533135f7de5SShteryana Shopova  */
534135f7de5SShteryana Shopova static int
usm_init(struct lmodule * mod,int argc __unused,char * argv[]__unused)535135f7de5SShteryana Shopova usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
536135f7de5SShteryana Shopova {
537135f7de5SShteryana Shopova 	usm_module = mod;
538135f7de5SShteryana Shopova 	usm_lock = random();
539135f7de5SShteryana Shopova 	bsnmpd_reset_usm_stats();
540135f7de5SShteryana Shopova 	return (0);
541135f7de5SShteryana Shopova }
542135f7de5SShteryana Shopova 
543135f7de5SShteryana Shopova /*
544135f7de5SShteryana Shopova  * USM snmp module finalization hook.
545135f7de5SShteryana Shopova  */
546135f7de5SShteryana Shopova static int
usm_fini(void)547135f7de5SShteryana Shopova usm_fini(void)
548135f7de5SShteryana Shopova {
549135f7de5SShteryana Shopova 	usm_flush_users();
550135f7de5SShteryana Shopova 	or_unregister(reg_usm);
551135f7de5SShteryana Shopova 
552135f7de5SShteryana Shopova 	return (0);
553135f7de5SShteryana Shopova }
554135f7de5SShteryana Shopova 
555135f7de5SShteryana Shopova /*
556135f7de5SShteryana Shopova  * USM snmp module start operation.
557135f7de5SShteryana Shopova  */
558135f7de5SShteryana Shopova static void
usm_start(void)559135f7de5SShteryana Shopova usm_start(void)
560135f7de5SShteryana Shopova {
561135f7de5SShteryana Shopova 	reg_usm = or_register(&oid_usm,
562135f7de5SShteryana Shopova 	    "The MIB module for managing SNMP User-Based Security Model.",
563135f7de5SShteryana Shopova 	    usm_module);
564135f7de5SShteryana Shopova }
565135f7de5SShteryana Shopova 
566135f7de5SShteryana Shopova static void
usm_dump(void)567135f7de5SShteryana Shopova usm_dump(void)
568135f7de5SShteryana Shopova {
569135f7de5SShteryana Shopova 	struct usm_user *uuser;
570135f7de5SShteryana Shopova 	struct snmpd_usmstat *usmstats;
571135f7de5SShteryana Shopova 	const char *const authstr[] = {
572135f7de5SShteryana Shopova 		"noauth",
573135f7de5SShteryana Shopova 		"md5",
574135f7de5SShteryana Shopova 		"sha",
575135f7de5SShteryana Shopova 		NULL
576135f7de5SShteryana Shopova 	};
577135f7de5SShteryana Shopova 	const char *const privstr[] = {
578135f7de5SShteryana Shopova 		"nopriv",
579135f7de5SShteryana Shopova 		"des",
580135f7de5SShteryana Shopova 		"aes",
581135f7de5SShteryana Shopova 		NULL
582135f7de5SShteryana Shopova 	};
583135f7de5SShteryana Shopova 
584135f7de5SShteryana Shopova 	if ((usmstats = bsnmpd_get_usm_stats()) != NULL) {
585135f7de5SShteryana Shopova 		syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u",
586135f7de5SShteryana Shopova 		    usmstats->unsupported_seclevels);
587135f7de5SShteryana Shopova 		syslog(LOG_ERR, "NotInTimeWindows\t\t%u",
588135f7de5SShteryana Shopova 		    usmstats->not_in_time_windows);
589135f7de5SShteryana Shopova 		syslog(LOG_ERR, "UnknownUserNames\t\t%u",
590135f7de5SShteryana Shopova 		    usmstats->unknown_users);
591135f7de5SShteryana Shopova 		syslog(LOG_ERR, "UnknownEngineIDs\t\t%u",
592135f7de5SShteryana Shopova 		    usmstats->unknown_engine_ids);
593135f7de5SShteryana Shopova 		syslog(LOG_ERR, "WrongDigests\t\t%u",
594135f7de5SShteryana Shopova 		    usmstats->wrong_digests);
595135f7de5SShteryana Shopova 		syslog(LOG_ERR, "DecryptionErrors\t\t%u",
596135f7de5SShteryana Shopova 		    usmstats->decrypt_errors);
597135f7de5SShteryana Shopova 	}
598135f7de5SShteryana Shopova 
599135f7de5SShteryana Shopova 	syslog(LOG_ERR, "USM users");
600135f7de5SShteryana Shopova 	for (uuser = usm_first_user(); uuser != NULL;
601135f7de5SShteryana Shopova 	    (uuser = usm_next_user(uuser)))
602135f7de5SShteryana Shopova 		syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name,
603135f7de5SShteryana Shopova 		    authstr[uuser->suser.auth_proto],
604135f7de5SShteryana Shopova 		    privstr[uuser->suser.priv_proto]);
605135f7de5SShteryana Shopova }
606135f7de5SShteryana Shopova 
607*0bf56da3SHartmut Brandt static const char usm_comment[] =
608135f7de5SShteryana Shopova "This module implements SNMP User-based Security Model defined in RFC 3414.";
609135f7de5SShteryana Shopova 
6108e9b3e70SHartmut Brandt extern const struct snmp_module config;
611135f7de5SShteryana Shopova const struct snmp_module config = {
612135f7de5SShteryana Shopova 	.comment =	usm_comment,
613135f7de5SShteryana Shopova 	.init =		usm_init,
614135f7de5SShteryana Shopova 	.fini =		usm_fini,
615135f7de5SShteryana Shopova 	.start =	usm_start,
616135f7de5SShteryana Shopova 	.tree =		usm_ctree,
617135f7de5SShteryana Shopova 	.dump =		usm_dump,
618135f7de5SShteryana Shopova 	.tree_size =	usm_CTREE_SIZE,
619135f7de5SShteryana Shopova };
620