/src/net-snmp/snmplib/snmpusm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Portions of this file are subject to the following copyright(s). See |
2 | | * the Net-SNMP's COPYING file for more details and other copyrights |
3 | | * that may apply: |
4 | | */ |
5 | | /* |
6 | | * Portions of this file are copyrighted by: |
7 | | * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
8 | | * Use is subject to license terms specified in the COPYING file |
9 | | * distributed with the Net-SNMP package. |
10 | | * |
11 | | * Portions of this file are copyrighted by: |
12 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
13 | | * Use is subject to license terms specified in the COPYING file |
14 | | * distributed with the Net-SNMP package. |
15 | | */ |
16 | | /* |
17 | | * snmpusm.c |
18 | | * |
19 | | * Routines to manipulate a information about a "user" as |
20 | | * defined by the SNMP-USER-BASED-SM-MIB MIB. |
21 | | * |
22 | | * All functions usm_set_usmStateReference_*() return 0 on success, -1 |
23 | | * otherwise. |
24 | | * |
25 | | * !! Tab stops set to 4 in some parts of this file. !! |
26 | | * (Designated on a per function.) |
27 | | */ |
28 | | |
29 | | #include <net-snmp/net-snmp-config.h> |
30 | | #include <net-snmp/net-snmp-features.h> |
31 | | |
32 | | #include <sys/types.h> |
33 | | #include <stdio.h> |
34 | | #ifdef HAVE_STDLIB_H |
35 | | #include <stdlib.h> |
36 | | #endif |
37 | | #ifdef TIME_WITH_SYS_TIME |
38 | | # include <sys/time.h> |
39 | | # include <time.h> |
40 | | #else |
41 | | # ifdef HAVE_SYS_TIME_H |
42 | | # include <sys/time.h> |
43 | | # else |
44 | | # include <time.h> |
45 | | # endif |
46 | | #endif |
47 | | #ifdef HAVE_STRING_H |
48 | | #include <string.h> |
49 | | #else |
50 | | #include <strings.h> |
51 | | #endif |
52 | | #ifdef HAVE_NETINET_IN_H |
53 | | #include <netinet/in.h> |
54 | | #endif |
55 | | |
56 | | #ifdef HAVE_UNISTD_H |
57 | | #include <unistd.h> |
58 | | #endif |
59 | | |
60 | | #include <net-snmp/types.h> |
61 | | #include <net-snmp/output_api.h> |
62 | | #include <net-snmp/config_api.h> |
63 | | #include <net-snmp/utilities.h> |
64 | | |
65 | | #include <net-snmp/library/openssl_config.h> |
66 | | #include <net-snmp/library/asn1.h> |
67 | | #include <net-snmp/library/snmp_api.h> |
68 | | #include <net-snmp/library/callback.h> |
69 | | #include <net-snmp/library/tools.h> |
70 | | #include <net-snmp/library/keytools.h> |
71 | | #include <net-snmp/library/snmpv3.h> |
72 | | #include <net-snmp/library/lcd_time.h> |
73 | | #include <net-snmp/library/scapi.h> |
74 | | #include <net-snmp/library/callback.h> |
75 | | #include <net-snmp/library/snmp_secmod.h> |
76 | | #include <net-snmp/library/snmpusm.h> |
77 | | #include <net-snmp/library/transform_oids.h> |
78 | | #include <net-snmp/library/snmp_enum.h> |
79 | | |
80 | | #ifdef HAVE_OPENSSL_DH_H |
81 | | #include <openssl/dh.h> |
82 | | #endif |
83 | | |
84 | | netsnmp_feature_child_of(usm_all, libnetsnmp); |
85 | | netsnmp_feature_child_of(usm_support, usm_all); |
86 | | |
87 | | netsnmp_feature_require(usm_support); |
88 | | |
89 | | struct usmStateReference { |
90 | | int refcnt; |
91 | | char *usr_name; |
92 | | size_t usr_name_length; |
93 | | u_char *usr_engine_id; |
94 | | size_t usr_engine_id_length; |
95 | | oid *usr_auth_protocol; |
96 | | size_t usr_auth_protocol_length; |
97 | | u_char *usr_auth_key; |
98 | | size_t usr_auth_key_length; |
99 | | oid *usr_priv_protocol; |
100 | | size_t usr_priv_protocol_length; |
101 | | u_char *usr_priv_key; |
102 | | size_t usr_priv_key_length; |
103 | | u_int usr_sec_level; |
104 | | }; |
105 | | |
106 | | const oid usmNoAuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
107 | | NETSNMP_USMAUTH_NOAUTH }; |
108 | | #ifndef NETSNMP_DISABLE_MD5 |
109 | | const oid usmHMACMD5AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
110 | | NETSNMP_USMAUTH_HMACMD5 }; |
111 | | #endif |
112 | | const oid usmHMACSHA1AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
113 | | NETSNMP_USMAUTH_HMACSHA1 }; |
114 | | |
115 | | #ifdef HAVE_EVP_SHA384 |
116 | | const oid usmHMAC384SHA512AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
117 | | NETSNMP_USMAUTH_HMAC384SHA512 }; |
118 | | const oid usmHMAC256SHA384AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
119 | | NETSNMP_USMAUTH_HMAC256SHA384 }; |
120 | | #endif /* HAVE_EVP_SHA384 */ |
121 | | |
122 | | #ifdef HAVE_EVP_SHA224 |
123 | | const oid usmHMAC192SHA256AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
124 | | NETSNMP_USMAUTH_HMAC192SHA256 }; |
125 | | const oid usmHMAC128SHA224AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, |
126 | | NETSNMP_USMAUTH_HMAC128SHA224 }; |
127 | | #endif /* HAVE_EVP_SHA384 */ |
128 | | |
129 | | const oid usmNoPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 }; |
130 | | |
131 | | #ifndef NETSNMP_DISABLE_DES |
132 | | const oid usmDESPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 }; |
133 | | #endif |
134 | | |
135 | | |
136 | | const oid usmAESPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 }; |
137 | | /* backwards compat */ |
138 | | const oid *usmAES128PrivProtocol = usmAESPrivProtocol; |
139 | | |
140 | | #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 |
141 | | /* OIDs from http://www.snmp.com/eso/esoConsortiumMIB.txt */ |
142 | | const oid usmAES192PrivProtocol[9] = { 1,3,6,1,4,1,14832,1,3 }; |
143 | | const oid usmAES256PrivProtocol[9] = { 1,3,6,1,4,1,14832,1,4 }; |
144 | | /* OIDs from CISCO MIB */ |
145 | | const oid usmAES192CiscoPrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,1 }; |
146 | | const oid usmAES256CiscoPrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,2 }; |
147 | | /* |
148 | | * these OIDs are in pySNMP source as OIDs for AES+Reeder. We'll just |
149 | | * use OIDS from CISCO-SNMP-USM-OIDS-MIB |
150 | | * |
151 | | const oid usmAES192Cisco2PrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,101 }; |
152 | | const oid usmAES256Cisco2PrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,102 }; |
153 | | */ |
154 | | #endif /* NETSNMP_DRAFT_BLUMENTHAL_AES_04 */ |
155 | | |
156 | | typedef struct usm_alg_type_s { |
157 | | const char *label; |
158 | | int value; |
159 | | } usm_alg_type_t; |
160 | | |
161 | | static const usm_alg_type_t usm_auth_type[] = { |
162 | | { "NOAUTH", NETSNMP_USMAUTH_NOAUTH }, |
163 | | { "SHA", NETSNMP_USMAUTH_HMACSHA1 }, |
164 | | { "SHA-1", NETSNMP_USMAUTH_HMACSHA1 }, |
165 | | { "SHA1", NETSNMP_USMAUTH_HMACSHA1 }, |
166 | | #ifndef NETSNMP_DISABLE_MD5 |
167 | | { "MD5", NETSNMP_USMAUTH_HMACMD5 }, |
168 | | #endif |
169 | | #ifdef HAVE_EVP_SHA224 |
170 | | { "SHA-224", NETSNMP_USMAUTH_HMAC128SHA224 }, |
171 | | { "SHA224", NETSNMP_USMAUTH_HMAC128SHA224 }, |
172 | | { "SHA-256", NETSNMP_USMAUTH_HMAC192SHA256 }, |
173 | | { "SHA256", NETSNMP_USMAUTH_HMAC192SHA256 }, |
174 | | #endif |
175 | | #ifdef HAVE_EVP_SHA384 |
176 | | { "SHA-384", NETSNMP_USMAUTH_HMAC256SHA384 }, |
177 | | { "SHA384", NETSNMP_USMAUTH_HMAC256SHA384 }, |
178 | | { "SHA-512", NETSNMP_USMAUTH_HMAC384SHA512 }, |
179 | | { "SHA512", NETSNMP_USMAUTH_HMAC384SHA512 }, |
180 | | #endif |
181 | | { NULL, -1 } |
182 | | }; |
183 | | |
184 | | static const usm_alg_type_t usm_priv_type[] = { |
185 | | { "NOPRIV", USM_CREATE_USER_PRIV_NONE }, |
186 | | #ifndef NETSNMP_DISABLE_DES |
187 | | { "DES", USM_CREATE_USER_PRIV_DES }, |
188 | | #endif |
189 | | #ifdef HAVE_AES |
190 | | { "AES", USM_CREATE_USER_PRIV_AES }, |
191 | | { "AES-128", USM_CREATE_USER_PRIV_AES }, |
192 | | { "AES128", USM_CREATE_USER_PRIV_AES }, |
193 | | #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 |
194 | | { "AES-192", USM_CREATE_USER_PRIV_AES192 }, |
195 | | { "AES192", USM_CREATE_USER_PRIV_AES192 }, |
196 | | { "AES-256", USM_CREATE_USER_PRIV_AES256 }, |
197 | | { "AES256", USM_CREATE_USER_PRIV_AES256 }, |
198 | | /** cisco / pysnmp variations */ |
199 | | { "AES-192-C", USM_CREATE_USER_PRIV_AES192_CISCO }, |
200 | | { "AES192C", USM_CREATE_USER_PRIV_AES192_CISCO }, |
201 | | { "AES-256-C", USM_CREATE_USER_PRIV_AES256_CISCO }, |
202 | | { "AES256C", USM_CREATE_USER_PRIV_AES256_CISCO }, |
203 | | #endif |
204 | | #endif |
205 | | { NULL, -1 }, |
206 | | }; |
207 | | |
208 | | static u_int dummy_etime, dummy_eboot; /* For ISENGINEKNOWN(). */ |
209 | | |
210 | | /* |
211 | | * Set up default snmpv3 parameter value storage. |
212 | | */ |
213 | | #ifdef NETSNMP_SECMOD_USM |
214 | | static const oid *defaultAuthType = NULL; |
215 | | static size_t defaultAuthTypeLen = 0; |
216 | | static const oid *defaultPrivType = NULL; |
217 | | static size_t defaultPrivTypeLen = 0; |
218 | | #endif /* NETSNMP_SECMOD_USM */ |
219 | | |
220 | | /* |
221 | | * Globals. |
222 | | */ |
223 | | static u_int salt_integer; |
224 | | #ifdef HAVE_AES |
225 | | static u_int salt_integer64_1, salt_integer64_2; |
226 | | #endif |
227 | | /* |
228 | | * 1/2 of seed for the salt. Cf. RFC2274, Sect 8.1.1.1. |
229 | | */ |
230 | | |
231 | | static struct usmUser *noNameUser = NULL; |
232 | | /* |
233 | | * Local storage (LCD) of the default user list. |
234 | | */ |
235 | | static struct usmUser *userList = NULL; |
236 | | |
237 | | /* |
238 | | * Set a given field of the secStateRef. |
239 | | * |
240 | | * Allocate <len> bytes for type <type> pointed to by ref-><field>. |
241 | | * Then copy in <item> and record its length in ref-><field_len>. |
242 | | * |
243 | | * Return 0 on success, -1 otherwise. |
244 | | */ |
245 | 0 | #define MAKE_ENTRY(ref, type, item, len, field, field_len) \ |
246 | 0 | do { \ |
247 | 0 | if (ref == NULL) \ |
248 | 0 | return -1; \ |
249 | 0 | if (ref->field != NULL) { \ |
250 | 0 | SNMP_ZERO(ref->field, ref->field_len); \ |
251 | 0 | SNMP_FREE(ref->field); \ |
252 | 0 | } \ |
253 | 0 | ref->field_len = 0; \ |
254 | 0 | if (len == 0 || item == NULL) \ |
255 | 0 | return 0; \ |
256 | 0 | ref->field = netsnmp_memdup(item, len * sizeof(type)); \ |
257 | 0 | if (ref->field == NULL) \ |
258 | 0 | return -1; \ |
259 | 0 | \ |
260 | 0 | ref->field_len = len; \ |
261 | 0 | return 0; \ |
262 | 0 | } while (0) |
263 | | |
264 | | static int |
265 | | usm_clone_usmStateReference(struct usmStateReference *from, |
266 | | struct usmStateReference **to); |
267 | | |
268 | | static int |
269 | | free_enginetime_on_shutdown(int majorid, int minorid, void *serverarg, |
270 | | void *clientarg) |
271 | 2.62k | { |
272 | 2.62k | u_char engineID[SNMP_MAX_ENG_SIZE]; |
273 | 2.62k | size_t engineID_len = sizeof(engineID); |
274 | | |
275 | 2.62k | DEBUGMSGTL(("snmpv3", "free enginetime callback called\n")); |
276 | | |
277 | 2.62k | engineID_len = snmpv3_get_engineID(engineID, engineID_len); |
278 | 2.62k | if (engineID_len > 0) |
279 | 0 | free_enginetime(engineID, engineID_len); |
280 | 2.62k | return 0; |
281 | 2.62k | } |
282 | | |
283 | | static struct usmStateReference * |
284 | | usm_malloc_usmStateReference(void) |
285 | 0 | { |
286 | 0 | struct usmStateReference *retval; |
287 | |
|
288 | 0 | retval = calloc(1, sizeof(struct usmStateReference)); |
289 | 0 | if (retval) |
290 | 0 | retval->refcnt = 1; |
291 | |
|
292 | 0 | return retval; |
293 | 0 | } /* end usm_malloc_usmStateReference() */ |
294 | | |
295 | | static int |
296 | | usm_clone(netsnmp_pdu *pdu, netsnmp_pdu *new_pdu) |
297 | 0 | { |
298 | 0 | struct usmStateReference *ref = pdu->securityStateRef; |
299 | 0 | struct usmStateReference **new_ref = |
300 | 0 | (struct usmStateReference **)&new_pdu->securityStateRef; |
301 | 0 | int ret = 0; |
302 | |
|
303 | 0 | if (!ref) |
304 | 0 | return ret; |
305 | | |
306 | 0 | if (pdu->command == SNMP_MSG_TRAP2) { |
307 | 0 | netsnmp_assert(pdu->securityModel == SNMP_DEFAULT_SECMODEL); |
308 | 0 | ret = usm_clone_usmStateReference(ref, new_ref); |
309 | 0 | } else { |
310 | 0 | netsnmp_assert(ref == *new_ref); |
311 | 0 | ref->refcnt++; |
312 | 0 | } |
313 | |
|
314 | 0 | return ret; |
315 | 0 | } |
316 | | |
317 | | static void |
318 | | usm_free_usmStateReference(void *old) |
319 | 0 | { |
320 | 0 | struct usmStateReference *ref = old; |
321 | |
|
322 | 0 | if (!ref) |
323 | 0 | return; |
324 | | |
325 | 0 | if (--ref->refcnt > 0) |
326 | 0 | return; |
327 | | |
328 | 0 | SNMP_FREE(ref->usr_name); |
329 | 0 | SNMP_FREE(ref->usr_engine_id); |
330 | 0 | SNMP_FREE(ref->usr_auth_protocol); |
331 | 0 | SNMP_FREE(ref->usr_priv_protocol); |
332 | |
|
333 | 0 | if (ref->usr_auth_key_length && ref->usr_auth_key) { |
334 | 0 | SNMP_ZERO(ref->usr_auth_key, ref->usr_auth_key_length); |
335 | 0 | SNMP_FREE(ref->usr_auth_key); |
336 | 0 | } |
337 | 0 | if (ref->usr_priv_key_length && ref->usr_priv_key) { |
338 | 0 | SNMP_ZERO(ref->usr_priv_key, ref->usr_priv_key_length); |
339 | 0 | SNMP_FREE(ref->usr_priv_key); |
340 | 0 | } |
341 | |
|
342 | 0 | SNMP_FREE(ref); |
343 | 0 | } /* end usm_free_usmStateReference() */ |
344 | | |
345 | | struct usmUser * |
346 | | usm_get_userList(void) |
347 | 0 | { |
348 | 0 | return userList; |
349 | 0 | } |
350 | | |
351 | | static int |
352 | | usm_set_usmStateReference_name(struct usmStateReference *ref, |
353 | | char *name, size_t name_len) |
354 | 0 | { |
355 | 0 | MAKE_ENTRY(ref, char, name, name_len, usr_name, usr_name_length); |
356 | 0 | } |
357 | | |
358 | | static int |
359 | | usm_set_usmStateReference_engine_id(struct usmStateReference *ref, |
360 | | u_char * engine_id, |
361 | | size_t engine_id_len) |
362 | 0 | { |
363 | 0 | MAKE_ENTRY(ref, u_char, engine_id, engine_id_len, |
364 | 0 | usr_engine_id, usr_engine_id_length); |
365 | 0 | } |
366 | | |
367 | | static int |
368 | | usm_set_usmStateReference_auth_protocol(struct usmStateReference *ref, |
369 | | oid * auth_protocol, |
370 | | size_t auth_protocol_len) |
371 | 0 | { |
372 | 0 | MAKE_ENTRY(ref, oid, auth_protocol, auth_protocol_len, |
373 | 0 | usr_auth_protocol, usr_auth_protocol_length); |
374 | 0 | } |
375 | | |
376 | | static int |
377 | | usm_set_usmStateReference_auth_key(struct usmStateReference *ref, |
378 | | u_char * auth_key, size_t auth_key_len) |
379 | 0 | { |
380 | 0 | MAKE_ENTRY(ref, u_char, auth_key, auth_key_len, |
381 | 0 | usr_auth_key, usr_auth_key_length); |
382 | 0 | } |
383 | | |
384 | | static int |
385 | | usm_set_usmStateReference_priv_protocol(struct usmStateReference *ref, |
386 | | oid * priv_protocol, |
387 | | size_t priv_protocol_len) |
388 | 0 | { |
389 | 0 | MAKE_ENTRY(ref, oid, priv_protocol, priv_protocol_len, |
390 | 0 | usr_priv_protocol, usr_priv_protocol_length); |
391 | 0 | } |
392 | | |
393 | | static int |
394 | | usm_set_usmStateReference_priv_key(struct usmStateReference *ref, |
395 | | u_char * priv_key, size_t priv_key_len) |
396 | 0 | { |
397 | 0 | MAKE_ENTRY(ref, u_char, priv_key, priv_key_len, |
398 | 0 | usr_priv_key, usr_priv_key_length); |
399 | 0 | } |
400 | | |
401 | | static int |
402 | | usm_set_usmStateReference_sec_level(struct usmStateReference *ref, |
403 | | int sec_level) |
404 | 0 | { |
405 | 0 | if (ref == NULL) |
406 | 0 | return -1; |
407 | 0 | ref->usr_sec_level = sec_level; |
408 | 0 | return 0; |
409 | 0 | } |
410 | | |
411 | | static int |
412 | | usm_clone_usmStateReference(struct usmStateReference *from, |
413 | | struct usmStateReference **to) |
414 | 0 | { |
415 | 0 | struct usmStateReference *cloned_usmStateRef; |
416 | |
|
417 | 0 | if (from == NULL || to == NULL) |
418 | 0 | return -1; |
419 | | |
420 | 0 | *to = usm_malloc_usmStateReference(); |
421 | 0 | cloned_usmStateRef = *to; |
422 | |
|
423 | 0 | if (usm_set_usmStateReference_name(cloned_usmStateRef, from->usr_name, from->usr_name_length) || |
424 | 0 | usm_set_usmStateReference_engine_id(cloned_usmStateRef, from->usr_engine_id, from->usr_engine_id_length) || |
425 | 0 | usm_set_usmStateReference_auth_protocol(cloned_usmStateRef, from->usr_auth_protocol, from->usr_auth_protocol_length) || |
426 | 0 | usm_set_usmStateReference_auth_key(cloned_usmStateRef, from->usr_auth_key, from->usr_auth_key_length) || |
427 | 0 | usm_set_usmStateReference_priv_protocol(cloned_usmStateRef, from->usr_priv_protocol, from->usr_priv_protocol_length) || |
428 | 0 | usm_set_usmStateReference_priv_key(cloned_usmStateRef, from->usr_priv_key, from->usr_priv_key_length) || |
429 | 0 | usm_set_usmStateReference_sec_level(cloned_usmStateRef, from->usr_sec_level)) |
430 | 0 | { |
431 | 0 | usm_free_usmStateReference(*to); |
432 | 0 | *to = NULL; |
433 | 0 | return -1; |
434 | 0 | } |
435 | | |
436 | 0 | return 0; |
437 | |
|
438 | 0 | } |
439 | | |
440 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
441 | | /*******************************************************************-o-****** |
442 | | * emergency_print |
443 | | * |
444 | | * Parameters: |
445 | | * *field |
446 | | * length |
447 | | * |
448 | | * This is a print routine that is solely included so that it can be |
449 | | * used in gdb. Don't use it as a function, it will be pulled before |
450 | | * a real release of the code. |
451 | | * |
452 | | * tab stop 4 |
453 | | * |
454 | | * XXX fflush() only works on FreeBSD; core dumps on Sun OS's |
455 | | */ |
456 | | void |
457 | | emergency_print(u_char * field, u_int length) |
458 | | { |
459 | | int iindex; |
460 | | int start = 0; |
461 | | int stop = 25; |
462 | | |
463 | | while (start < stop) { |
464 | | for (iindex = start; iindex < stop; iindex++) |
465 | | printf("%02X ", field[iindex]); |
466 | | |
467 | | printf("\n"); |
468 | | start = stop; |
469 | | stop = stop + 25 < length ? stop + 25 : length; |
470 | | } |
471 | | fflush(0); |
472 | | |
473 | | } /* end emergency_print() */ |
474 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
475 | | |
476 | | static struct usmUser * |
477 | | usm_get_user_from_list(const u_char *engineID, size_t engineIDLen, |
478 | | const char *name, size_t nameLen, |
479 | | struct usmUser *puserList, int use_default) |
480 | 0 | { |
481 | 0 | struct usmUser *ptr; |
482 | |
|
483 | 0 | for (ptr = puserList; ptr != NULL; ptr = ptr->next) { |
484 | 0 | if (ptr->name && strlen(ptr->name) == nameLen && |
485 | 0 | memcmp(ptr->name, name, nameLen) == 0) { |
486 | 0 | DEBUGMSGTL(("usm", "match on user %s\n", ptr->name)); |
487 | 0 | if (ptr->engineIDLen == engineIDLen && |
488 | 0 | ((ptr->engineID == NULL && engineID == NULL) || |
489 | 0 | (ptr->engineID != NULL && engineID != NULL && |
490 | 0 | memcmp(ptr->engineID, engineID, engineIDLen) == 0))) |
491 | 0 | return ptr; |
492 | 0 | DEBUGMSGTL(("usm", "no match on engineID (")); |
493 | 0 | if (engineID) { |
494 | 0 | DEBUGMSGHEX(("usm", engineID, engineIDLen)); |
495 | 0 | } else { |
496 | 0 | DEBUGMSGTL(("usm", "Empty EngineID")); |
497 | 0 | } |
498 | 0 | DEBUGMSG(("usm", ")\n")); |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | /* |
503 | | * return "" user used to facilitate engineID discovery |
504 | | */ |
505 | 0 | if (use_default && !strcmp(name, "")) { |
506 | 0 | DEBUGMSGTL(("usm", "return noNameUser\n")); |
507 | 0 | return noNameUser; |
508 | 0 | } |
509 | 0 | return NULL; |
510 | 0 | } |
511 | | |
512 | | struct usmUser * |
513 | | usm_get_user2(const u_char *engineID, size_t engineIDLen, const void *name, |
514 | | size_t nameLen) |
515 | 0 | { |
516 | 0 | DEBUGMSGTL(("usm", "getting user %.*s\n", (int)nameLen, |
517 | 0 | (const char *)name)); |
518 | 0 | return usm_get_user_from_list(engineID, engineIDLen, name, nameLen, |
519 | 0 | userList, 1); |
520 | 0 | } |
521 | | |
522 | | /* |
523 | | * usm_get_user(): Returns a user from userList based on the engineID, |
524 | | * engineIDLen and name of the requested user. |
525 | | */ |
526 | | struct usmUser * |
527 | | usm_get_user(const u_char *engineID, size_t engineIDLen, const char *name) |
528 | 0 | { |
529 | 0 | return usm_get_user2(engineID, engineIDLen, name, strlen(name)); |
530 | 0 | } |
531 | | |
532 | | static struct usmUser * |
533 | | usm_add_user_to_list(struct usmUser *user, struct usmUser *puserList) |
534 | 0 | { |
535 | 0 | struct usmUser *nptr, *pptr, *optr; |
536 | | |
537 | | /* |
538 | | * loop through puserList till we find the proper, sorted place to |
539 | | * insert the new user |
540 | | */ |
541 | | /* XXX - how to handle a NULL user->name ?? */ |
542 | | /* XXX - similarly for a NULL nptr->name ?? */ |
543 | 0 | for (nptr = puserList, pptr = NULL; nptr != NULL; |
544 | 0 | pptr = nptr, nptr = nptr->next) { |
545 | 0 | if (nptr->engineIDLen > user->engineIDLen) |
546 | 0 | break; |
547 | | |
548 | 0 | if (user->engineID == NULL && nptr->engineID != NULL) |
549 | 0 | break; |
550 | | |
551 | 0 | if (nptr->engineIDLen == user->engineIDLen && |
552 | 0 | (nptr->engineID != NULL && user->engineID != NULL && |
553 | 0 | memcmp(nptr->engineID, user->engineID, |
554 | 0 | user->engineIDLen) > 0)) |
555 | 0 | break; |
556 | | |
557 | 0 | if (!(nptr->engineID == NULL && user->engineID != NULL)) { |
558 | 0 | if (nptr->engineIDLen == user->engineIDLen && |
559 | 0 | ((nptr->engineID == NULL && user->engineID == NULL) || |
560 | 0 | memcmp(nptr->engineID, user->engineID, |
561 | 0 | user->engineIDLen) == 0) |
562 | 0 | && strlen(nptr->name) > strlen(user->name)) |
563 | 0 | break; |
564 | | |
565 | 0 | if (nptr->engineIDLen == user->engineIDLen && |
566 | 0 | ((nptr->engineID == NULL && user->engineID == NULL) || |
567 | 0 | memcmp(nptr->engineID, user->engineID, |
568 | 0 | user->engineIDLen) == 0) |
569 | 0 | && strlen(nptr->name) == strlen(user->name) |
570 | 0 | && strcmp(nptr->name, user->name) > 0) |
571 | 0 | break; |
572 | | |
573 | 0 | if (nptr->engineIDLen == user->engineIDLen && |
574 | 0 | ((nptr->engineID == NULL && user->engineID == NULL) || |
575 | 0 | memcmp(nptr->engineID, user->engineID, |
576 | 0 | user->engineIDLen) == 0) |
577 | 0 | && strlen(nptr->name) == strlen(user->name) |
578 | 0 | && strcmp(nptr->name, user->name) == 0) { |
579 | | /* |
580 | | * the user is an exact match of a previous entry. |
581 | | * Credentials may be different, though, so remove |
582 | | * the old entry (and add the new one)! |
583 | | */ |
584 | 0 | if (pptr) { /* change prev's next pointer */ |
585 | 0 | pptr->next = nptr->next; |
586 | 0 | } |
587 | 0 | if (nptr->next) { /* change next's prev pointer */ |
588 | 0 | nptr->next->prev = pptr; |
589 | 0 | } |
590 | 0 | optr = nptr; |
591 | 0 | nptr = optr->next; /* add new user at this position */ |
592 | | /* free the old user */ |
593 | 0 | optr->next=NULL; |
594 | 0 | optr->prev=NULL; |
595 | 0 | usm_free_user(optr); |
596 | 0 | break; /* new user will be added below */ |
597 | 0 | } |
598 | 0 | } |
599 | 0 | } |
600 | | |
601 | | /* |
602 | | * nptr should now point to the user that we need to add ourselves |
603 | | * in front of, and pptr should be our new 'prev'. |
604 | | */ |
605 | | |
606 | | /* |
607 | | * change our pointers |
608 | | */ |
609 | 0 | user->prev = pptr; |
610 | 0 | user->next = nptr; |
611 | | |
612 | | /* |
613 | | * change the next's prev pointer |
614 | | */ |
615 | 0 | if (user->next) |
616 | 0 | user->next->prev = user; |
617 | | |
618 | | /* |
619 | | * change the prev's next pointer |
620 | | */ |
621 | 0 | if (user->prev) |
622 | 0 | user->prev->next = user; |
623 | | |
624 | | /* |
625 | | * rewind to the head of the list and return it (since the new head |
626 | | * could be us, we need to notify the above routine who the head now is. |
627 | | */ |
628 | 0 | for (pptr = user; pptr->prev != NULL; pptr = pptr->prev); |
629 | 0 | return pptr; |
630 | 0 | } |
631 | | |
632 | | /* |
633 | | * usm_add_user(): Add's a user to the userList, sorted by the |
634 | | * engineIDLength then the engineID then the name length then the name |
635 | | * to facilitate getNext calls on a usmUser table which is indexed by |
636 | | * these values. |
637 | | * |
638 | | * returns the head of the list (which could change due to this add). |
639 | | */ |
640 | | |
641 | | struct usmUser * |
642 | | usm_add_user(struct usmUser *user) |
643 | 0 | { |
644 | 0 | struct usmUser *uptr; |
645 | 0 | uptr = usm_add_user_to_list(user, userList); |
646 | 0 | if (uptr != NULL) |
647 | 0 | userList = uptr; |
648 | 0 | return uptr; |
649 | 0 | } |
650 | | |
651 | | /* |
652 | | * usm_remove_usmUser_from_list remove user from (optional) list |
653 | | * |
654 | | * if list is not specified, defaults to global userList. |
655 | | * |
656 | | * returns SNMPERR_SUCCESS or SNMPERR_USM_UNKNOWNSECURITYNAME |
657 | | */ |
658 | | static int |
659 | | usm_remove_usmUser_from_list(struct usmUser *user, struct usmUser **ppuserList) |
660 | 0 | { |
661 | 0 | struct usmUser *nptr, *pptr; |
662 | | |
663 | | /* |
664 | | * NULL pointers aren't allowed |
665 | | */ |
666 | 0 | if (ppuserList == NULL) |
667 | 0 | ppuserList = &userList; |
668 | |
|
669 | 0 | if (*ppuserList == NULL) |
670 | 0 | return SNMPERR_USM_UNKNOWNSECURITYNAME; |
671 | | |
672 | | /* |
673 | | * find the user in the list |
674 | | */ |
675 | 0 | for (nptr = *ppuserList, pptr = NULL; nptr != NULL; |
676 | 0 | pptr = nptr, nptr = nptr->next) { |
677 | 0 | if (nptr == user) |
678 | 0 | break; |
679 | 0 | } |
680 | |
|
681 | 0 | if (nptr) { |
682 | | /* |
683 | | * remove the user from the linked list |
684 | | */ |
685 | 0 | if (pptr) { |
686 | 0 | pptr->next = nptr->next; |
687 | 0 | } |
688 | 0 | if (nptr->next) { |
689 | 0 | nptr->next->prev = pptr; |
690 | 0 | } |
691 | 0 | } else { |
692 | | /* |
693 | | * user didn't exist |
694 | | */ |
695 | 0 | return SNMPERR_USM_UNKNOWNSECURITYNAME; |
696 | 0 | } |
697 | 0 | if (nptr == *ppuserList) /* we're the head of the list, need to change |
698 | | * * the head to the next user */ |
699 | 0 | *ppuserList = nptr->next; |
700 | 0 | return SNMPERR_SUCCESS; |
701 | 0 | } /* end usm_remove_usmUser_from_list() */ |
702 | | |
703 | | /* |
704 | | * usm_remove_user_from_list |
705 | | * |
706 | | * removes user from list. |
707 | | * |
708 | | * returns new list head on success, or NULL on error. |
709 | | * |
710 | | * NOTE: if there was only one user in the list, list head will be NULL. |
711 | | * So NULL can also mean success. Use the newer usm_remove_usmUser() for |
712 | | * more specific return codes. This function is kept for backwards |
713 | | * compatibility with this ambiguous behaviour. |
714 | | */ |
715 | | static struct usmUser * |
716 | | usm_remove_user_from_list(struct usmUser *user, |
717 | | struct usmUser **ppuserList) |
718 | 0 | { |
719 | 0 | int rc = usm_remove_usmUser_from_list(user, ppuserList); |
720 | 0 | if (rc != SNMPERR_SUCCESS || NULL == ppuserList) |
721 | 0 | return NULL; |
722 | | |
723 | 0 | return *ppuserList; |
724 | 0 | } /* end usm_remove_user_from_list() */ |
725 | | |
726 | | /* |
727 | | * usm_remove_user(): finds and removes a user from a list |
728 | | */ |
729 | | struct usmUser * |
730 | | usm_remove_user(struct usmUser *user) |
731 | 0 | { |
732 | 0 | return usm_remove_user_from_list(user, &userList); |
733 | 0 | } |
734 | | |
735 | | /* |
736 | | * usm_free_user(): calls free() on all needed parts of struct usmUser and |
737 | | * the user himself. |
738 | | * |
739 | | * Note: This should *not* be called on an object in a list (IE, |
740 | | * remove it from the list first, and set next and prev to NULL), but |
741 | | * will try to reconnect the list pieces again if it is called this |
742 | | * way. If called on the head of the list, the entire list will be |
743 | | * lost. |
744 | | */ |
745 | | struct usmUser * |
746 | | usm_free_user(struct usmUser *user) |
747 | 13.9k | { |
748 | 13.9k | if (user == NULL) |
749 | 11.3k | return NULL; |
750 | | |
751 | 2.62k | SNMP_FREE(user->engineID); |
752 | 2.62k | SNMP_FREE(user->name); |
753 | 2.62k | SNMP_FREE(user->secName); |
754 | 2.62k | SNMP_FREE(user->cloneFrom); |
755 | 2.62k | SNMP_FREE(user->userPublicString); |
756 | 2.62k | SNMP_FREE(user->authProtocol); |
757 | 2.62k | SNMP_FREE(user->privProtocol); |
758 | | |
759 | 2.62k | if (user->authKey != NULL) { |
760 | 0 | SNMP_ZERO(user->authKey, user->authKeyLen); |
761 | 0 | SNMP_FREE(user->authKey); |
762 | 0 | } |
763 | | |
764 | 2.62k | if (user->privKey != NULL) { |
765 | 0 | SNMP_ZERO(user->privKey, user->privKeyLen); |
766 | 0 | SNMP_FREE(user->privKey); |
767 | 0 | } |
768 | | |
769 | 2.62k | if (user->authKeyKu != NULL) { |
770 | 0 | SNMP_ZERO(user->authKeyKu, user->authKeyKuLen); |
771 | 0 | SNMP_FREE(user->authKeyKu); |
772 | 0 | } |
773 | | |
774 | 2.62k | if (user->privKeyKu != NULL) { |
775 | 0 | SNMP_ZERO(user->privKeyKu, user->privKeyKuLen); |
776 | 0 | SNMP_FREE(user->privKeyKu); |
777 | 0 | } |
778 | | |
779 | 2.62k | #ifdef NETSNMP_USE_OPENSSL |
780 | 2.62k | if (user->usmDHUserAuthKeyChange) |
781 | 0 | { |
782 | 0 | DH_free(user->usmDHUserAuthKeyChange); |
783 | 0 | user->usmDHUserAuthKeyChange = NULL; |
784 | 0 | } |
785 | | |
786 | 2.62k | if (user->usmDHUserPrivKeyChange) |
787 | 0 | { |
788 | 0 | DH_free(user->usmDHUserPrivKeyChange); |
789 | 0 | user->usmDHUserPrivKeyChange = NULL; |
790 | 0 | } |
791 | 2.62k | #endif |
792 | | |
793 | | /* |
794 | | * FIX Why not put this check *first?* |
795 | | */ |
796 | 2.62k | if (user->prev != NULL && user->prev != (struct usmUser *)-1) { /* ack, this shouldn't happen */ |
797 | 0 | user->prev->next = user->next; |
798 | 0 | } |
799 | 2.62k | if (user->next != NULL && user->next != (struct usmUser *)-1) { |
800 | 0 | user->next->prev = user->prev; |
801 | 0 | if (user->prev != NULL) /* ack this is really bad, because it means |
802 | | * * we'll loose the head of some structure tree */ |
803 | 0 | DEBUGMSGTL(("usm", |
804 | 0 | "Severe: Asked to free the head of a usmUser tree somewhere.")); |
805 | 0 | } |
806 | | |
807 | | |
808 | 2.62k | SNMP_ZERO(user, sizeof(*user)); |
809 | 2.62k | SNMP_FREE(user); |
810 | | |
811 | 2.62k | return NULL; /* for convenience to returns from calling functions */ |
812 | | |
813 | 13.9k | } /* end usm_free_user() */ |
814 | | |
815 | | int usm_set_priv_key(struct usmUser *user, const char *fname, |
816 | | u_char **old_key, size_t *old_key_len, |
817 | | const u_char *new_key, u_int new_key_len) |
818 | 0 | { |
819 | 0 | u_char buf[SNMP_MAXBUF_SMALL], buf2[SNMP_MAXBUF_SMALL]; |
820 | 0 | size_t buflen = sizeof(buf); |
821 | 0 | int plen, res; |
822 | |
|
823 | 0 | plen = sc_get_proper_priv_length(user->privProtocol, |
824 | 0 | user->privProtocolLen); |
825 | 0 | DEBUGMSGTL(("usmUser", "plen %d\n", plen)); |
826 | | /* |
827 | | * extend key as needed |
828 | | */ |
829 | 0 | DEBUGMSGTL(("9:usmUser", "%s: new_key_len %d\n", fname, new_key_len)); |
830 | 0 | if (new_key_len < 2 * plen) { |
831 | 0 | struct usmUser dummy; |
832 | |
|
833 | 0 | memset(&dummy, 0x0, sizeof(dummy)); |
834 | 0 | dummy.engineID = user->engineID; |
835 | 0 | dummy.engineIDLen = user->engineIDLen; |
836 | 0 | dummy.authProtocol = user->authProtocol; |
837 | 0 | dummy.authProtocolLen = user->authProtocolLen; |
838 | 0 | dummy.privProtocol = user->privProtocol; |
839 | 0 | dummy.privProtocolLen = user->privProtocolLen; |
840 | 0 | memcpy(buf2, new_key, new_key_len); |
841 | 0 | dummy.privKey = buf2; |
842 | 0 | dummy.privKeyLen = new_key_len; |
843 | 0 | res = usm_extend_user_kul(&dummy, sizeof(buf2)); |
844 | 0 | if (res != SNMP_ERR_NOERROR) { |
845 | 0 | DEBUGMSGTL(("usmUser", "%s: extend kul failed\n", fname)); |
846 | 0 | return SNMP_ERR_GENERR; |
847 | 0 | } |
848 | 0 | DEBUGMSGTL(("9:usmUser", "%s: extend kul OK\n", fname)); |
849 | 0 | new_key = dummy.privKey; |
850 | 0 | new_key_len = dummy.privKeyLen; |
851 | | /* |
852 | | * make sure no reallocation happened; buf2 must be large enough |
853 | | */ |
854 | 0 | netsnmp_assert(dummy.privKey == buf2); |
855 | 0 | } |
856 | | |
857 | | /* |
858 | | * Change the key. |
859 | | */ |
860 | 0 | DEBUGMSGTL(("usmUser", "%s: changing priv key for user %s\n", |
861 | 0 | fname, user->secName)); |
862 | |
|
863 | 0 | res = decode_keychange(user->authProtocol, user->authProtocolLen, |
864 | 0 | user->privKey, user->privKeyLen, new_key, |
865 | 0 | new_key_len, buf, &buflen); |
866 | 0 | if (res != SNMPERR_SUCCESS) { |
867 | 0 | DEBUGMSGTL(("usmUser", "%s failed\n", fname)); |
868 | 0 | return SNMP_ERR_GENERR; |
869 | 0 | } |
870 | 0 | DEBUGMSGTL(("usmUser", "%s succeeded\n", fname)); |
871 | 0 | *old_key = user->privKey; |
872 | 0 | *old_key_len = user->privKeyLen; |
873 | 0 | user->privKey = netsnmp_memdup(buf, buflen); |
874 | 0 | if (user->privKey == NULL) |
875 | 0 | return SNMP_ERR_RESOURCEUNAVAILABLE; |
876 | 0 | user->privKeyLen = buflen; |
877 | 0 | return SNMP_ERR_NOERROR; |
878 | 0 | } |
879 | | |
880 | | /*******************************************************************-o-****** |
881 | | * usm_generate_OID |
882 | | * |
883 | | * Parameters: |
884 | | * *prefix (I) OID prefix to the usmUser table entry. |
885 | | * prefixLen (I) |
886 | | * *uptr (I) Pointer to a user in the user list. |
887 | | * *length (O) Length of generated index OID. |
888 | | * |
889 | | * Returns: |
890 | | * Pointer to the OID index for the user (uptr) -OR- |
891 | | * NULL on failure. |
892 | | * |
893 | | * |
894 | | * Generate the index OID for a given usmUser name. 'length' is set to |
895 | | * the length of the index OID. |
896 | | * |
897 | | * Index OID format is: |
898 | | * |
899 | | * <...prefix>.<engineID_length>.<engineID>.<user_name_length>.<user_name> |
900 | | */ |
901 | | oid * |
902 | | usm_generate_OID(const oid *prefix, size_t prefixLen, |
903 | | const struct usmUser *uptr, size_t *length) |
904 | 0 | { |
905 | 0 | oid *indexOid; |
906 | 0 | int i; |
907 | |
|
908 | 0 | *length = 2 + uptr->engineIDLen + strlen(uptr->name) + prefixLen; |
909 | 0 | indexOid = malloc(*length * sizeof(oid)); |
910 | 0 | if (!indexOid) |
911 | 0 | return indexOid; |
912 | | |
913 | 0 | memmove(indexOid, prefix, prefixLen * sizeof(oid)); |
914 | |
|
915 | 0 | indexOid[prefixLen] = uptr->engineIDLen; |
916 | 0 | for (i = 0; i < uptr->engineIDLen; i++) |
917 | 0 | indexOid[prefixLen + 1 + i] = (oid) uptr->engineID[i]; |
918 | |
|
919 | 0 | indexOid[prefixLen + uptr->engineIDLen + 1] = strlen(uptr->name); |
920 | 0 | for (i = 0; i < strlen(uptr->name); i++) |
921 | 0 | indexOid[prefixLen + uptr->engineIDLen + 2 + i] = (oid) uptr->name[i]; |
922 | |
|
923 | 0 | return indexOid; |
924 | 0 | } /* end usm_generate_OID() */ |
925 | | |
926 | | /*******************************************************************-o-****** |
927 | | * asn_predict_int_length |
928 | | * |
929 | | * Parameters: |
930 | | * type (UNUSED) |
931 | | * number |
932 | | * len |
933 | | * |
934 | | * Returns: |
935 | | * Number of bytes necessary to store the ASN.1 encoded value of 'number'. |
936 | | * |
937 | | * |
938 | | * This gives the number of bytes that the ASN.1 encoder (in asn1.c) will |
939 | | * use to encode a particular integer value. |
940 | | * |
941 | | * Returns the length of the integer -- NOT THE HEADER! |
942 | | * |
943 | | * Do this the same way as asn_build_int()... |
944 | | */ |
945 | | static int |
946 | | asn_predict_int_length(int type, long number, size_t len) |
947 | 0 | { |
948 | 0 | register u_long mask; |
949 | | |
950 | |
|
951 | 0 | if (len != sizeof(long)) |
952 | 0 | return -1; |
953 | | |
954 | 0 | mask = ((u_long) 0x1FF) << ((8 * (sizeof(long) - 1)) - 1); |
955 | | /* |
956 | | * mask is 0xFF800000 on a big-endian machine |
957 | | */ |
958 | |
|
959 | 0 | while ((((number & mask) == 0) || ((number & mask) == mask)) |
960 | 0 | && len > 1) { |
961 | 0 | len--; |
962 | 0 | number <<= 8; |
963 | 0 | } |
964 | |
|
965 | 0 | return len; |
966 | |
|
967 | 0 | } /* end asn_predict_length() */ |
968 | | |
969 | | /*******************************************************************-o-****** |
970 | | * asn_predict_length |
971 | | * |
972 | | * Parameters: |
973 | | * type |
974 | | * *ptr |
975 | | * u_char_len |
976 | | * |
977 | | * Returns: |
978 | | * Length in bytes: 1 + <n> + <u_char_len>, where |
979 | | * |
980 | | * 1 For the ASN.1 type. |
981 | | * <n> # of bytes to store length of data. |
982 | | * <u_char_len> Length of data associated with ASN.1 type. |
983 | | * |
984 | | * This gives the number of bytes that the ASN.1 encoder (in asn1.c) will |
985 | | * use to encode a particular integer value. This is as broken as the |
986 | | * currently used encoder. |
987 | | * |
988 | | * XXX How is <n> chosen, exactly?? |
989 | | */ |
990 | | static int |
991 | | asn_predict_length(int type, u_char * ptr, size_t u_char_len) |
992 | 0 | { |
993 | | /* Check for integer overflow. */ |
994 | 0 | if (1 + 3 + u_char_len < 1 + 3) |
995 | 0 | return -1; |
996 | | |
997 | 0 | if (type & ASN_SEQUENCE) |
998 | 0 | return 1 + 3 + u_char_len; |
999 | | |
1000 | 0 | if (type & ASN_INTEGER) { |
1001 | 0 | u_long value; |
1002 | 0 | memcpy(&value, ptr, u_char_len); |
1003 | 0 | u_char_len = asn_predict_int_length(type, value, u_char_len); |
1004 | 0 | } |
1005 | | |
1006 | | /* Check for integer overflow. */ |
1007 | 0 | if (1 + 3 + u_char_len < 1 + 3) |
1008 | 0 | return -1; |
1009 | | |
1010 | 0 | if (u_char_len < 0x80) |
1011 | 0 | return 1 + 1 + u_char_len; |
1012 | 0 | else if (u_char_len < 0xFF) |
1013 | 0 | return 1 + 2 + u_char_len; |
1014 | 0 | else |
1015 | 0 | return 1 + 3 + u_char_len; |
1016 | |
|
1017 | 0 | } /* end asn_predict_length() */ |
1018 | | |
1019 | | /*******************************************************************-o-****** |
1020 | | * usm_calc_offsets |
1021 | | * |
1022 | | * Parameters: |
1023 | | * (See list below...) |
1024 | | * |
1025 | | * Returns: |
1026 | | * 0 On success, |
1027 | | * -1 Otherwise. |
1028 | | * |
1029 | | * |
1030 | | * This routine calculates the offsets into an outgoing message buffer |
1031 | | * for the necessary values. The outgoing buffer will generically |
1032 | | * look like this: |
1033 | | * |
1034 | | * SNMPv3 Message |
1035 | | * SEQ len[11] |
1036 | | * INT len version |
1037 | | * Header |
1038 | | * SEQ len |
1039 | | * INT len MsgID |
1040 | | * INT len msgMaxSize |
1041 | | * OST len msgFlags (OST = OCTET STRING) |
1042 | | * INT len msgSecurityModel |
1043 | | * MsgSecurityParameters |
1044 | | * [1] OST len[2] |
1045 | | * SEQ len[3] |
1046 | | * OST len msgAuthoritativeEngineID |
1047 | | * INT len msgAuthoritativeEngineBoots |
1048 | | * INT len msgAuthoritativeEngineTime |
1049 | | * OST len msgUserName |
1050 | | * OST len[4] [5] msgAuthenticationParameters |
1051 | | * OST len[6] [7] msgPrivacyParameters |
1052 | | * MsgData |
1053 | | * [8] OST len[9] [10] encryptedPDU |
1054 | | * or |
1055 | | * [8,10] SEQUENCE len[9] scopedPDU |
1056 | | * [12] |
1057 | | * |
1058 | | * The bracketed points will be needed to be identified ([x] is an index |
1059 | | * value, len[x] means a length value). Here is a semantic guide to them: |
1060 | | * |
1061 | | * [1] = globalDataLen (input) |
1062 | | * [2] = otstlen |
1063 | | * [3] = seq_len |
1064 | | * [4] = msgAuthParmLen (may be 0 or 12) |
1065 | | * [5] = authParamsOffset |
1066 | | * [6] = msgPrivParmLen (may be 0 or 8) |
1067 | | * [7] = privParamsOffset |
1068 | | * [8] = globalDataLen + msgSecParmLen |
1069 | | * [9] = datalen |
1070 | | * [10] = dataOffset |
1071 | | * [11] = theTotalLength - the length of the header itself |
1072 | | * [12] = theTotalLength |
1073 | | */ |
1074 | | static int |
1075 | | usm_calc_offsets(size_t globalDataLen, /* SNMPv3Message + HeaderData */ |
1076 | | int secLevel, size_t secEngineIDLen, size_t secNameLen, size_t scopedPduLen, /* An BER encoded sequence. */ |
1077 | | u_long engineboots, /* XXX (asn1.c works in long, not int.) */ |
1078 | | long engine_time, /* XXX (asn1.c works in long, not int.) */ |
1079 | | size_t * theTotalLength, /* globalDataLen + msgSecurityP. + msgData */ |
1080 | | size_t * authParamsOffset, /* Distance to auth bytes. */ |
1081 | | size_t * privParamsOffset, /* Distance to priv bytes. */ |
1082 | | size_t * dataOffset, /* Distance to scopedPdu SEQ -or- the |
1083 | | * crypted (data) portion of msgData. */ |
1084 | | size_t * datalen, /* Size of msgData OCTET STRING encoding. */ |
1085 | | size_t * msgAuthParmLen, /* Size of msgAuthenticationParameters. */ |
1086 | | size_t * msgPrivParmLen, /* Size of msgPrivacyParameters. */ |
1087 | | size_t * otstlen, /* Size of msgSecurityP. O.S. encoding. */ |
1088 | | size_t * seq_len, /* Size of msgSecurityP. SEQ data. */ |
1089 | | size_t * msgSecParmLen) |
1090 | 0 | { /* Size of msgSecurityP. SEQ. */ |
1091 | 0 | int engIDlen, /* Sizes of OCTET STRING and SEQ encodings */ |
1092 | 0 | engBtlen, /* for fields within */ |
1093 | 0 | engTmlen, /* msgSecurityParameters portion of */ |
1094 | 0 | namelen, /* SNMPv3Message. */ |
1095 | 0 | authlen, privlen, ret; |
1096 | | |
1097 | | /* |
1098 | | * If doing authentication, msgAuthParmLen = 12 else msgAuthParmLen = 0. |
1099 | | * If doing encryption, msgPrivParmLen = 8 else msgPrivParmLen = 0. |
1100 | | */ |
1101 | 0 | *msgAuthParmLen = (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
1102 | 0 | || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) ? 12 : 0; |
1103 | |
|
1104 | 0 | *msgPrivParmLen = (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) ? 8 : 0; |
1105 | | |
1106 | | |
1107 | | /* |
1108 | | * Calculate lengths. |
1109 | | */ |
1110 | 0 | if ((engIDlen = asn_predict_length(ASN_OCTET_STR, |
1111 | 0 | NULL, secEngineIDLen)) == -1) { |
1112 | 0 | return -1; |
1113 | 0 | } |
1114 | | |
1115 | 0 | if ((engBtlen = asn_predict_length(ASN_INTEGER, |
1116 | 0 | (u_char *) & engineboots, |
1117 | 0 | sizeof(long))) == -1) { |
1118 | 0 | return -1; |
1119 | 0 | } |
1120 | | |
1121 | 0 | if ((engTmlen = asn_predict_length(ASN_INTEGER, |
1122 | 0 | (u_char *) & engine_time, |
1123 | 0 | sizeof(long))) == -1) { |
1124 | 0 | return -1; |
1125 | 0 | } |
1126 | | |
1127 | 0 | if ((namelen = asn_predict_length(ASN_OCTET_STR, |
1128 | 0 | NULL, secNameLen)) == -1) { |
1129 | 0 | return -1; |
1130 | 0 | } |
1131 | | |
1132 | 0 | if ((authlen = asn_predict_length(ASN_OCTET_STR, |
1133 | 0 | NULL, *msgAuthParmLen)) == -1) { |
1134 | 0 | return -1; |
1135 | 0 | } |
1136 | | |
1137 | 0 | if ((privlen = asn_predict_length(ASN_OCTET_STR, |
1138 | 0 | NULL, *msgPrivParmLen)) == -1) { |
1139 | 0 | return -1; |
1140 | 0 | } |
1141 | | |
1142 | 0 | *seq_len = |
1143 | 0 | engIDlen + engBtlen + engTmlen + namelen + authlen + privlen; |
1144 | |
|
1145 | 0 | if ((ret = asn_predict_length(ASN_SEQUENCE, |
1146 | 0 | NULL, *seq_len)) == -1) { |
1147 | 0 | return -1; |
1148 | 0 | } |
1149 | 0 | *otstlen = (size_t)ret; |
1150 | |
|
1151 | 0 | if ((ret = asn_predict_length(ASN_OCTET_STR, |
1152 | 0 | NULL, *otstlen)) == -1) { |
1153 | 0 | return -1; |
1154 | 0 | } |
1155 | 0 | *msgSecParmLen = (size_t)ret; |
1156 | |
|
1157 | 0 | *authParamsOffset = globalDataLen + +(*msgSecParmLen - *seq_len) |
1158 | 0 | + engIDlen + engBtlen + engTmlen + namelen |
1159 | 0 | + (authlen - *msgAuthParmLen); |
1160 | |
|
1161 | 0 | *privParamsOffset = *authParamsOffset + *msgAuthParmLen |
1162 | 0 | + (privlen - *msgPrivParmLen); |
1163 | | |
1164 | | |
1165 | | /* |
1166 | | * Compute the size of the plaintext. Round up to account for cipher |
1167 | | * block size, if necessary. |
1168 | | * |
1169 | | * XXX This is hardwired for 1DES... If scopedPduLen is already |
1170 | | * a multiple of 8, then *add* 8 more; otherwise, round up |
1171 | | * to the next multiple of 8. |
1172 | | * |
1173 | | * FIX Calculation of encrypted portion of msgData and consequent |
1174 | | * setting and sanity checking of theTotalLength, et al. should |
1175 | | * occur *after* encryption has taken place. |
1176 | | */ |
1177 | 0 | if (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
1178 | 0 | scopedPduLen = ROUNDUP8(scopedPduLen); |
1179 | |
|
1180 | 0 | if ((ret = asn_predict_length(ASN_OCTET_STR, NULL, scopedPduLen)) == -1) { |
1181 | 0 | return -1; |
1182 | 0 | } |
1183 | 0 | *datalen = (size_t)ret; |
1184 | 0 | } else { |
1185 | 0 | *datalen = scopedPduLen; |
1186 | 0 | } |
1187 | | |
1188 | 0 | *dataOffset = globalDataLen + *msgSecParmLen + |
1189 | 0 | (*datalen - scopedPduLen); |
1190 | 0 | *theTotalLength = globalDataLen + *msgSecParmLen + *datalen; |
1191 | |
|
1192 | 0 | return 0; |
1193 | |
|
1194 | 0 | } /* end usm_calc_offsets() */ |
1195 | | |
1196 | | #ifndef NETSNMP_DISABLE_DES |
1197 | | /*******************************************************************-o-****** |
1198 | | * usm_set_salt |
1199 | | * |
1200 | | * Parameters: |
1201 | | * *iv (O) Buffer to contain IV. |
1202 | | * *iv_length (O) Length of iv. |
1203 | | * *priv_salt (I) Salt portion of private key. |
1204 | | * priv_salt_length (I) Length of priv_salt. |
1205 | | * *msgSalt (I/O) Pointer salt portion of outgoing msg buffer. |
1206 | | * |
1207 | | * Returns: |
1208 | | * 0 On success, |
1209 | | * -1 Otherwise. |
1210 | | * |
1211 | | * Determine the initialization vector for the DES-CBC encryption. |
1212 | | * (Cf. RFC 2274, 8.1.1.1.) |
1213 | | * |
1214 | | * iv is defined as the concatenation of engineBoots and the |
1215 | | * salt integer. |
1216 | | * The salt integer is incremented. |
1217 | | * The resulting salt is copied into the msgSalt buffer. |
1218 | | * The result of the concatenation is then XORed with the salt |
1219 | | * portion of the private key (last 8 bytes). |
1220 | | * The IV result is returned individually for further use. |
1221 | | */ |
1222 | | static int |
1223 | | usm_set_salt(u_char * iv, |
1224 | | size_t * iv_length, |
1225 | | u_char * priv_salt, size_t priv_salt_length, u_char * msgSalt) |
1226 | 0 | { |
1227 | 0 | size_t propersize_salt = BYTESIZE(USM_DES_SALT_LENGTH); |
1228 | 0 | int net_boots; |
1229 | 0 | int net_salt_int; |
1230 | | /* |
1231 | | * net_* should be encoded in network byte order. XXX Why? |
1232 | | */ |
1233 | 0 | int iindex; |
1234 | | |
1235 | | |
1236 | | /* |
1237 | | * Sanity check. |
1238 | | */ |
1239 | 0 | if (!iv || !iv_length || !priv_salt || (*iv_length != propersize_salt) |
1240 | 0 | || (priv_salt_length < propersize_salt)) { |
1241 | 0 | return -1; |
1242 | 0 | } |
1243 | | |
1244 | | |
1245 | 0 | net_boots = htonl(snmpv3_local_snmpEngineBoots()); |
1246 | 0 | net_salt_int = htonl(salt_integer); |
1247 | |
|
1248 | 0 | salt_integer += 1; |
1249 | |
|
1250 | 0 | memcpy(iv, &net_boots, propersize_salt / 2); |
1251 | 0 | memcpy(iv + (propersize_salt / 2), &net_salt_int, propersize_salt / 2); |
1252 | |
|
1253 | 0 | if (msgSalt) |
1254 | 0 | memcpy(msgSalt, iv, propersize_salt); |
1255 | | |
1256 | | |
1257 | | /* |
1258 | | * Turn the salt into an IV: XOR <boots, salt_int> with salt |
1259 | | * portion of priv_key. |
1260 | | */ |
1261 | 0 | for (iindex = 0; iindex < (int) propersize_salt; iindex++) |
1262 | 0 | iv[iindex] ^= priv_salt[iindex]; |
1263 | | |
1264 | |
|
1265 | 0 | return 0; |
1266 | |
|
1267 | 0 | } /* end usm_set_salt() */ |
1268 | | #endif |
1269 | | |
1270 | | #ifdef HAVE_AES |
1271 | | /*******************************************************************-o-****** |
1272 | | * usm_set_aes_iv |
1273 | | * |
1274 | | * Parameters: |
1275 | | * *iv (O) Buffer to contain IV. |
1276 | | * *iv_length (O) Length of iv. |
1277 | | * net_boots (I) the network byte order of the authEng boots val |
1278 | | * net_time (I) the network byte order of the authEng time val |
1279 | | * *salt (O) A buffer for the outgoing salt (= 8 bytes of iv) |
1280 | | * |
1281 | | * Returns: |
1282 | | * 0 On success, |
1283 | | * -1 Otherwise. |
1284 | | * |
1285 | | * Determine the initialization vector for AES encryption. |
1286 | | * (draft-blumenthal-aes-usm-03.txt, 3.1.2.2) |
1287 | | * |
1288 | | * iv is defined as the concatenation of engineBoots, engineTime |
1289 | | and a 64 bit salt-integer. |
1290 | | * The 64 bit salt integer is incremented. |
1291 | | * The resulting salt is copied into the salt buffer. |
1292 | | * The IV result is returned individually for further use. |
1293 | | */ |
1294 | | static int |
1295 | | usm_set_aes_iv(u_char * iv, |
1296 | | size_t * iv_length, |
1297 | | u_int net_boots, |
1298 | | u_int net_time, |
1299 | | u_char * salt) |
1300 | 0 | { |
1301 | | /* |
1302 | | * net_* should be encoded in network byte order. |
1303 | | */ |
1304 | 0 | int net_salt_int1, net_salt_int2; |
1305 | 0 | #define PROPER_AES_IV_SIZE 64 |
1306 | | |
1307 | | /* |
1308 | | * Sanity check. |
1309 | | */ |
1310 | 0 | if (!iv || !iv_length) { |
1311 | 0 | return -1; |
1312 | 0 | } |
1313 | | |
1314 | 0 | net_salt_int1 = htonl(salt_integer64_1); |
1315 | 0 | net_salt_int2 = htonl(salt_integer64_2); |
1316 | |
|
1317 | 0 | if ((salt_integer64_2 += 1) == 0) |
1318 | 0 | salt_integer64_2 += 1; |
1319 | | |
1320 | | /* XXX: warning: hard coded proper lengths */ |
1321 | 0 | memcpy(iv, &net_boots, 4); |
1322 | 0 | memcpy(iv+4, &net_time, 4); |
1323 | 0 | memcpy(iv+8, &net_salt_int1, 4); |
1324 | 0 | memcpy(iv+12, &net_salt_int2, 4); |
1325 | |
|
1326 | 0 | memcpy(salt, iv+8, 8); /* only copy the needed portion */ |
1327 | 0 | return 0; |
1328 | 0 | } /* end usm_set_aes_iv() */ |
1329 | | #endif /* HAVE_AES */ |
1330 | | |
1331 | | /*******************************************************************-o-****** |
1332 | | * usm_check_secLevel_vs_protocols |
1333 | | * |
1334 | | * Parameters: |
1335 | | * level |
1336 | | * *authProtocol |
1337 | | * authProtocolLen |
1338 | | * *privProtocol |
1339 | | * privProtocolLen |
1340 | | * |
1341 | | * Returns: |
1342 | | * 0 On success, |
1343 | | * 1 Otherwise. |
1344 | | * |
1345 | | * Same as above but with explicitly named transform types instead of taking |
1346 | | * from the usmUser structure. |
1347 | | */ |
1348 | | static int |
1349 | | usm_check_secLevel_vs_protocols(int level, |
1350 | | const oid * authProtocol, |
1351 | | u_int authProtocolLen, |
1352 | | const oid * privProtocol, |
1353 | | u_int privProtocolLen) |
1354 | 0 | { |
1355 | |
|
1356 | 0 | if (level == SNMP_SEC_LEVEL_AUTHPRIV |
1357 | 0 | && |
1358 | 0 | (netsnmp_oid_equals |
1359 | 0 | (privProtocol, privProtocolLen, usmNoPrivProtocol, |
1360 | 0 | OID_LENGTH(usmNoPrivProtocol)) == 0)) { |
1361 | 0 | DEBUGMSGTL(("usm", "Level: %d\n", level)); |
1362 | 0 | DEBUGMSGTL(("usm", "Auth Protocol: ")); |
1363 | 0 | DEBUGMSGOID(("usm", authProtocol, authProtocolLen)); |
1364 | 0 | DEBUGMSG(("usm", ", Priv Protocol: ")); |
1365 | 0 | DEBUGMSGOID(("usm", privProtocol, privProtocolLen)); |
1366 | 0 | DEBUGMSG(("usm", "\n")); |
1367 | 0 | return 1; |
1368 | 0 | } |
1369 | 0 | if ((level == SNMP_SEC_LEVEL_AUTHPRIV |
1370 | 0 | || level == SNMP_SEC_LEVEL_AUTHNOPRIV) |
1371 | 0 | && |
1372 | 0 | (netsnmp_oid_equals |
1373 | 0 | (authProtocol, authProtocolLen, usmNoAuthProtocol, |
1374 | 0 | OID_LENGTH(usmNoAuthProtocol)) == 0)) { |
1375 | 0 | DEBUGMSGTL(("usm", "Level: %d\n", level)); |
1376 | 0 | DEBUGMSGTL(("usm", "Auth Protocol: ")); |
1377 | 0 | DEBUGMSGOID(("usm", authProtocol, authProtocolLen)); |
1378 | 0 | DEBUGMSG(("usm", ", Priv Protocol: ")); |
1379 | 0 | DEBUGMSGOID(("usm", privProtocol, privProtocolLen)); |
1380 | 0 | DEBUGMSG(("usm", "\n")); |
1381 | 0 | return 1; |
1382 | 0 | } |
1383 | | |
1384 | 0 | return 0; |
1385 | |
|
1386 | 0 | } /* end usm_check_secLevel_vs_protocols() */ |
1387 | | |
1388 | | /*******************************************************************-o-****** |
1389 | | * usm_generate_out_msg |
1390 | | * |
1391 | | * Parameters: |
1392 | | * (See list below...) |
1393 | | * |
1394 | | * Returns: |
1395 | | * SNMPERR_SUCCESS On success. |
1396 | | * SNMPERR_USM_AUTHENTICATIONFAILURE |
1397 | | * SNMPERR_USM_ENCRYPTIONERROR |
1398 | | * SNMPERR_USM_GENERICERROR |
1399 | | * SNMPERR_USM_UNKNOWNSECURITYNAME |
1400 | | * SNMPERR_USM_GENERICERROR |
1401 | | * SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL |
1402 | | * |
1403 | | * |
1404 | | * Generates an outgoing message. |
1405 | | * |
1406 | | * XXX Beware of misnomers! |
1407 | | */ |
1408 | | static int |
1409 | | usm_generate_out_msg(int msgProcModel, /* (UNUSED) */ |
1410 | | u_char * globalData, /* IN */ |
1411 | | /* |
1412 | | * Pointer to msg header data will point to the beginning |
1413 | | * * of the entire packet buffer to be transmitted on wire, |
1414 | | * * memory will be contiguous with secParams, typically |
1415 | | * * this pointer will be passed back as beginning of |
1416 | | * * wholeMsg below. asn seq. length is updated w/ new length. |
1417 | | * * |
1418 | | * * While this points to a buffer that should be big enough |
1419 | | * * for the whole message, only the first two parts |
1420 | | * * of the message are completed, namely SNMPv3Message and |
1421 | | * * HeaderData. globalDataLen (next parameter) represents |
1422 | | * * the length of these two completed parts. |
1423 | | */ |
1424 | | size_t globalDataLen, /* IN - Length of msg header data. */ |
1425 | | int maxMsgSize, /* (UNUSED) */ |
1426 | | int secModel, /* (UNUSED) */ |
1427 | | const u_char *secEngineID, /* IN - Pointer snmpEngineID. */ |
1428 | | size_t secEngineIDLen, /* IN - SnmpEngineID length. */ |
1429 | | const char *secName, /* IN - Pointer to securityName.*/ |
1430 | | size_t secNameLen, /* IN - SecurityName length. */ |
1431 | | int secLevel, /* IN - AuthNoPriv, authPriv etc. */ |
1432 | | const u_char *scopedPdu, /* IN */ |
1433 | | /* |
1434 | | * Pointer to scopedPdu will be encrypted by USM if needed |
1435 | | * * and written to packet buffer immediately following |
1436 | | * * securityParameters, entire msg will be authenticated by |
1437 | | * * USM if needed. |
1438 | | */ |
1439 | | size_t scopedPduLen, /* IN - scopedPdu length. */ |
1440 | | const void *secStateRef, /* IN */ |
1441 | | /* |
1442 | | * secStateRef, pointer to cached info provided only for |
1443 | | * * Response, otherwise NULL. |
1444 | | */ |
1445 | | struct usmUser *sessUser, /* IN - pointer to current session user. */ |
1446 | | u_char * secParams, /* OUT */ |
1447 | | /* |
1448 | | * BER encoded securityParameters pointer to offset within |
1449 | | * * packet buffer where secParams should be written, the |
1450 | | * * entire BER encoded OCTET STRING (including header) is |
1451 | | * * written here by USM secParams = globalData + |
1452 | | * * globalDataLen. |
1453 | | */ |
1454 | | size_t * secParamsLen, /* IN/OUT - Len available, len returned. */ |
1455 | | u_char ** wholeMsg, /* OUT */ |
1456 | | /* |
1457 | | * Complete authenticated/encrypted message - typically |
1458 | | * * the pointer to start of packet buffer provided in |
1459 | | * * globalData is returned here, could also be a separate |
1460 | | * * buffer. |
1461 | | */ |
1462 | | size_t * wholeMsgLen) |
1463 | 0 | { /* IN/OUT - Len available, len returned. */ |
1464 | 0 | size_t otstlen; |
1465 | 0 | size_t seq_len; |
1466 | 0 | size_t msgAuthParmLen; |
1467 | 0 | size_t msgPrivParmLen; |
1468 | 0 | size_t msgSecParmLen; |
1469 | 0 | size_t authParamsOffset; |
1470 | 0 | size_t privParamsOffset; |
1471 | 0 | size_t datalen; |
1472 | 0 | size_t dataOffset; |
1473 | 0 | size_t theTotalLength; |
1474 | |
|
1475 | 0 | u_char *ptr; |
1476 | 0 | size_t ptr_len; |
1477 | 0 | size_t remaining; |
1478 | 0 | size_t offSet; |
1479 | 0 | u_int boots_uint; |
1480 | 0 | u_int time_uint; |
1481 | 0 | long boots_long; |
1482 | 0 | long time_long; |
1483 | | |
1484 | | /* |
1485 | | * Indirection because secStateRef values override parameters. |
1486 | | * |
1487 | | * None of these are to be free'd - they are either pointing to |
1488 | | * what's in the secStateRef or to something either in the |
1489 | | * actual parameter list or the user list. |
1490 | | */ |
1491 | |
|
1492 | 0 | const char *theName = NULL; |
1493 | 0 | u_int theNameLength = 0; |
1494 | 0 | const u_char *theEngineID = NULL; |
1495 | 0 | u_int theEngineIDLength = 0; |
1496 | 0 | u_char *theAuthKey = NULL; |
1497 | 0 | u_int theAuthKeyLength = 0; |
1498 | 0 | const oid *theAuthProtocol = NULL; |
1499 | 0 | u_int theAuthProtocolLength = 0; |
1500 | 0 | u_char *thePrivKey = NULL; |
1501 | 0 | u_int thePrivKeyLength = 0; |
1502 | 0 | const oid *thePrivProtocol = NULL; |
1503 | 0 | u_int thePrivProtocolLength = 0; |
1504 | 0 | int theSecLevel = 0; /* No defined const for bad |
1505 | | * value (other then err). |
1506 | | */ |
1507 | |
|
1508 | 0 | DEBUGMSGTL(("usm", "USM processing has begun.\n")); |
1509 | |
|
1510 | 0 | if (secStateRef != NULL) { |
1511 | | /* |
1512 | | * To hush the compiler for now. XXX |
1513 | | */ |
1514 | 0 | const struct usmStateReference *ref = secStateRef; |
1515 | |
|
1516 | 0 | theName = ref->usr_name; |
1517 | 0 | theNameLength = ref->usr_name_length; |
1518 | 0 | theEngineID = ref->usr_engine_id; |
1519 | 0 | theEngineIDLength = ref->usr_engine_id_length; |
1520 | |
|
1521 | 0 | if (!theEngineIDLength) { |
1522 | 0 | theEngineID = secEngineID; |
1523 | 0 | theEngineIDLength = secEngineIDLen; |
1524 | 0 | } |
1525 | |
|
1526 | 0 | theAuthProtocol = ref->usr_auth_protocol; |
1527 | 0 | theAuthProtocolLength = ref->usr_auth_protocol_length; |
1528 | 0 | theAuthKey = ref->usr_auth_key; |
1529 | 0 | theAuthKeyLength = ref->usr_auth_key_length; |
1530 | 0 | thePrivProtocol = ref->usr_priv_protocol; |
1531 | 0 | thePrivProtocolLength = ref->usr_priv_protocol_length; |
1532 | 0 | thePrivKey = ref->usr_priv_key; |
1533 | 0 | thePrivKeyLength = ref->usr_priv_key_length; |
1534 | 0 | theSecLevel = ref->usr_sec_level; |
1535 | 0 | } |
1536 | | |
1537 | | /* |
1538 | | * Identify the user record. |
1539 | | */ |
1540 | 0 | else { |
1541 | 0 | struct usmUser *user; |
1542 | | |
1543 | | /* |
1544 | | * we do allow an unknown user name for |
1545 | | * unauthenticated requests. |
1546 | | */ |
1547 | 0 | if (sessUser) |
1548 | 0 | user = sessUser; |
1549 | 0 | else |
1550 | 0 | user = usm_get_user2(secEngineID, secEngineIDLen, secName, secNameLen); |
1551 | |
|
1552 | 0 | if (user == NULL && secLevel != SNMP_SEC_LEVEL_NOAUTH) { |
1553 | 0 | DEBUGMSGTL(("usm", "Unknown User(%s)\n", secName)); |
1554 | 0 | return SNMPERR_USM_UNKNOWNSECURITYNAME; |
1555 | 0 | } |
1556 | | |
1557 | 0 | theName = secName; |
1558 | 0 | theNameLength = secNameLen; |
1559 | 0 | theEngineID = secEngineID; |
1560 | 0 | theSecLevel = secLevel; |
1561 | 0 | theEngineIDLength = secEngineIDLen; |
1562 | 0 | if (user) { |
1563 | 0 | theAuthProtocol = user->authProtocol; |
1564 | 0 | theAuthProtocolLength = user->authProtocolLen; |
1565 | 0 | theAuthKey = user->authKey; |
1566 | 0 | theAuthKeyLength = user->authKeyLen; |
1567 | 0 | thePrivProtocol = user->privProtocol; |
1568 | 0 | thePrivProtocolLength = user->privProtocolLen; |
1569 | 0 | thePrivKey = user->privKey; |
1570 | 0 | thePrivKeyLength = user->privKeyLen; |
1571 | 0 | } else { |
1572 | | /* |
1573 | | * unknown users can not do authentication (obviously) |
1574 | | */ |
1575 | 0 | theAuthProtocol = usmNoAuthProtocol; |
1576 | 0 | theAuthProtocolLength = |
1577 | 0 | OID_LENGTH(usmNoAuthProtocol); |
1578 | 0 | theAuthKey = NULL; |
1579 | 0 | theAuthKeyLength = 0; |
1580 | 0 | thePrivProtocol = usmNoPrivProtocol; |
1581 | 0 | thePrivProtocolLength = |
1582 | 0 | OID_LENGTH(usmNoPrivProtocol); |
1583 | 0 | thePrivKey = NULL; |
1584 | 0 | thePrivKeyLength = 0; |
1585 | 0 | } |
1586 | 0 | } /* endif -- secStateRef==NULL */ |
1587 | | |
1588 | | |
1589 | | /* |
1590 | | * From here to the end of the function, avoid reference to |
1591 | | * secName, secEngineID, secLevel, and associated lengths. |
1592 | | */ |
1593 | | |
1594 | | |
1595 | | /* |
1596 | | * Check to see if the user can use the requested sec services. |
1597 | | */ |
1598 | 0 | if (usm_check_secLevel_vs_protocols(theSecLevel, |
1599 | 0 | theAuthProtocol, |
1600 | 0 | theAuthProtocolLength, |
1601 | 0 | thePrivProtocol, |
1602 | 0 | thePrivProtocolLength) == 1) { |
1603 | 0 | DEBUGMSGTL(("usm", "Unsupported Security Level (%d)\n", |
1604 | 0 | theSecLevel)); |
1605 | 0 | return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL; |
1606 | 0 | } |
1607 | | |
1608 | | |
1609 | | /* |
1610 | | * Retrieve the engine information. |
1611 | | * |
1612 | | * XXX No error is declared in the EoP when sending messages to |
1613 | | * unknown engines, processing continues w/ boots/time == (0,0). |
1614 | | */ |
1615 | 0 | if (get_enginetime(theEngineID, theEngineIDLength, |
1616 | 0 | &boots_uint, &time_uint, FALSE) == -1) { |
1617 | 0 | DEBUGMSGTL(("usm", "%s\n", "Failed to find engine data.")); |
1618 | 0 | } |
1619 | |
|
1620 | 0 | boots_long = boots_uint; |
1621 | 0 | time_long = time_uint; |
1622 | | |
1623 | | |
1624 | | /* |
1625 | | * Set up the Offsets. |
1626 | | */ |
1627 | 0 | if (usm_calc_offsets(globalDataLen, theSecLevel, theEngineIDLength, |
1628 | 0 | theNameLength, scopedPduLen, boots_long, |
1629 | 0 | time_long, &theTotalLength, &authParamsOffset, |
1630 | 0 | &privParamsOffset, &dataOffset, &datalen, |
1631 | 0 | &msgAuthParmLen, &msgPrivParmLen, &otstlen, |
1632 | 0 | &seq_len, &msgSecParmLen) == -1) { |
1633 | 0 | DEBUGMSGTL(("usm", "Failed calculating offsets.\n")); |
1634 | 0 | return SNMPERR_USM_GENERICERROR; |
1635 | 0 | } |
1636 | | |
1637 | | /* |
1638 | | * So, we have the offsets for the three parts that need to be |
1639 | | * determined, and an overall length. Now we need to make |
1640 | | * sure all of this would fit in the outgoing buffer, and |
1641 | | * whether or not we need to make a new buffer, etc. |
1642 | | */ |
1643 | | |
1644 | | |
1645 | | /* |
1646 | | * Set wholeMsg as a pointer to globalData. Sanity check for |
1647 | | * the proper size. |
1648 | | * |
1649 | | * Mark workspace in the message with bytes of all 1's to make it |
1650 | | * easier to find mistakes in raw message dumps. |
1651 | | */ |
1652 | 0 | ptr = *wholeMsg = globalData; |
1653 | 0 | if (theTotalLength > *wholeMsgLen) { |
1654 | 0 | DEBUGMSGTL(("usm", "Message won't fit in buffer.\n")); |
1655 | 0 | return SNMPERR_USM_GENERICERROR; |
1656 | 0 | } |
1657 | | |
1658 | 0 | ptr_len = *wholeMsgLen = theTotalLength; |
1659 | |
|
1660 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1661 | | memset(&ptr[globalDataLen], 0xFF, theTotalLength - globalDataLen); |
1662 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
1663 | | |
1664 | | /* |
1665 | | * Do the encryption. |
1666 | | */ |
1667 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
1668 | 0 | size_t encrypted_length = theTotalLength - dataOffset; |
1669 | 0 | size_t salt_length = BYTESIZE(USM_MAX_SALT_LENGTH); |
1670 | 0 | u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)]; |
1671 | 0 | int priv_type = sc_get_privtype(thePrivProtocol, |
1672 | 0 | thePrivProtocolLength); |
1673 | 0 | #ifdef HAVE_AES |
1674 | 0 | if (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { |
1675 | 0 | if (!thePrivKey || |
1676 | 0 | usm_set_aes_iv(salt, &salt_length, |
1677 | 0 | htonl(boots_uint), htonl(time_uint), |
1678 | 0 | &ptr[privParamsOffset]) == -1) { |
1679 | 0 | DEBUGMSGTL(("usm", "Can't set AES iv.\n")); |
1680 | 0 | return SNMPERR_USM_GENERICERROR; |
1681 | 0 | } |
1682 | 0 | } |
1683 | 0 | #endif |
1684 | 0 | #ifndef NETSNMP_DISABLE_DES |
1685 | | /* |
1686 | | * XXX Hardwired to seek into a 1DES private key! |
1687 | | */ |
1688 | 0 | if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { |
1689 | 0 | if (!thePrivKey || |
1690 | 0 | (usm_set_salt(salt, &salt_length, |
1691 | 0 | thePrivKey + 8, thePrivKeyLength - 8, |
1692 | 0 | &ptr[privParamsOffset]) |
1693 | 0 | == -1)) { |
1694 | 0 | DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n")); |
1695 | 0 | return SNMPERR_USM_GENERICERROR; |
1696 | 0 | } |
1697 | 0 | } |
1698 | 0 | #endif |
1699 | | |
1700 | 0 | if (sc_encrypt(thePrivProtocol, thePrivProtocolLength, |
1701 | 0 | thePrivKey, thePrivKeyLength, |
1702 | 0 | salt, salt_length, |
1703 | 0 | scopedPdu, scopedPduLen, |
1704 | 0 | &ptr[dataOffset], &encrypted_length) |
1705 | 0 | != SNMP_ERR_NOERROR) { |
1706 | 0 | DEBUGMSGTL(("usm", "encryption error.\n")); |
1707 | 0 | return SNMPERR_USM_ENCRYPTIONERROR; |
1708 | 0 | } |
1709 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1710 | | if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { |
1711 | | dump_chunk("usm/dump", "This data was encrypted:", |
1712 | | scopedPdu, scopedPduLen); |
1713 | | dump_chunk("usm/dump", "salt + Encrypted form:", |
1714 | | salt, salt_length); |
1715 | | dump_chunk("usm/dump", NULL, |
1716 | | &ptr[dataOffset], encrypted_length); |
1717 | | dump_chunk("usm/dump", "*wholeMsg:", |
1718 | | *wholeMsg, theTotalLength); |
1719 | | } |
1720 | | #endif |
1721 | | |
1722 | | |
1723 | 0 | ptr = *wholeMsg; |
1724 | 0 | ptr_len = *wholeMsgLen = theTotalLength; |
1725 | | |
1726 | | |
1727 | | /* |
1728 | | * XXX Sanity check for salt length should be moved up |
1729 | | * under usm_calc_offsets() or tossed. |
1730 | | */ |
1731 | 0 | if ((encrypted_length != (theTotalLength - dataOffset)) |
1732 | 0 | || (salt_length != msgPrivParmLen)) { |
1733 | 0 | DEBUGMSGTL(("usm", "encryption length error.\n")); |
1734 | 0 | return SNMPERR_USM_ENCRYPTIONERROR; |
1735 | 0 | } |
1736 | | |
1737 | 0 | DEBUGMSGTL(("usm", "Encryption successful.\n")); |
1738 | 0 | } |
1739 | | |
1740 | | /* |
1741 | | * No encryption for you! |
1742 | | */ |
1743 | 0 | else { |
1744 | 0 | memcpy(&ptr[dataOffset], scopedPdu, scopedPduLen); |
1745 | 0 | } |
1746 | | |
1747 | | |
1748 | | |
1749 | | /* |
1750 | | * Start filling in the other fields (in prep for authentication). |
1751 | | * |
1752 | | * offSet is an octet string header, which is different from all |
1753 | | * the other headers. |
1754 | | */ |
1755 | 0 | remaining = ptr_len - globalDataLen; |
1756 | |
|
1757 | 0 | offSet = ptr_len - remaining; |
1758 | 0 | asn_build_header(&ptr[offSet], &remaining, |
1759 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1760 | 0 | ASN_OCTET_STR), otstlen); |
1761 | |
|
1762 | 0 | offSet = ptr_len - remaining; |
1763 | 0 | asn_build_sequence(&ptr[offSet], &remaining, |
1764 | 0 | (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), seq_len); |
1765 | |
|
1766 | 0 | offSet = ptr_len - remaining; |
1767 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineID"); |
1768 | 0 | asn_build_string(&ptr[offSet], &remaining, |
1769 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1770 | 0 | ASN_OCTET_STR), theEngineID, |
1771 | 0 | theEngineIDLength); |
1772 | 0 | DEBUGINDENTLESS(); |
1773 | |
|
1774 | 0 | offSet = ptr_len - remaining; |
1775 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineBoots"); |
1776 | 0 | asn_build_int(&ptr[offSet], &remaining, |
1777 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), |
1778 | 0 | &boots_long, sizeof(long)); |
1779 | 0 | DEBUGINDENTLESS(); |
1780 | |
|
1781 | 0 | offSet = ptr_len - remaining; |
1782 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineTime"); |
1783 | 0 | asn_build_int(&ptr[offSet], &remaining, |
1784 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), |
1785 | 0 | &time_long, sizeof(long)); |
1786 | 0 | DEBUGINDENTLESS(); |
1787 | |
|
1788 | 0 | offSet = ptr_len - remaining; |
1789 | 0 | DEBUGDUMPHEADER("send", "msgUserName"); |
1790 | 0 | asn_build_string(&ptr[offSet], &remaining, |
1791 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1792 | 0 | ASN_OCTET_STR), (const u_char *) theName, |
1793 | 0 | theNameLength); |
1794 | 0 | DEBUGINDENTLESS(); |
1795 | | |
1796 | | |
1797 | | /* |
1798 | | * Note: if there is no authentication being done, |
1799 | | * msgAuthParmLen is 0, and there is no effect (other than |
1800 | | * inserting a zero-length header) of the following |
1801 | | * statements. |
1802 | | */ |
1803 | |
|
1804 | 0 | offSet = ptr_len - remaining; |
1805 | 0 | asn_build_header(&ptr[offSet], |
1806 | 0 | &remaining, |
1807 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1808 | 0 | ASN_OCTET_STR), msgAuthParmLen); |
1809 | |
|
1810 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
1811 | 0 | || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
1812 | 0 | offSet = ptr_len - remaining; |
1813 | 0 | memset(&ptr[offSet], 0, msgAuthParmLen); |
1814 | 0 | } |
1815 | |
|
1816 | 0 | remaining -= msgAuthParmLen; |
1817 | | |
1818 | | |
1819 | | /* |
1820 | | * Note: if there is no encryption being done, msgPrivParmLen |
1821 | | * is 0, and there is no effect (other than inserting a |
1822 | | * zero-length header) of the following statements. |
1823 | | */ |
1824 | |
|
1825 | 0 | offSet = ptr_len - remaining; |
1826 | 0 | asn_build_header(&ptr[offSet], |
1827 | 0 | &remaining, |
1828 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1829 | 0 | ASN_OCTET_STR), msgPrivParmLen); |
1830 | |
|
1831 | 0 | remaining -= msgPrivParmLen; /* Skipping the IV already there. */ |
1832 | | |
1833 | | |
1834 | | /* |
1835 | | * For privacy, need to add the octet string header for it. |
1836 | | */ |
1837 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
1838 | 0 | offSet = ptr_len - remaining; |
1839 | 0 | asn_build_header(&ptr[offSet], |
1840 | 0 | &remaining, |
1841 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
1842 | 0 | ASN_OCTET_STR), |
1843 | 0 | theTotalLength - dataOffset); |
1844 | 0 | } |
1845 | | |
1846 | | |
1847 | | /* |
1848 | | * Adjust overall length and store it as the first SEQ length |
1849 | | * of the SNMPv3Message. |
1850 | | * |
1851 | | * FIX 4 is a magic number! |
1852 | | */ |
1853 | 0 | remaining = theTotalLength; |
1854 | 0 | asn_build_sequence(ptr, &remaining, |
1855 | 0 | (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
1856 | 0 | theTotalLength - 4); |
1857 | | |
1858 | | |
1859 | | /* |
1860 | | * Now, time to consider / do authentication. |
1861 | | */ |
1862 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
1863 | 0 | || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
1864 | 0 | size_t temp_sig_len = msgAuthParmLen; |
1865 | 0 | u_char *temp_sig = (u_char *) malloc(temp_sig_len); |
1866 | |
|
1867 | 0 | if (temp_sig == NULL) { |
1868 | 0 | DEBUGMSGTL(("usm", "Out of memory.\n")); |
1869 | 0 | return SNMPERR_USM_GENERICERROR; |
1870 | 0 | } |
1871 | | |
1872 | 0 | if (sc_generate_keyed_hash(theAuthProtocol, theAuthProtocolLength, |
1873 | 0 | theAuthKey, theAuthKeyLength, |
1874 | 0 | ptr, ptr_len, temp_sig, &temp_sig_len) |
1875 | 0 | != SNMP_ERR_NOERROR) { |
1876 | | /* |
1877 | | * FIX temp_sig_len defined?! |
1878 | | */ |
1879 | 0 | SNMP_ZERO(temp_sig, temp_sig_len); |
1880 | 0 | SNMP_FREE(temp_sig); |
1881 | 0 | DEBUGMSGTL(("usm", "Signing failed.\n")); |
1882 | 0 | return SNMPERR_USM_AUTHENTICATIONFAILURE; |
1883 | 0 | } |
1884 | | |
1885 | 0 | if (temp_sig_len != msgAuthParmLen) { |
1886 | 0 | SNMP_ZERO(temp_sig, temp_sig_len); |
1887 | 0 | SNMP_FREE(temp_sig); |
1888 | 0 | DEBUGMSGTL(("usm", "Signing lengths failed.\n")); |
1889 | 0 | return SNMPERR_USM_AUTHENTICATIONFAILURE; |
1890 | 0 | } |
1891 | | |
1892 | 0 | memcpy(&ptr[authParamsOffset], temp_sig, msgAuthParmLen); |
1893 | |
|
1894 | 0 | SNMP_ZERO(temp_sig, temp_sig_len); |
1895 | 0 | SNMP_FREE(temp_sig); |
1896 | |
|
1897 | 0 | } |
1898 | | |
1899 | | /* |
1900 | | * endif -- create keyed hash |
1901 | | */ |
1902 | | |
1903 | 0 | DEBUGMSGTL(("usm", "USM processing completed.\n")); |
1904 | |
|
1905 | 0 | return SNMPERR_SUCCESS; |
1906 | |
|
1907 | 0 | } /* end usm_generate_out_msg() */ |
1908 | | |
1909 | | static int |
1910 | | usm_secmod_generate_out_msg(struct snmp_secmod_outgoing_params *parms) |
1911 | 0 | { |
1912 | 0 | if (!parms) |
1913 | 0 | return SNMPERR_GENERR; |
1914 | | |
1915 | 0 | return usm_generate_out_msg(parms->msgProcModel, |
1916 | 0 | parms->globalData, parms->globalDataLen, |
1917 | 0 | parms->maxMsgSize, parms->secModel, |
1918 | 0 | parms->secEngineID, parms->secEngineIDLen, |
1919 | 0 | parms->secName, parms->secNameLen, |
1920 | 0 | parms->secLevel, |
1921 | 0 | parms->scopedPdu, parms->scopedPduLen, |
1922 | 0 | parms->secStateRef, |
1923 | 0 | parms->session->sessUser, |
1924 | 0 | parms->secParams, parms->secParamsLen, |
1925 | 0 | parms->wholeMsg, parms->wholeMsgLen); |
1926 | 0 | } |
1927 | | |
1928 | | #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
1929 | | static int |
1930 | | usm_rgenerate_out_msg(int msgProcModel, /* (UNUSED) */ |
1931 | | u_char * globalData, /* IN */ |
1932 | | /* |
1933 | | * points at the msgGlobalData, which is of length given by next |
1934 | | * parameter. |
1935 | | */ |
1936 | | size_t globalDataLen, /* IN - Length of msg header data. */ |
1937 | | int maxMsgSize, /* (UNUSED) */ |
1938 | | int secModel, /* (UNUSED) */ |
1939 | | const u_char *secEngineID, /* IN - Pointer snmpEngineID.*/ |
1940 | | size_t secEngineIDLen, /* IN - SnmpEngineID length. */ |
1941 | | const char *secName, /* IN - Pointer to securityName.*/ |
1942 | | size_t secNameLen, /* IN - SecurityName length. */ |
1943 | | int secLevel, /* IN - AuthNoPriv, authPriv etc. */ |
1944 | | const u_char *scopedPdu, /* IN */ |
1945 | | /* |
1946 | | * Pointer to scopedPdu will be encrypted by USM if needed |
1947 | | * * and written to packet buffer immediately following |
1948 | | * * securityParameters, entire msg will be authenticated by |
1949 | | * * USM if needed. |
1950 | | */ |
1951 | | size_t scopedPduLen, /* IN - scopedPdu length. */ |
1952 | | const void *secStateRef, /* IN */ |
1953 | | /* |
1954 | | * secStateRef, pointer to cached info provided only for |
1955 | | * * Response, otherwise NULL. |
1956 | | */ |
1957 | | struct usmUser *sessUser, /* IN - pointer to current session user. */ |
1958 | | |
1959 | | u_char ** wholeMsg, /* IN/OUT */ |
1960 | | /* |
1961 | | * Points at the pointer to the packet buffer, which might get extended |
1962 | | * if necessary via realloc(). |
1963 | | */ |
1964 | | size_t * wholeMsgLen, /* IN/OUT */ |
1965 | | /* |
1966 | | * Length of the entire packet buffer, **not** the length of the |
1967 | | * packet. |
1968 | | */ |
1969 | | size_t * offset /* IN/OUT */ |
1970 | | /* |
1971 | | * Offset from the end of the packet buffer to the start of the packet, |
1972 | | * also known as the packet length. |
1973 | | */ |
1974 | | ) |
1975 | 0 | { |
1976 | 0 | size_t msgAuthParmLen = 0; |
1977 | 0 | u_int boots_uint; |
1978 | 0 | u_int time_uint; |
1979 | 0 | long boots_long; |
1980 | 0 | long time_long; |
1981 | | |
1982 | | /* |
1983 | | * Indirection because secStateRef values override parameters. |
1984 | | * |
1985 | | * None of these are to be free'd - they are either pointing to |
1986 | | * what's in the secStateRef or to something either in the |
1987 | | * actual parameter list or the user list. |
1988 | | */ |
1989 | |
|
1990 | 0 | const char *theName = NULL; |
1991 | 0 | u_int theNameLength = 0; |
1992 | 0 | const u_char *theEngineID = NULL; |
1993 | 0 | u_int theEngineIDLength = 0; |
1994 | 0 | u_char *theAuthKey = NULL; |
1995 | 0 | u_int theAuthKeyLength = 0; |
1996 | 0 | const oid *theAuthProtocol = NULL; |
1997 | 0 | u_int theAuthProtocolLength = 0; |
1998 | 0 | u_char *thePrivKey = NULL; |
1999 | 0 | u_int thePrivKeyLength = 0; |
2000 | 0 | const oid *thePrivProtocol = NULL; |
2001 | 0 | u_int thePrivProtocolLength = 0; |
2002 | 0 | int theSecLevel = 0; /* No defined const for bad |
2003 | | * value (other then err). */ |
2004 | 0 | size_t salt_length = 0, save_salt_length = 0; |
2005 | 0 | u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)]; |
2006 | 0 | u_char authParams[USM_MAX_AUTHSIZE]; |
2007 | 0 | u_char iv[BYTESIZE(USM_MAX_SALT_LENGTH)]; |
2008 | 0 | size_t sp_offset = 0, mac_offset = 0; |
2009 | 0 | int rc = 0; |
2010 | |
|
2011 | 0 | DEBUGMSGTL(("usm", "USM processing has begun (offset %d)\n", (int)*offset)); |
2012 | |
|
2013 | 0 | if (secStateRef != NULL) { |
2014 | | /* |
2015 | | * To hush the compiler for now. XXX |
2016 | | */ |
2017 | 0 | const struct usmStateReference *ref = secStateRef; |
2018 | |
|
2019 | 0 | theName = ref->usr_name; |
2020 | 0 | theNameLength = ref->usr_name_length; |
2021 | 0 | theEngineID = ref->usr_engine_id; |
2022 | 0 | theEngineIDLength = ref->usr_engine_id_length; |
2023 | |
|
2024 | 0 | if (!theEngineIDLength) { |
2025 | 0 | theEngineID = secEngineID; |
2026 | 0 | theEngineIDLength = secEngineIDLen; |
2027 | 0 | } |
2028 | |
|
2029 | 0 | theAuthProtocol = ref->usr_auth_protocol; |
2030 | 0 | theAuthProtocolLength = ref->usr_auth_protocol_length; |
2031 | 0 | theAuthKey = ref->usr_auth_key; |
2032 | 0 | theAuthKeyLength = ref->usr_auth_key_length; |
2033 | 0 | thePrivProtocol = ref->usr_priv_protocol; |
2034 | 0 | thePrivProtocolLength = ref->usr_priv_protocol_length; |
2035 | 0 | thePrivKey = ref->usr_priv_key; |
2036 | 0 | thePrivKeyLength = ref->usr_priv_key_length; |
2037 | 0 | theSecLevel = ref->usr_sec_level; |
2038 | 0 | } |
2039 | | |
2040 | | /* |
2041 | | * * Identify the user record. |
2042 | | */ |
2043 | 0 | else { |
2044 | 0 | struct usmUser *user; |
2045 | | |
2046 | | /* |
2047 | | * we do allow an unknown user name for |
2048 | | * unauthenticated requests. |
2049 | | */ |
2050 | 0 | if (sessUser) |
2051 | 0 | user = sessUser; |
2052 | 0 | else |
2053 | 0 | user = usm_get_user2(secEngineID, secEngineIDLen, secName, secNameLen); |
2054 | |
|
2055 | 0 | if (user == NULL && secLevel != SNMP_SEC_LEVEL_NOAUTH) { |
2056 | 0 | DEBUGMSGTL(("usm", "Unknown User\n")); |
2057 | 0 | return SNMPERR_USM_UNKNOWNSECURITYNAME; |
2058 | 0 | } |
2059 | | |
2060 | 0 | theName = secName; |
2061 | 0 | theNameLength = secNameLen; |
2062 | 0 | theEngineID = secEngineID; |
2063 | 0 | theSecLevel = secLevel; |
2064 | 0 | theEngineIDLength = secEngineIDLen; |
2065 | 0 | if (user) { |
2066 | 0 | theAuthProtocol = user->authProtocol; |
2067 | 0 | theAuthProtocolLength = user->authProtocolLen; |
2068 | 0 | theAuthKey = user->authKey; |
2069 | 0 | theAuthKeyLength = user->authKeyLen; |
2070 | 0 | thePrivProtocol = user->privProtocol; |
2071 | 0 | thePrivProtocolLength = user->privProtocolLen; |
2072 | 0 | thePrivKey = user->privKey; |
2073 | 0 | thePrivKeyLength = user->privKeyLen; |
2074 | 0 | } else { |
2075 | | /* |
2076 | | * unknown users can not do authentication (obviously) |
2077 | | */ |
2078 | 0 | theAuthProtocol = usmNoAuthProtocol; |
2079 | 0 | theAuthProtocolLength = |
2080 | 0 | OID_LENGTH(usmNoAuthProtocol); |
2081 | 0 | theAuthKey = NULL; |
2082 | 0 | theAuthKeyLength = 0; |
2083 | 0 | thePrivProtocol = usmNoPrivProtocol; |
2084 | 0 | thePrivProtocolLength = |
2085 | 0 | OID_LENGTH(usmNoPrivProtocol); |
2086 | 0 | thePrivKey = NULL; |
2087 | 0 | thePrivKeyLength = 0; |
2088 | 0 | } |
2089 | 0 | } /* endif -- secStateRef==NULL */ |
2090 | | |
2091 | | |
2092 | | /* |
2093 | | * From here to the end of the function, avoid reference to |
2094 | | * secName, secEngineID, secLevel, and associated lengths. |
2095 | | */ |
2096 | | |
2097 | | |
2098 | | /* |
2099 | | * Check to see if the user can use the requested sec services. |
2100 | | */ |
2101 | 0 | if (usm_check_secLevel_vs_protocols(theSecLevel, |
2102 | 0 | theAuthProtocol, |
2103 | 0 | theAuthProtocolLength, |
2104 | 0 | thePrivProtocol, |
2105 | 0 | thePrivProtocolLength) == 1) { |
2106 | 0 | DEBUGMSGTL(("usm", "Unsupported Security Level or type (%d)\n", |
2107 | 0 | theSecLevel)); |
2108 | |
|
2109 | 0 | return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL; |
2110 | 0 | } |
2111 | | |
2112 | | |
2113 | | /* |
2114 | | * * Retrieve the engine information. |
2115 | | * * |
2116 | | * * XXX No error is declared in the EoP when sending messages to |
2117 | | * * unknown engines, processing continues w/ boots/time == (0,0). |
2118 | | */ |
2119 | 0 | if (get_enginetime(theEngineID, theEngineIDLength, |
2120 | 0 | &boots_uint, &time_uint, FALSE) == -1) { |
2121 | 0 | DEBUGMSGTL(("usm", "%s\n", "Failed to find engine data.")); |
2122 | 0 | } |
2123 | |
|
2124 | 0 | boots_long = boots_uint; |
2125 | 0 | time_long = time_uint; |
2126 | |
|
2127 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
2128 | | /* |
2129 | | * Initially assume that the ciphertext will end up the same size as |
2130 | | * the plaintext plus some padding. Really sc_encrypt ought to be able |
2131 | | * to grow this for us, a la asn_realloc_rbuild_<type> functions, but |
2132 | | * this will do for now. |
2133 | | */ |
2134 | 0 | u_char *ciphertext = NULL; |
2135 | 0 | size_t ciphertextlen = scopedPduLen + 64; |
2136 | 0 | int priv_type = sc_get_privtype(thePrivProtocol, |
2137 | 0 | thePrivProtocolLength); |
2138 | |
|
2139 | 0 | if ((ciphertext = (u_char *) malloc(ciphertextlen)) == NULL) { |
2140 | 0 | DEBUGMSGTL(("usm", |
2141 | 0 | "couldn't malloc %d bytes for encrypted PDU\n", |
2142 | 0 | (int)ciphertextlen)); |
2143 | 0 | return SNMPERR_MALLOC; |
2144 | 0 | } |
2145 | | |
2146 | | /* |
2147 | | * XXX Hardwired to seek into a 1DES private key! |
2148 | | */ |
2149 | 0 | #ifdef HAVE_AES |
2150 | 0 | if (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { |
2151 | 0 | salt_length = BYTESIZE(USM_AES_SALT_LENGTH); |
2152 | 0 | save_salt_length = BYTESIZE(USM_AES_SALT_LENGTH)/2; |
2153 | 0 | if (!thePrivKey || |
2154 | 0 | usm_set_aes_iv(salt, &salt_length, |
2155 | 0 | htonl(boots_uint), htonl(time_uint), |
2156 | 0 | iv) == -1) { |
2157 | 0 | DEBUGMSGTL(("usm", "Can't set AES iv.\n")); |
2158 | 0 | SNMP_FREE(ciphertext); |
2159 | 0 | return SNMPERR_USM_GENERICERROR; |
2160 | 0 | } |
2161 | 0 | } |
2162 | 0 | #endif |
2163 | 0 | #ifndef NETSNMP_DISABLE_DES |
2164 | 0 | if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { |
2165 | 0 | salt_length = BYTESIZE(USM_DES_SALT_LENGTH); |
2166 | 0 | save_salt_length = BYTESIZE(USM_DES_SALT_LENGTH); |
2167 | 0 | if (!thePrivKey || (usm_set_salt(salt, &salt_length, |
2168 | 0 | thePrivKey + 8, |
2169 | 0 | thePrivKeyLength - 8, |
2170 | 0 | iv) == -1)) { |
2171 | 0 | DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n")); |
2172 | 0 | SNMP_FREE(ciphertext); |
2173 | 0 | return SNMPERR_USM_GENERICERROR; |
2174 | 0 | } |
2175 | 0 | } |
2176 | 0 | #endif |
2177 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
2178 | | if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { |
2179 | | dump_chunk("usm/dump", "This data was encrypted:", |
2180 | | scopedPdu, scopedPduLen); |
2181 | | } |
2182 | | #endif |
2183 | | |
2184 | 0 | if (sc_encrypt(thePrivProtocol, thePrivProtocolLength, |
2185 | 0 | thePrivKey, thePrivKeyLength, |
2186 | 0 | salt, salt_length, |
2187 | 0 | scopedPdu, scopedPduLen, |
2188 | 0 | ciphertext, &ciphertextlen) != SNMP_ERR_NOERROR) { |
2189 | 0 | DEBUGMSGTL(("usm", "encryption error.\n")); |
2190 | 0 | SNMP_FREE(ciphertext); |
2191 | 0 | return SNMPERR_USM_ENCRYPTIONERROR; |
2192 | 0 | } |
2193 | | |
2194 | | /* |
2195 | | * Write the encrypted scopedPdu back into the packet buffer. |
2196 | | */ |
2197 | | |
2198 | 0 | *offset = 0; |
2199 | 0 | rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1, |
2200 | 0 | (u_char) (ASN_UNIVERSAL | |
2201 | 0 | ASN_PRIMITIVE | |
2202 | 0 | ASN_OCTET_STR), |
2203 | 0 | ciphertext, ciphertextlen); |
2204 | 0 | if (rc == 0) { |
2205 | 0 | DEBUGMSGTL(("usm", "Encryption failed.\n")); |
2206 | 0 | SNMP_FREE(ciphertext); |
2207 | 0 | return SNMPERR_USM_ENCRYPTIONERROR; |
2208 | 0 | } |
2209 | | |
2210 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
2211 | | if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { |
2212 | | dump_chunk("usm/dump", "salt + Encrypted form: ", salt, |
2213 | | salt_length); |
2214 | | dump_chunk("usm/dump", "wholeMsg:", |
2215 | | (*wholeMsg + *wholeMsgLen - *offset), *offset); |
2216 | | } |
2217 | | #endif |
2218 | | |
2219 | 0 | DEBUGMSGTL(("usm", "Encryption successful.\n")); |
2220 | 0 | SNMP_FREE(ciphertext); |
2221 | 0 | } else { |
2222 | | /* |
2223 | | * theSecLevel != SNMP_SEC_LEVEL_AUTHPRIV |
2224 | | */ |
2225 | 0 | } |
2226 | | |
2227 | | /* |
2228 | | * Start encoding the msgSecurityParameters. |
2229 | | */ |
2230 | | |
2231 | 0 | sp_offset = *offset; |
2232 | |
|
2233 | 0 | DEBUGDUMPHEADER("send", "msgPrivacyParameters"); |
2234 | | /* |
2235 | | * msgPrivacyParameters (warning: assumes DES salt). |
2236 | | */ |
2237 | 0 | rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1, |
2238 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
2239 | 0 | | ASN_OCTET_STR), |
2240 | 0 | iv, |
2241 | 0 | save_salt_length); |
2242 | 0 | DEBUGINDENTLESS(); |
2243 | 0 | if (rc == 0) { |
2244 | 0 | DEBUGMSGTL(("usm", "building privParams failed.\n")); |
2245 | 0 | return SNMPERR_TOO_LONG; |
2246 | 0 | } |
2247 | | |
2248 | 0 | DEBUGDUMPHEADER("send", "msgAuthenticationParameters"); |
2249 | | /* |
2250 | | * msgAuthenticationParameters. |
2251 | | */ |
2252 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
2253 | 0 | || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
2254 | 0 | memset(authParams, 0, sizeof(authParams)); |
2255 | 0 | msgAuthParmLen = |
2256 | 0 | sc_get_auth_maclen(sc_get_authtype(theAuthProtocol, |
2257 | 0 | theAuthProtocolLength)); |
2258 | 0 | } |
2259 | |
|
2260 | 0 | rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1, |
2261 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
2262 | 0 | | ASN_OCTET_STR), authParams, |
2263 | 0 | msgAuthParmLen); |
2264 | 0 | DEBUGINDENTLESS(); |
2265 | 0 | if (rc == 0) { |
2266 | 0 | DEBUGMSGTL(("usm", "building authParams failed.\n")); |
2267 | 0 | return SNMPERR_TOO_LONG; |
2268 | 0 | } |
2269 | | |
2270 | | /* |
2271 | | * Remember where to put the actual HMAC we calculate later on. An |
2272 | | * encoded OCTET STRING of length USM_MD5_AND_SHA_AUTH_LEN has an ASN.1 |
2273 | | * header of length 2, hence the fudge factor. This works as long as |
2274 | | * auth lengths stay < 127. |
2275 | | */ |
2276 | 0 | mac_offset = *offset - 2; |
2277 | | |
2278 | | /* |
2279 | | * msgUserName. |
2280 | | */ |
2281 | 0 | DEBUGDUMPHEADER("send", "msgUserName"); |
2282 | 0 | rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1, |
2283 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
2284 | 0 | | ASN_OCTET_STR), |
2285 | 0 | (const u_char *) theName, theNameLength); |
2286 | 0 | DEBUGINDENTLESS(); |
2287 | 0 | if (rc == 0) { |
2288 | 0 | DEBUGMSGTL(("usm", "building authParams failed.\n")); |
2289 | 0 | return SNMPERR_TOO_LONG; |
2290 | 0 | } |
2291 | | |
2292 | | /* |
2293 | | * msgAuthoritativeEngineTime. |
2294 | | */ |
2295 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineTime"); |
2296 | 0 | rc = asn_realloc_rbuild_int(wholeMsg, wholeMsgLen, offset, 1, |
2297 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
2298 | 0 | ASN_INTEGER), &time_long, |
2299 | 0 | sizeof(long)); |
2300 | 0 | DEBUGINDENTLESS(); |
2301 | 0 | if (rc == 0) { |
2302 | 0 | DEBUGMSGTL(("usm", |
2303 | 0 | "building msgAuthoritativeEngineTime failed.\n")); |
2304 | 0 | return SNMPERR_TOO_LONG; |
2305 | 0 | } |
2306 | | |
2307 | | /* |
2308 | | * msgAuthoritativeEngineBoots. |
2309 | | */ |
2310 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineBoots"); |
2311 | 0 | rc = asn_realloc_rbuild_int(wholeMsg, wholeMsgLen, offset, 1, |
2312 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
2313 | 0 | ASN_INTEGER), &boots_long, |
2314 | 0 | sizeof(long)); |
2315 | 0 | DEBUGINDENTLESS(); |
2316 | 0 | if (rc == 0) { |
2317 | 0 | DEBUGMSGTL(("usm", |
2318 | 0 | "building msgAuthoritativeEngineBoots failed.\n")); |
2319 | 0 | return SNMPERR_TOO_LONG; |
2320 | 0 | } |
2321 | | |
2322 | 0 | DEBUGDUMPHEADER("send", "msgAuthoritativeEngineID"); |
2323 | 0 | rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1, |
2324 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
2325 | 0 | | ASN_OCTET_STR), theEngineID, |
2326 | 0 | theEngineIDLength); |
2327 | 0 | DEBUGINDENTLESS(); |
2328 | 0 | if (rc == 0) { |
2329 | 0 | DEBUGMSGTL(("usm", "building msgAuthoritativeEngineID failed.\n")); |
2330 | 0 | return SNMPERR_TOO_LONG; |
2331 | 0 | } |
2332 | | |
2333 | | /* |
2334 | | * USM msgSecurityParameters sequence header |
2335 | | */ |
2336 | 0 | rc = asn_realloc_rbuild_sequence(wholeMsg, wholeMsgLen, offset, 1, |
2337 | 0 | (u_char) (ASN_SEQUENCE | |
2338 | 0 | ASN_CONSTRUCTOR), |
2339 | 0 | *offset - sp_offset); |
2340 | 0 | if (rc == 0) { |
2341 | 0 | DEBUGMSGTL(("usm", "building usm security parameters failed.\n")); |
2342 | 0 | return SNMPERR_TOO_LONG; |
2343 | 0 | } |
2344 | | |
2345 | | /* |
2346 | | * msgSecurityParameters OCTET STRING wrapper. |
2347 | | */ |
2348 | 0 | rc = asn_realloc_rbuild_header(wholeMsg, wholeMsgLen, offset, 1, |
2349 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
2350 | 0 | | ASN_OCTET_STR), |
2351 | 0 | *offset - sp_offset); |
2352 | |
|
2353 | 0 | if (rc == 0) { |
2354 | 0 | DEBUGMSGTL(("usm", "building msgSecurityParameters failed.\n")); |
2355 | 0 | return SNMPERR_TOO_LONG; |
2356 | 0 | } |
2357 | | |
2358 | | /* |
2359 | | * Copy in the msgGlobalData and msgVersion. |
2360 | | */ |
2361 | 0 | while ((*wholeMsgLen - *offset) < globalDataLen) { |
2362 | 0 | if (!asn_realloc(wholeMsg, wholeMsgLen)) { |
2363 | 0 | DEBUGMSGTL(("usm", "building global data failed.\n")); |
2364 | 0 | return SNMPERR_TOO_LONG; |
2365 | 0 | } |
2366 | 0 | } |
2367 | | |
2368 | 0 | *offset += globalDataLen; |
2369 | 0 | memcpy(*wholeMsg + *wholeMsgLen - *offset, globalData, globalDataLen); |
2370 | | |
2371 | | /* |
2372 | | * Total packet sequence. |
2373 | | */ |
2374 | 0 | rc = asn_realloc_rbuild_sequence(wholeMsg, wholeMsgLen, offset, 1, |
2375 | 0 | (u_char) (ASN_SEQUENCE | |
2376 | 0 | ASN_CONSTRUCTOR), *offset); |
2377 | 0 | if (rc == 0) { |
2378 | 0 | DEBUGMSGTL(("usm", "building master packet sequence failed.\n")); |
2379 | 0 | return SNMPERR_TOO_LONG; |
2380 | 0 | } |
2381 | | |
2382 | | /* |
2383 | | * Now consider / do authentication. |
2384 | | */ |
2385 | | |
2386 | 0 | if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || |
2387 | 0 | theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
2388 | 0 | size_t temp_sig_len = msgAuthParmLen; |
2389 | 0 | u_char *temp_sig = (u_char *) malloc(temp_sig_len); |
2390 | 0 | u_char *proto_msg = *wholeMsg + *wholeMsgLen - *offset; |
2391 | 0 | size_t proto_msg_len = *offset; |
2392 | | |
2393 | |
|
2394 | 0 | if (temp_sig == NULL) { |
2395 | 0 | DEBUGMSGTL(("usm", "Out of memory.\n")); |
2396 | 0 | return SNMPERR_USM_GENERICERROR; |
2397 | 0 | } |
2398 | | |
2399 | 0 | if (sc_generate_keyed_hash(theAuthProtocol, theAuthProtocolLength, |
2400 | 0 | theAuthKey, theAuthKeyLength, |
2401 | 0 | proto_msg, proto_msg_len, |
2402 | 0 | temp_sig, &temp_sig_len) |
2403 | 0 | != SNMP_ERR_NOERROR) { |
2404 | 0 | SNMP_FREE(temp_sig); |
2405 | 0 | DEBUGMSGTL(("usm", "Signing failed.\n")); |
2406 | 0 | return SNMPERR_USM_AUTHENTICATIONFAILURE; |
2407 | 0 | } |
2408 | | |
2409 | 0 | if (temp_sig_len != msgAuthParmLen) { |
2410 | 0 | SNMP_FREE(temp_sig); |
2411 | 0 | DEBUGMSGTL(("usm", "Signing lengths failed.\n")); |
2412 | 0 | return SNMPERR_USM_AUTHENTICATIONFAILURE; |
2413 | 0 | } |
2414 | | |
2415 | 0 | memcpy(*wholeMsg + *wholeMsgLen - mac_offset, temp_sig, |
2416 | 0 | msgAuthParmLen); |
2417 | 0 | SNMP_FREE(temp_sig); |
2418 | 0 | } |
2419 | | /* |
2420 | | * endif -- create keyed hash |
2421 | | */ |
2422 | 0 | DEBUGMSGTL(("usm", "USM processing completed.\n")); |
2423 | 0 | return SNMPERR_SUCCESS; |
2424 | 0 | } /* end usm_rgenerate_out_msg() */ |
2425 | | |
2426 | | static int |
2427 | | usm_secmod_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms) |
2428 | 0 | { |
2429 | 0 | if (!parms) |
2430 | 0 | return SNMPERR_GENERR; |
2431 | | |
2432 | 0 | return usm_rgenerate_out_msg(parms->msgProcModel, |
2433 | 0 | parms->globalData, parms->globalDataLen, |
2434 | 0 | parms->maxMsgSize, parms->secModel, |
2435 | 0 | parms->secEngineID, parms->secEngineIDLen, |
2436 | 0 | parms->secName, parms->secNameLen, |
2437 | 0 | parms->secLevel, |
2438 | 0 | parms->scopedPdu, parms->scopedPduLen, |
2439 | 0 | parms->secStateRef, |
2440 | 0 | parms->session->sessUser, |
2441 | 0 | parms->wholeMsg, parms->wholeMsgLen, |
2442 | 0 | parms->wholeMsgOffset); |
2443 | 0 | } |
2444 | | #endif /* */ |
2445 | | |
2446 | | /*******************************************************************-o-****** |
2447 | | * usm_parse_security_parameters |
2448 | | * |
2449 | | * Parameters: |
2450 | | * (See list below...) |
2451 | | * |
2452 | | * Returns: |
2453 | | * 0 On success, |
2454 | | * -1 Otherwise. |
2455 | | * |
2456 | | * tab stop 4 |
2457 | | * |
2458 | | * Extracts values from the security header and data portions of the |
2459 | | * incoming buffer. |
2460 | | */ |
2461 | | static int |
2462 | | usm_parse_security_parameters(u_char * secParams, |
2463 | | size_t remaining, |
2464 | | u_char * secEngineID, |
2465 | | size_t * secEngineIDLen, |
2466 | | u_int * boots_uint, |
2467 | | u_int * time_uint, |
2468 | | char *secName, |
2469 | | size_t * secNameLen, |
2470 | | u_char * signature, |
2471 | | size_t * signature_length, |
2472 | | u_char * salt, |
2473 | | size_t * salt_length, u_char ** data_ptr) |
2474 | 0 | { |
2475 | 0 | u_char *parse_ptr = secParams; |
2476 | 0 | u_char *value_ptr; |
2477 | 0 | u_char *next_ptr; |
2478 | 0 | u_char type_value; |
2479 | |
|
2480 | 0 | size_t octet_string_length = remaining; |
2481 | 0 | size_t sequence_length; |
2482 | 0 | size_t remaining_bytes; |
2483 | |
|
2484 | 0 | long boots_long; |
2485 | 0 | long time_long; |
2486 | |
|
2487 | 0 | u_int origNameLen; |
2488 | | |
2489 | | |
2490 | | /* |
2491 | | * Eat the first octet header. |
2492 | | */ |
2493 | 0 | if ((value_ptr = asn_parse_sequence(parse_ptr, &octet_string_length, |
2494 | 0 | &type_value, |
2495 | 0 | (ASN_UNIVERSAL | ASN_PRIMITIVE | |
2496 | 0 | ASN_OCTET_STR), |
2497 | 0 | "usm first octet")) == NULL) { |
2498 | | /* |
2499 | | * RETURN parse error |
2500 | 0 | */ return -1; |
2501 | 0 | } |
2502 | | |
2503 | | |
2504 | | /* |
2505 | | * Eat the sequence header. |
2506 | | */ |
2507 | 0 | parse_ptr = value_ptr; |
2508 | 0 | sequence_length = octet_string_length; |
2509 | |
|
2510 | 0 | if ((value_ptr = asn_parse_sequence(parse_ptr, &sequence_length, |
2511 | 0 | &type_value, |
2512 | 0 | (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
2513 | 0 | "usm sequence")) == NULL) { |
2514 | | /* |
2515 | | * RETURN parse error |
2516 | 0 | */ return -1; |
2517 | 0 | } |
2518 | | |
2519 | | |
2520 | | /* |
2521 | | * Retrieve the engineID. |
2522 | | */ |
2523 | 0 | parse_ptr = value_ptr; |
2524 | 0 | remaining_bytes = sequence_length; |
2525 | |
|
2526 | 0 | DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineID"); |
2527 | 0 | if ((next_ptr |
2528 | 0 | = asn_parse_string(parse_ptr, &remaining_bytes, &type_value, |
2529 | 0 | secEngineID, secEngineIDLen)) == NULL) { |
2530 | 0 | DEBUGINDENTLESS(); |
2531 | | /* |
2532 | | * RETURN parse error |
2533 | 0 | */ return -1; |
2534 | 0 | } |
2535 | 0 | DEBUGINDENTLESS(); |
2536 | |
|
2537 | 0 | if (type_value != |
2538 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) { |
2539 | | /* |
2540 | | * RETURN parse error |
2541 | 0 | */ return -1; |
2542 | 0 | } |
2543 | | |
2544 | | |
2545 | | /* |
2546 | | * Retrieve the engine boots, notice switch in the way next_ptr and |
2547 | | * remaining_bytes are used (to accommodate the asn code). |
2548 | | */ |
2549 | 0 | DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineBoots"); |
2550 | 0 | if ((next_ptr = asn_parse_int(next_ptr, &remaining_bytes, &type_value, |
2551 | 0 | &boots_long, sizeof(long))) == NULL) { |
2552 | 0 | DEBUGINDENTLESS(); |
2553 | | /* |
2554 | | * RETURN parse error |
2555 | 0 | */ return -1; |
2556 | 0 | } |
2557 | 0 | DEBUGINDENTLESS(); |
2558 | |
|
2559 | 0 | if (type_value != |
2560 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER)) { |
2561 | 0 | DEBUGINDENTLESS(); |
2562 | | /* |
2563 | | * RETURN parse error |
2564 | 0 | */ return -1; |
2565 | 0 | } |
2566 | | |
2567 | 0 | *boots_uint = (u_int) boots_long; |
2568 | | |
2569 | | |
2570 | | /* |
2571 | | * Retrieve the time value. |
2572 | | */ |
2573 | 0 | DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineTime"); |
2574 | 0 | if ((next_ptr = asn_parse_int(next_ptr, &remaining_bytes, &type_value, |
2575 | 0 | &time_long, sizeof(long))) == NULL) { |
2576 | | /* |
2577 | | * RETURN parse error |
2578 | 0 | */ return -1; |
2579 | 0 | } |
2580 | 0 | DEBUGINDENTLESS(); |
2581 | |
|
2582 | 0 | if (type_value != |
2583 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER)) { |
2584 | | /* |
2585 | | * RETURN parse error |
2586 | 0 | */ return -1; |
2587 | 0 | } |
2588 | | |
2589 | 0 | *time_uint = (u_int) time_long; |
2590 | |
|
2591 | 0 | if (*boots_uint > ENGINEBOOT_MAX || *time_uint > ENGINETIME_MAX) { |
2592 | 0 | return -1; |
2593 | 0 | } |
2594 | | |
2595 | | /* |
2596 | | * Retrieve the secName. |
2597 | | */ |
2598 | 0 | origNameLen = *secNameLen; |
2599 | | |
2600 | |
|
2601 | 0 | DEBUGDUMPHEADER("recv", "msgUserName"); |
2602 | 0 | if ((next_ptr |
2603 | 0 | = asn_parse_string(next_ptr, &remaining_bytes, &type_value, |
2604 | 0 | (u_char *) secName, secNameLen)) == NULL) { |
2605 | 0 | DEBUGINDENTLESS(); |
2606 | | /* |
2607 | | * RETURN parse error |
2608 | 0 | */ return -1; |
2609 | 0 | } |
2610 | 0 | DEBUGINDENTLESS(); |
2611 | | |
2612 | | /* |
2613 | | * FIX -- doesn't this also indicate a buffer overrun? |
2614 | | */ |
2615 | 0 | if (origNameLen < *secNameLen + 1) { |
2616 | | /* |
2617 | | * RETURN parse error, but it's really a parameter error |
2618 | | */ |
2619 | 0 | return -1; |
2620 | 0 | } |
2621 | | |
2622 | 0 | if (*secNameLen > 32) { |
2623 | | /* |
2624 | | * This is a USM-specific limitation over and above the above |
2625 | | * limitation (which will probably default to the length of an |
2626 | | * SnmpAdminString, i.e. 255). See RFC 2574, sec. 2.4. |
2627 | | */ |
2628 | 0 | return -1; |
2629 | 0 | } |
2630 | | |
2631 | 0 | secName[*secNameLen] = '\0'; |
2632 | |
|
2633 | 0 | if (type_value != |
2634 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) { |
2635 | | /* |
2636 | | * RETURN parse error |
2637 | 0 | */ return -1; |
2638 | 0 | } |
2639 | | |
2640 | | |
2641 | | /* |
2642 | | * Retrieve the signature and blank it if there. |
2643 | | */ |
2644 | 0 | DEBUGDUMPHEADER("recv", "msgAuthenticationParameters"); |
2645 | 0 | if ((next_ptr |
2646 | 0 | = asn_parse_string(next_ptr, &remaining_bytes, &type_value, |
2647 | 0 | signature, signature_length)) == NULL) { |
2648 | 0 | DEBUGINDENTLESS(); |
2649 | | /* |
2650 | | * RETURN parse error |
2651 | 0 | */ return -1; |
2652 | 0 | } |
2653 | 0 | DEBUGINDENTLESS(); |
2654 | |
|
2655 | 0 | if (type_value != |
2656 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) { |
2657 | | /* |
2658 | | * RETURN parse error |
2659 | 0 | */ return -1; |
2660 | 0 | } |
2661 | | |
2662 | 0 | if (*signature_length != 0) { /* Blanking for authentication step later */ |
2663 | 0 | memset(next_ptr - (u_long) * signature_length, |
2664 | 0 | 0, *signature_length); |
2665 | 0 | } |
2666 | | |
2667 | | |
2668 | | /* |
2669 | | * Retrieve the salt. |
2670 | | * |
2671 | | * Note that the next ptr is where the data section starts. |
2672 | | */ |
2673 | 0 | DEBUGDUMPHEADER("recv", "msgPrivacyParameters"); |
2674 | 0 | if ((*data_ptr |
2675 | 0 | = asn_parse_string(next_ptr, &remaining_bytes, &type_value, |
2676 | 0 | salt, salt_length)) == NULL) { |
2677 | 0 | DEBUGINDENTLESS(); |
2678 | | /* |
2679 | | * RETURN parse error |
2680 | 0 | */ return -2; |
2681 | 0 | } |
2682 | 0 | DEBUGINDENTLESS(); |
2683 | |
|
2684 | 0 | if (type_value != |
2685 | 0 | (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) { |
2686 | | /* |
2687 | | * RETURN parse error |
2688 | 0 | */ return -2; |
2689 | 0 | } |
2690 | | |
2691 | 0 | return 0; |
2692 | |
|
2693 | 0 | } /* end usm_parse_security_parameters() */ |
2694 | | |
2695 | | |
2696 | | |
2697 | | |
2698 | | /*******************************************************************-o-****** |
2699 | | * usm_check_and_update_timeliness |
2700 | | * |
2701 | | * Parameters: |
2702 | | * *secEngineID |
2703 | | * secEngineIDen |
2704 | | * boots_uint |
2705 | | * time_uint |
2706 | | * *error |
2707 | | * |
2708 | | * Returns: |
2709 | | * 0 On success, |
2710 | | * -1 Otherwise. |
2711 | | * |
2712 | | * |
2713 | | * Performs the incoming timeliness checking and setting. |
2714 | | */ |
2715 | | static int |
2716 | | usm_check_and_update_timeliness(u_char * secEngineID, |
2717 | | size_t secEngineIDLen, |
2718 | | u_int boots_uint, |
2719 | | u_int time_uint, int *error) |
2720 | 0 | { |
2721 | 0 | u_char myID[USM_MAX_ID_LENGTH]; |
2722 | 0 | u_long myIDLength = |
2723 | 0 | snmpv3_get_engineID(myID, USM_MAX_ID_LENGTH); |
2724 | 0 | u_int myBoots; |
2725 | 0 | u_int myTime; |
2726 | | |
2727 | | |
2728 | |
|
2729 | 0 | if ((myIDLength > USM_MAX_ID_LENGTH) || (myIDLength == 0)) { |
2730 | | /* |
2731 | | * We're probably already screwed...buffer overwrite. XXX? |
2732 | | */ |
2733 | 0 | DEBUGMSGTL(("usm", "Buffer overflow.\n")); |
2734 | 0 | *error = SNMPERR_USM_GENERICERROR; |
2735 | 0 | return -1; |
2736 | 0 | } |
2737 | | |
2738 | 0 | myBoots = snmpv3_local_snmpEngineBoots(); |
2739 | 0 | myTime = snmpv3_local_snmpEngineTime(); |
2740 | | |
2741 | | |
2742 | | /* |
2743 | | * IF the time involved is local |
2744 | | * Make sure message is inside the time window |
2745 | | * ELSE |
2746 | | * IF boots is higher or boots is the same and time is higher |
2747 | | * remember this new data |
2748 | | * ELSE |
2749 | | * IF !(boots same and time within USM_TIME_WINDOW secs) |
2750 | | * Message is too old |
2751 | | * ELSE |
2752 | | * Message is ok, but don't take time |
2753 | | * ENDIF |
2754 | | * ENDIF |
2755 | | * ENDIF |
2756 | | */ |
2757 | | |
2758 | | /* |
2759 | | * This is a local reference. |
2760 | | */ |
2761 | 0 | if (secEngineIDLen == myIDLength |
2762 | 0 | && memcmp(secEngineID, myID, myIDLength) == 0) { |
2763 | 0 | u_int time_difference = myTime > time_uint ? |
2764 | 0 | myTime - time_uint : time_uint - myTime; |
2765 | |
|
2766 | 0 | if (boots_uint == ENGINEBOOT_MAX |
2767 | 0 | || boots_uint != myBoots |
2768 | 0 | || time_difference > USM_TIME_WINDOW) { |
2769 | 0 | snmp_increment_statistic(STAT_USMSTATSNOTINTIMEWINDOWS); |
2770 | |
|
2771 | 0 | DEBUGMSGTL(("usm", |
2772 | 0 | "boot_uint %u myBoots %u time_diff %u => not in time window\n", |
2773 | 0 | boots_uint, myBoots, time_difference)); |
2774 | 0 | *error = SNMPERR_USM_NOTINTIMEWINDOW; |
2775 | 0 | return -1; |
2776 | 0 | } |
2777 | | |
2778 | 0 | *error = SNMPERR_SUCCESS; |
2779 | 0 | return 0; |
2780 | 0 | } |
2781 | | |
2782 | | /* |
2783 | | * This is a remote reference. |
2784 | | */ |
2785 | 0 | else { |
2786 | 0 | u_int theirBoots, theirTime, theirLastTime; |
2787 | 0 | u_int time_difference; |
2788 | |
|
2789 | 0 | if (get_enginetime_ex(secEngineID, secEngineIDLen, |
2790 | 0 | &theirBoots, &theirTime, |
2791 | 0 | &theirLastTime, TRUE) |
2792 | 0 | != SNMPERR_SUCCESS) { |
2793 | 0 | DEBUGMSGTL(("usm", "%s\n", |
2794 | 0 | "Failed to get remote engine's times.")); |
2795 | |
|
2796 | 0 | *error = SNMPERR_USM_GENERICERROR; |
2797 | 0 | return -1; |
2798 | 0 | } |
2799 | | |
2800 | 0 | time_difference = theirTime > time_uint ? |
2801 | 0 | theirTime - time_uint : time_uint - theirTime; |
2802 | | |
2803 | | |
2804 | | /* |
2805 | | * XXX Contrary to the pseudocode: |
2806 | | * See if boots is invalid first. |
2807 | | */ |
2808 | 0 | if (theirBoots == ENGINEBOOT_MAX || theirBoots > boots_uint) { |
2809 | 0 | DEBUGMSGTL(("usm", "%s\n", "Remote boot count invalid.")); |
2810 | |
|
2811 | 0 | *error = SNMPERR_USM_NOTINTIMEWINDOW; |
2812 | 0 | return -1; |
2813 | 0 | } |
2814 | | |
2815 | | |
2816 | | /* |
2817 | | * Boots is ok, see if the boots is the same but the time |
2818 | | * is old. |
2819 | | */ |
2820 | 0 | if (theirBoots == boots_uint && time_uint < theirLastTime) { |
2821 | 0 | if (time_difference > USM_TIME_WINDOW) { |
2822 | 0 | DEBUGMSGTL(("usm", "%s\n", "Message too old.")); |
2823 | 0 | *error = SNMPERR_USM_NOTINTIMEWINDOW; |
2824 | 0 | return -1; |
2825 | 0 | } |
2826 | | |
2827 | 0 | else { /* Old, but acceptable */ |
2828 | |
|
2829 | 0 | *error = SNMPERR_SUCCESS; |
2830 | 0 | return 0; |
2831 | 0 | } |
2832 | 0 | } |
2833 | | |
2834 | | |
2835 | | /* |
2836 | | * Message is ok, either boots has been advanced, or |
2837 | | * time is greater than before with the same boots. |
2838 | | */ |
2839 | | |
2840 | 0 | if (set_enginetime(secEngineID, secEngineIDLen, |
2841 | 0 | boots_uint, time_uint, TRUE) |
2842 | 0 | != SNMPERR_SUCCESS) { |
2843 | 0 | DEBUGMSGTL(("usm", "%s\n", |
2844 | 0 | "Failed updating remote boot/time.")); |
2845 | 0 | *error = SNMPERR_USM_GENERICERROR; |
2846 | 0 | return -1; |
2847 | 0 | } |
2848 | | |
2849 | 0 | *error = SNMPERR_SUCCESS; |
2850 | 0 | return 0; /* Fresh message and time updated */ |
2851 | |
|
2852 | 0 | } /* endif -- local or remote time reference. */ |
2853 | | |
2854 | |
|
2855 | 0 | } /* end usm_check_and_update_timeliness() */ |
2856 | | |
2857 | | /*******************************************************************-o-****** |
2858 | | * usm_check_secLevel |
2859 | | * |
2860 | | * Parameters: |
2861 | | * level |
2862 | | * *user |
2863 | | * |
2864 | | * Returns: |
2865 | | * 0 On success, |
2866 | | * -1 Otherwise. |
2867 | | * |
2868 | | * Checks that a given security level is valid for a given user. |
2869 | | */ |
2870 | | static int |
2871 | | usm_check_secLevel(int level, struct usmUser *user) |
2872 | 0 | { |
2873 | |
|
2874 | 0 | if (user->userStatus != RS_ACTIVE) |
2875 | 0 | return -1; |
2876 | | |
2877 | 0 | DEBUGMSGTL(("comparex", "Comparing: %" NETSNMP_PRIo "u %" NETSNMP_PRIo "u ", |
2878 | 0 | usmNoPrivProtocol[0], usmNoPrivProtocol[1])); |
2879 | 0 | DEBUGMSGOID(("comparex", usmNoPrivProtocol, |
2880 | 0 | OID_LENGTH(usmNoPrivProtocol))); |
2881 | 0 | DEBUGMSG(("comparex", "\n")); |
2882 | 0 | if (level == SNMP_SEC_LEVEL_AUTHPRIV |
2883 | 0 | && (netsnmp_oid_equals(user->privProtocol, user->privProtocolLen, |
2884 | 0 | usmNoPrivProtocol, |
2885 | 0 | OID_LENGTH(usmNoPrivProtocol)) == |
2886 | 0 | 0)) { |
2887 | 0 | DEBUGMSGTL(("usm", "Level: %d\n", level)); |
2888 | 0 | DEBUGMSGTL(("usm", "User (%s) Auth Protocol: ", user->name)); |
2889 | 0 | DEBUGMSGOID(("usm", user->authProtocol, user->authProtocolLen)); |
2890 | 0 | DEBUGMSG(("usm", ", User Priv Protocol: ")); |
2891 | 0 | DEBUGMSGOID(("usm", user->privProtocol, user->privProtocolLen)); |
2892 | 0 | DEBUGMSG(("usm", "\n")); |
2893 | 0 | return 1; |
2894 | 0 | } |
2895 | 0 | if ((level == SNMP_SEC_LEVEL_AUTHPRIV |
2896 | 0 | || level == SNMP_SEC_LEVEL_AUTHNOPRIV) |
2897 | 0 | && |
2898 | 0 | (netsnmp_oid_equals |
2899 | 0 | (user->authProtocol, user->authProtocolLen, usmNoAuthProtocol, |
2900 | 0 | OID_LENGTH(usmNoAuthProtocol)) == 0)) { |
2901 | 0 | DEBUGMSGTL(("usm", "Level: %d\n", level)); |
2902 | 0 | DEBUGMSGTL(("usm", "User (%s) Auth Protocol: ", user->name)); |
2903 | 0 | DEBUGMSGOID(("usm", user->authProtocol, user->authProtocolLen)); |
2904 | 0 | DEBUGMSG(("usm", ", User Priv Protocol: ")); |
2905 | 0 | DEBUGMSGOID(("usm", user->privProtocol, user->privProtocolLen)); |
2906 | 0 | DEBUGMSG(("usm", "\n")); |
2907 | 0 | return 1; |
2908 | 0 | } |
2909 | | |
2910 | 0 | return 0; |
2911 | 0 | } /* end usm_check_secLevel() */ |
2912 | | |
2913 | | /*******************************************************************-o-****** |
2914 | | * usm_process_in_msg |
2915 | | * |
2916 | | * Parameters: |
2917 | | * (See list below...) |
2918 | | * |
2919 | | * Returns: |
2920 | | * SNMPERR_SUCCESS On success. |
2921 | | * SNMPERR_USM_AUTHENTICATIONFAILURE |
2922 | | * SNMPERR_USM_DECRYPTIONERROR |
2923 | | * SNMPERR_USM_GENERICERROR |
2924 | | * SNMPERR_USM_PARSEERROR |
2925 | | * SNMPERR_USM_UNKNOWNENGINEID |
2926 | | * SNMPERR_USM_PARSEERROR |
2927 | | * SNMPERR_USM_UNKNOWNSECURITYNAME |
2928 | | * SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL |
2929 | | * |
2930 | | * |
2931 | | * ASSUMES size of decrypt_buf will always be >= size of encrypted sPDU. |
2932 | | */ |
2933 | | static int |
2934 | | usm_process_in_msg(int msgProcModel, /* (UNUSED) */ |
2935 | | size_t maxMsgSize, /* IN - Used to calc maxSizeResponse. */ |
2936 | | u_char * secParams, /* IN - BER encoded securityParameters. */ |
2937 | | int secModel, /* (UNUSED) */ |
2938 | | int secLevel, /* IN - AuthNoPriv, authPriv etc. */ |
2939 | | u_char * wholeMsg, /* IN - Original v3 message. */ |
2940 | | size_t wholeMsgLen, /* IN - Msg length. */ |
2941 | | u_char * secEngineID, /* OUT - Pointer snmpEngineID. */ |
2942 | | size_t * secEngineIDLen, /* IN/OUT - Len available, len returned. */ |
2943 | | /* |
2944 | | * NOTE: Memory provided by caller. |
2945 | | */ |
2946 | | char *secName, /* OUT - Pointer to securityName. */ |
2947 | | size_t * secNameLen, /* IN/OUT - Len available, len returned. */ |
2948 | | u_char ** scopedPdu, /* OUT - Pointer to plaintext scopedPdu. */ |
2949 | | size_t * scopedPduLen, /* IN/OUT - Len available, len returned. */ |
2950 | | size_t * maxSizeResponse, /* OUT - Max size of Response PDU. */ |
2951 | | void **secStateRf, /* OUT - Ref to security state. */ |
2952 | | netsnmp_session * sess, /* IN - session which got the message */ |
2953 | | u_char msg_flags) |
2954 | 0 | { /* IN - v3 Message flags. */ |
2955 | 0 | size_t remaining = wholeMsgLen - (u_int) |
2956 | 0 | ((u_long) * secParams - (u_long) * wholeMsg); |
2957 | 0 | u_int boots_uint; |
2958 | 0 | u_int time_uint; |
2959 | 0 | #ifdef HAVE_AES |
2960 | 0 | u_int net_boots, net_time; |
2961 | 0 | #endif |
2962 | 0 | #ifndef NETSNMP_DISABLE_DES |
2963 | 0 | int i; |
2964 | 0 | #endif |
2965 | 0 | u_char signature[USM_MAX_AUTHSIZE]; |
2966 | 0 | size_t signature_length = USM_MAX_AUTHSIZE; |
2967 | 0 | u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)]; |
2968 | 0 | size_t salt_length = BYTESIZE(USM_MAX_SALT_LENGTH); |
2969 | 0 | u_char iv[BYTESIZE(USM_MAX_SALT_LENGTH)]; |
2970 | 0 | u_int iv_length = BYTESIZE(USM_MAX_SALT_LENGTH); |
2971 | 0 | u_char *data_ptr; |
2972 | 0 | u_char *value_ptr; |
2973 | 0 | u_char type_value; |
2974 | 0 | u_char *end_of_overhead = NULL; |
2975 | 0 | int error; |
2976 | 0 | int rc = 0; |
2977 | 0 | struct usmStateReference **secStateRef = |
2978 | 0 | (struct usmStateReference **) secStateRf; |
2979 | |
|
2980 | 0 | struct usmUser *user; |
2981 | | |
2982 | |
|
2983 | 0 | DEBUGMSGTL(("usm", "USM processing begun...\n")); |
2984 | |
|
2985 | 0 | netsnmp_assert(secStateRef); |
2986 | |
|
2987 | 0 | usm_free_usmStateReference(*secStateRef); |
2988 | 0 | *secStateRef = usm_malloc_usmStateReference(); |
2989 | 0 | if (*secStateRef == NULL) { |
2990 | 0 | DEBUGMSGTL(("usm", "Out of memory.\n")); |
2991 | 0 | return SNMPERR_USM_GENERICERROR; |
2992 | 0 | } |
2993 | | |
2994 | | /* |
2995 | | * Make sure the *secParms is an OCTET STRING. |
2996 | | * Extract the user name, engine ID, and security level. |
2997 | | */ |
2998 | 0 | if ((rc = usm_parse_security_parameters(secParams, remaining, |
2999 | 0 | secEngineID, secEngineIDLen, |
3000 | 0 | &boots_uint, &time_uint, |
3001 | 0 | secName, secNameLen, |
3002 | 0 | signature, &signature_length, |
3003 | 0 | salt, &salt_length, |
3004 | 0 | &data_ptr)) < 0) { |
3005 | 0 | DEBUGMSGTL(("usm", "Parsing failed (rc %d).\n", rc)); |
3006 | 0 | if (rc == -2) { |
3007 | | /* |
3008 | | * This indicates a decryptionError. |
3009 | | */ |
3010 | 0 | snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); |
3011 | 0 | error = SNMPERR_USM_DECRYPTIONERROR; |
3012 | 0 | } else { |
3013 | 0 | snmp_increment_statistic(STAT_SNMPINASNPARSEERRS); |
3014 | 0 | error = SNMPERR_USM_PARSEERROR; |
3015 | 0 | } |
3016 | 0 | goto err; |
3017 | 0 | } |
3018 | | |
3019 | | /* |
3020 | | * RFC 2574 section 8.3.2 |
3021 | | * 1) If the privParameters field is not an 8-octet OCTET STRING, |
3022 | | * then an error indication (decryptionError) is returned to the |
3023 | | * calling module. |
3024 | | */ |
3025 | 0 | if ((secLevel == SNMP_SEC_LEVEL_AUTHPRIV) && (salt_length != 8)) { |
3026 | 0 | snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); |
3027 | 0 | error = SNMPERR_USM_DECRYPTIONERROR; |
3028 | 0 | goto err; |
3029 | 0 | } |
3030 | | |
3031 | 0 | if (secLevel != SNMP_SEC_LEVEL_AUTHPRIV) { |
3032 | | /* |
3033 | | * pull these out now so reports can use them |
3034 | | */ |
3035 | 0 | *scopedPdu = data_ptr; |
3036 | 0 | *scopedPduLen = wholeMsgLen - (data_ptr - wholeMsg); |
3037 | 0 | end_of_overhead = data_ptr; |
3038 | 0 | } |
3039 | | |
3040 | | /* |
3041 | | * Cache the name, engine ID, and security level, |
3042 | | * * per step 2 (section 3.2) |
3043 | | */ |
3044 | 0 | if (usm_set_usmStateReference_name |
3045 | 0 | (*secStateRef, secName, *secNameLen) == -1) { |
3046 | 0 | DEBUGMSGTL(("usm", "%s\n", "Couldn't cache name.")); |
3047 | 0 | error = SNMPERR_USM_GENERICERROR; |
3048 | 0 | goto err; |
3049 | 0 | } |
3050 | | |
3051 | 0 | if (usm_set_usmStateReference_engine_id |
3052 | 0 | (*secStateRef, secEngineID, *secEngineIDLen) == -1) { |
3053 | 0 | DEBUGMSGTL(("usm", "%s\n", "Couldn't cache engine id.")); |
3054 | 0 | error = SNMPERR_USM_GENERICERROR; |
3055 | 0 | goto err; |
3056 | 0 | } |
3057 | | |
3058 | 0 | if (usm_set_usmStateReference_sec_level(*secStateRef, secLevel) == |
3059 | 0 | -1) { |
3060 | 0 | DEBUGMSGTL(("usm", "%s\n", "Couldn't cache security level.")); |
3061 | 0 | error = SNMPERR_USM_GENERICERROR; |
3062 | 0 | goto err; |
3063 | 0 | } |
3064 | | |
3065 | | /* |
3066 | | * Locate the engine ID record. |
3067 | | * If it is unknown, then either create one or note this as an error. |
3068 | | */ |
3069 | 0 | if ((sess && (sess->isAuthoritative == SNMP_SESS_AUTHORITATIVE || |
3070 | 0 | (sess->isAuthoritative == SNMP_SESS_UNKNOWNAUTH && |
3071 | 0 | (msg_flags & SNMP_MSG_FLAG_RPRT_BIT)))) || |
3072 | 0 | (!sess && (msg_flags & SNMP_MSG_FLAG_RPRT_BIT))) { |
3073 | 0 | if (ISENGINEKNOWN(secEngineID, *secEngineIDLen) == FALSE) { |
3074 | 0 | DEBUGMSGTL(("usm", "Unknown Engine ID.\n")); |
3075 | 0 | snmp_increment_statistic(STAT_USMSTATSUNKNOWNENGINEIDS); |
3076 | 0 | error = SNMPERR_USM_UNKNOWNENGINEID; |
3077 | 0 | goto err; |
3078 | 0 | } |
3079 | 0 | } else { |
3080 | 0 | if (ENSURE_ENGINE_RECORD(secEngineID, *secEngineIDLen) |
3081 | 0 | != SNMPERR_SUCCESS) { |
3082 | 0 | DEBUGMSGTL(("usm", "%s\n", "Couldn't ensure engine record.")); |
3083 | 0 | error = SNMPERR_USM_GENERICERROR; |
3084 | 0 | goto err; |
3085 | 0 | } |
3086 | |
|
3087 | 0 | } |
3088 | | |
3089 | | |
3090 | | /* |
3091 | | * Locate the User record. |
3092 | | * If the user/engine ID is unknown, report this as an error. |
3093 | | */ |
3094 | 0 | if (sess && sess->sessUser) |
3095 | 0 | user = sess->sessUser; |
3096 | 0 | else |
3097 | 0 | user = usm_get_user_from_list(secEngineID, *secEngineIDLen, |
3098 | 0 | secName, *secNameLen, userList, |
3099 | 0 | (((sess && sess->isAuthoritative == |
3100 | 0 | SNMP_SESS_AUTHORITATIVE) || |
3101 | 0 | (!sess)) ? 0 : 1)); |
3102 | |
|
3103 | 0 | if (user == NULL) { |
3104 | 0 | DEBUGMSGTL(("usm", "Unknown User(%s)\n", secName)); |
3105 | 0 | snmp_increment_statistic(STAT_USMSTATSUNKNOWNUSERNAMES); |
3106 | 0 | error = SNMPERR_USM_UNKNOWNSECURITYNAME; |
3107 | 0 | goto err; |
3108 | 0 | } |
3109 | | |
3110 | | /* ensure the user is active */ |
3111 | 0 | if (user->userStatus != RS_ACTIVE) { |
3112 | 0 | DEBUGMSGTL(("usm", "Attempt to use an inactive user.\n")); |
3113 | 0 | error = SNMPERR_USM_UNKNOWNSECURITYNAME; |
3114 | 0 | goto err; |
3115 | 0 | } |
3116 | | |
3117 | | /* |
3118 | | * Make sure the security level is appropriate. |
3119 | | */ |
3120 | | |
3121 | 0 | rc = usm_check_secLevel(secLevel, user); |
3122 | 0 | if (1 == rc) { |
3123 | 0 | DEBUGMSGTL(("usm", "Unsupported Security Level (%d).\n", |
3124 | 0 | secLevel)); |
3125 | 0 | snmp_increment_statistic(STAT_USMSTATSUNSUPPORTEDSECLEVELS); |
3126 | 0 | error = SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL; |
3127 | 0 | goto err; |
3128 | 0 | } else if (rc != 0) { |
3129 | 0 | DEBUGMSGTL(("usm", "Unknown issue.\n")); |
3130 | 0 | error = SNMPERR_USM_GENERICERROR; |
3131 | 0 | goto err; |
3132 | 0 | } |
3133 | | |
3134 | | /* |
3135 | | * Check the authentication credentials of the message. |
3136 | | */ |
3137 | 0 | if (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
3138 | 0 | || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
3139 | 0 | if (sc_check_keyed_hash(user->authProtocol, user->authProtocolLen, |
3140 | 0 | user->authKey, user->authKeyLen, |
3141 | 0 | wholeMsg, wholeMsgLen, |
3142 | 0 | signature, signature_length) |
3143 | 0 | != SNMP_ERR_NOERROR) { |
3144 | 0 | DEBUGMSGTL(("usm", "Verification failed.\n")); |
3145 | 0 | snmp_increment_statistic(STAT_USMSTATSWRONGDIGESTS); |
3146 | 0 | snmp_log(LOG_WARNING, "Authentication failed for %s\n", |
3147 | 0 | user->name); |
3148 | 0 | error = SNMPERR_USM_AUTHENTICATIONFAILURE; |
3149 | 0 | goto err; |
3150 | 0 | } |
3151 | | |
3152 | 0 | DEBUGMSGTL(("usm", "Verification succeeded.\n")); |
3153 | 0 | } |
3154 | | |
3155 | | |
3156 | | /* |
3157 | | * Steps 10-11 user is already set - relocated before timeliness |
3158 | | * check in case it fails - still save user data for response. |
3159 | | * |
3160 | | * Cache the keys and protocol oids, per step 11 (s3.2). |
3161 | | */ |
3162 | 0 | if (usm_set_usmStateReference_auth_protocol(*secStateRef, |
3163 | 0 | user->authProtocol, |
3164 | 0 | user-> |
3165 | 0 | authProtocolLen) == -1) { |
3166 | 0 | DEBUGMSGTL(("usm", "%s\n", |
3167 | 0 | "Couldn't cache authentication protocol.")); |
3168 | 0 | error = SNMPERR_USM_GENERICERROR; |
3169 | 0 | goto err; |
3170 | 0 | } |
3171 | | |
3172 | 0 | if (usm_set_usmStateReference_auth_key(*secStateRef, |
3173 | 0 | user->authKey, |
3174 | 0 | user->authKeyLen) == -1) { |
3175 | 0 | DEBUGMSGTL(("usm", "%s\n", |
3176 | 0 | "Couldn't cache authentication key.")); |
3177 | 0 | error = SNMPERR_USM_GENERICERROR; |
3178 | 0 | goto err; |
3179 | 0 | } |
3180 | | |
3181 | 0 | if (usm_set_usmStateReference_priv_protocol(*secStateRef, |
3182 | 0 | user->privProtocol, |
3183 | 0 | user-> |
3184 | 0 | privProtocolLen) == -1) { |
3185 | 0 | DEBUGMSGTL(("usm", "%s\n", |
3186 | 0 | "Couldn't cache privacy protocol.")); |
3187 | 0 | error = SNMPERR_USM_GENERICERROR; |
3188 | 0 | goto err; |
3189 | 0 | } |
3190 | | |
3191 | 0 | if (usm_set_usmStateReference_priv_key(*secStateRef, |
3192 | 0 | user->privKey, |
3193 | 0 | user->privKeyLen) == -1) { |
3194 | 0 | DEBUGMSGTL(("usm", "%s\n", "Couldn't cache privacy key.")); |
3195 | 0 | error = SNMPERR_USM_GENERICERROR; |
3196 | 0 | goto err; |
3197 | 0 | } |
3198 | | |
3199 | | |
3200 | | /* |
3201 | | * Perform the timeliness/time manager functions. |
3202 | | */ |
3203 | 0 | if (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV |
3204 | 0 | || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
3205 | 0 | if (usm_check_and_update_timeliness(secEngineID, *secEngineIDLen, |
3206 | 0 | boots_uint, time_uint, |
3207 | 0 | &error) == -1) { |
3208 | 0 | goto err; |
3209 | 0 | } |
3210 | 0 | } |
3211 | 0 | #ifdef LCD_TIME_SYNC_OPT |
3212 | | /* |
3213 | | * Cache the unauthenticated time to use in case we don't have |
3214 | | * anything better - this guess will be no worse than (0,0) |
3215 | | * that we normally use. |
3216 | | */ |
3217 | 0 | else { |
3218 | 0 | set_enginetime(secEngineID, *secEngineIDLen, |
3219 | 0 | boots_uint, time_uint, FALSE); |
3220 | 0 | } |
3221 | 0 | #endif /* LCD_TIME_SYNC_OPT */ |
3222 | | |
3223 | | |
3224 | | /* |
3225 | | * If needed, decrypt the scoped PDU. |
3226 | | */ |
3227 | 0 | if (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { |
3228 | 0 | int priv_type = sc_get_privtype(user->privProtocol, |
3229 | 0 | user->privProtocolLen); |
3230 | 0 | remaining = wholeMsgLen - (data_ptr - wholeMsg); |
3231 | |
|
3232 | 0 | if ((value_ptr = asn_parse_sequence(data_ptr, &remaining, |
3233 | 0 | &type_value, |
3234 | 0 | (ASN_UNIVERSAL | ASN_PRIMITIVE |
3235 | 0 | | ASN_OCTET_STR), |
3236 | 0 | "encrypted sPDU")) == NULL) { |
3237 | 0 | DEBUGMSGTL(("usm", "%s\n", |
3238 | 0 | "Failed while parsing encrypted sPDU.")); |
3239 | 0 | snmp_increment_statistic(STAT_SNMPINASNPARSEERRS); |
3240 | 0 | usm_free_usmStateReference(*secStateRef); |
3241 | 0 | *secStateRef = NULL; |
3242 | 0 | error = SNMPERR_USM_PARSEERROR; |
3243 | 0 | goto err; |
3244 | 0 | } |
3245 | | |
3246 | 0 | #ifndef NETSNMP_DISABLE_DES |
3247 | 0 | if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { |
3248 | | /* |
3249 | | * From RFC2574: |
3250 | | * |
3251 | | * "Before decryption, the encrypted data length is verified. |
3252 | | * If the length of the OCTET STRING to be decrypted is not |
3253 | | * an integral multiple of 8 octets, the decryption process |
3254 | | * is halted and an appropriate exception noted." |
3255 | | */ |
3256 | |
|
3257 | 0 | if (remaining % 8 != 0) { |
3258 | 0 | DEBUGMSGTL(("usm", |
3259 | 0 | "Ciphertext is %lu bytes, not an integer multiple of 8 (rem %lu)\n", |
3260 | 0 | (unsigned long)remaining, (unsigned long)remaining % 8)); |
3261 | 0 | snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); |
3262 | 0 | usm_free_usmStateReference(*secStateRef); |
3263 | 0 | *secStateRef = NULL; |
3264 | 0 | error = SNMPERR_USM_DECRYPTIONERROR; |
3265 | 0 | goto err; |
3266 | 0 | } |
3267 | | |
3268 | 0 | end_of_overhead = value_ptr; |
3269 | |
|
3270 | 0 | if ( !user->privKey ) { |
3271 | 0 | DEBUGMSGTL(("usm", "No privacy pass phrase for %s\n", user->secName)); |
3272 | 0 | snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); |
3273 | 0 | usm_free_usmStateReference(*secStateRef); |
3274 | 0 | *secStateRef = NULL; |
3275 | 0 | error = SNMPERR_USM_DECRYPTIONERROR; |
3276 | 0 | goto err; |
3277 | 0 | } |
3278 | | |
3279 | | /* |
3280 | | * XOR the salt with the last (iv_length) bytes |
3281 | | * of the priv_key to obtain the IV. |
3282 | | */ |
3283 | 0 | iv_length = BYTESIZE(USM_DES_SALT_LENGTH); |
3284 | 0 | for (i = 0; i < (int) iv_length; i++) |
3285 | 0 | iv[i] = salt[i] ^ user->privKey[iv_length + i]; |
3286 | 0 | } |
3287 | 0 | #endif |
3288 | 0 | #ifdef HAVE_AES |
3289 | 0 | if (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { |
3290 | 0 | iv_length = BYTESIZE(USM_AES_SALT_LENGTH); |
3291 | 0 | net_boots = ntohl(boots_uint); |
3292 | 0 | net_time = ntohl(time_uint); |
3293 | 0 | memcpy(iv, &net_boots, 4); |
3294 | 0 | memcpy(iv+4, &net_time, 4); |
3295 | 0 | memcpy(iv+8, salt, salt_length); |
3296 | 0 | } |
3297 | 0 | #endif |
3298 | |
|
3299 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
3300 | | if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { |
3301 | | dump_chunk("usm/dump", "Cypher Text", value_ptr, remaining); |
3302 | | dump_chunk("usm/dump", "salt + Encrypted form:", |
3303 | | salt, salt_length); |
3304 | | dump_chunk("usm/dump", "IV + Encrypted form:", iv, iv_length); |
3305 | | } |
3306 | | #endif |
3307 | 0 | if (sc_decrypt(user->privProtocol, user->privProtocolLen, |
3308 | 0 | user->privKey, user->privKeyLen, |
3309 | 0 | iv, iv_length, |
3310 | 0 | value_ptr, remaining, *scopedPdu, scopedPduLen) |
3311 | 0 | != SNMP_ERR_NOERROR) { |
3312 | 0 | DEBUGMSGTL(("usm", "%s\n", "Failed decryption.")); |
3313 | 0 | snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); |
3314 | 0 | error = SNMPERR_USM_DECRYPTIONERROR; |
3315 | 0 | goto err; |
3316 | 0 | } |
3317 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
3318 | | if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { |
3319 | | dump_chunk("usm/dump", "Decrypted chunk:", |
3320 | | *scopedPdu, *scopedPduLen); |
3321 | | } |
3322 | | #endif |
3323 | 0 | } |
3324 | | /* |
3325 | | * sPDU is plaintext. |
3326 | | */ |
3327 | 0 | else { |
3328 | 0 | *scopedPdu = data_ptr; |
3329 | 0 | *scopedPduLen = wholeMsgLen - (data_ptr - wholeMsg); |
3330 | 0 | end_of_overhead = data_ptr; |
3331 | |
|
3332 | 0 | } /* endif -- PDU decryption */ |
3333 | | |
3334 | | |
3335 | | /* |
3336 | | * Calculate the biggest sPDU for the response (i.e., whole - ovrhd). |
3337 | | * |
3338 | | * FIX Correct? |
3339 | | */ |
3340 | 0 | *maxSizeResponse = maxMsgSize - (end_of_overhead - wholeMsg); |
3341 | | |
3342 | |
|
3343 | 0 | DEBUGMSGTL(("usm", "USM processing completed.\n")); |
3344 | |
|
3345 | 0 | return SNMPERR_SUCCESS; |
3346 | | |
3347 | 0 | err: |
3348 | 0 | usm_free_usmStateReference(*secStateRef); |
3349 | 0 | *secStateRef = NULL; |
3350 | 0 | netsnmp_assert(error != SNMPERR_SUCCESS); |
3351 | 0 | return error; |
3352 | 0 | } /* end usm_process_in_msg() */ |
3353 | | |
3354 | | static int |
3355 | | usm_secmod_process_in_msg(struct snmp_secmod_incoming_params *parms) |
3356 | 0 | { |
3357 | 0 | if (!parms) |
3358 | 0 | return SNMPERR_GENERR; |
3359 | | |
3360 | 0 | return usm_process_in_msg(parms->msgProcModel, |
3361 | 0 | parms->maxMsgSize, |
3362 | 0 | parms->secParams, |
3363 | 0 | parms->secModel, |
3364 | 0 | parms->secLevel, |
3365 | 0 | parms->wholeMsg, |
3366 | 0 | parms->wholeMsgLen, |
3367 | 0 | parms->secEngineID, |
3368 | 0 | parms->secEngineIDLen, |
3369 | 0 | parms->secName, |
3370 | 0 | parms->secNameLen, |
3371 | 0 | parms->scopedPdu, |
3372 | 0 | parms->scopedPduLen, |
3373 | 0 | parms->maxSizeResponse, |
3374 | 0 | parms->secStateRef, |
3375 | 0 | parms->sess, parms->msg_flags); |
3376 | 0 | } |
3377 | | |
3378 | | static void |
3379 | | usm_handle_report(struct session_list *slp, |
3380 | | netsnmp_transport *transport, netsnmp_session *session, |
3381 | | int result, netsnmp_pdu *pdu) |
3382 | 0 | { |
3383 | | /* |
3384 | | * handle reportable errors |
3385 | | */ |
3386 | | |
3387 | | /* this will get in our way */ |
3388 | 0 | usm_free_usmStateReference(pdu->securityStateRef); |
3389 | 0 | pdu->securityStateRef = NULL; |
3390 | |
|
3391 | 0 | switch (result) { |
3392 | 0 | case SNMPERR_USM_AUTHENTICATIONFAILURE: |
3393 | 0 | { |
3394 | 0 | int res = session->s_snmp_errno; |
3395 | 0 | session->s_snmp_errno = result; |
3396 | 0 | if (session->callback) { |
3397 | 0 | session->callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE, |
3398 | 0 | session, pdu->reqid, pdu, |
3399 | 0 | session->callback_magic); |
3400 | 0 | } |
3401 | 0 | session->s_snmp_errno = res; |
3402 | 0 | } |
3403 | 0 | NETSNMP_FALLTHROUGH; |
3404 | 0 | case SNMPERR_USM_UNKNOWNENGINEID: |
3405 | 0 | case SNMPERR_USM_UNKNOWNSECURITYNAME: |
3406 | 0 | case SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL: |
3407 | 0 | case SNMPERR_USM_NOTINTIMEWINDOW: |
3408 | 0 | case SNMPERR_USM_DECRYPTIONERROR: |
3409 | |
|
3410 | 0 | if (SNMP_CMD_CONFIRMED(pdu->command) || |
3411 | 0 | (pdu->command == 0 |
3412 | 0 | && (pdu->flags & SNMP_MSG_FLAG_RPRT_BIT))) { |
3413 | 0 | netsnmp_pdu *pdu2; |
3414 | 0 | int flags = pdu->flags; |
3415 | |
|
3416 | 0 | pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY; |
3417 | 0 | pdu2 = snmp_clone_pdu(pdu); |
3418 | 0 | pdu->flags = pdu2->flags = flags; |
3419 | 0 | snmpv3_make_report(pdu2, result); |
3420 | 0 | if (0 == snmp_sess_send(slp, pdu2)) { |
3421 | 0 | snmp_free_pdu(pdu2); |
3422 | | /* |
3423 | | * TODO: indicate error |
3424 | | */ |
3425 | 0 | } |
3426 | 0 | } |
3427 | 0 | break; |
3428 | 0 | } |
3429 | 0 | } |
3430 | | |
3431 | | /** utility function to call netsnmp_extend_kul for a usmUser */ |
3432 | | int |
3433 | | usm_extend_user_kul(struct usmUser *user, u_int privKeyBufSize) |
3434 | 0 | { |
3435 | 0 | const netsnmp_priv_alg_info *pai; |
3436 | |
|
3437 | 0 | DEBUGMSGTL(("usm", "extending key\n")); |
3438 | |
|
3439 | 0 | if (NULL == user) { |
3440 | 0 | DEBUGMSGTL(("usm", "null user!\n")); |
3441 | 0 | return SNMPERR_GENERR; |
3442 | 0 | } |
3443 | | |
3444 | 0 | pai = sc_get_priv_alg_byoid(user->privProtocol, user->privProtocolLen); |
3445 | 0 | if (NULL == pai) { |
3446 | 0 | DEBUGMSGTL(("usm", "privProtocol lookup failed!\n")); |
3447 | 0 | return SNMPERR_GENERR; |
3448 | 0 | } |
3449 | | |
3450 | 0 | return netsnmp_extend_kul(pai->proper_length, user->authProtocol, |
3451 | 0 | user->authProtocolLen, pai->type, user->engineID, |
3452 | 0 | user->engineIDLen, &user->privKey, |
3453 | 0 | &user->privKeyLen, privKeyBufSize); |
3454 | 0 | } |
3455 | | |
3456 | | /* sets up initial default session parameters */ |
3457 | | static int |
3458 | | usm_session_init(netsnmp_session *in_session, netsnmp_session *session) |
3459 | 4.38k | { |
3460 | 4.38k | char *cp; |
3461 | 4.38k | size_t i; |
3462 | | |
3463 | 4.38k | if (in_session->securityAuthProtoLen > 0) { |
3464 | 0 | session->securityAuthProto = |
3465 | 0 | snmp_duplicate_objid(in_session->securityAuthProto, |
3466 | 0 | in_session->securityAuthProtoLen); |
3467 | 0 | if (session->securityAuthProto == NULL) { |
3468 | 0 | in_session->s_snmp_errno = SNMPERR_MALLOC; |
3469 | 0 | return SNMPERR_MALLOC; |
3470 | 0 | } |
3471 | 4.38k | } else if (get_default_authtype(&i) != NULL) { |
3472 | 4.38k | session->securityAuthProto = |
3473 | 4.38k | snmp_duplicate_objid(get_default_authtype(NULL), i); |
3474 | 4.38k | session->securityAuthProtoLen = i; |
3475 | 4.38k | } |
3476 | | |
3477 | 4.38k | if (in_session->securityPrivProtoLen > 0) { |
3478 | 0 | session->securityPrivProto = |
3479 | 0 | snmp_duplicate_objid(in_session->securityPrivProto, |
3480 | 0 | in_session->securityPrivProtoLen); |
3481 | 0 | if (session->securityPrivProto == NULL) { |
3482 | 0 | in_session->s_snmp_errno = SNMPERR_MALLOC; |
3483 | 0 | return SNMPERR_MALLOC; |
3484 | 0 | } |
3485 | 4.38k | } else if (get_default_privtype(&i) != NULL) { |
3486 | 4.38k | session->securityPrivProto = |
3487 | 4.38k | snmp_duplicate_objid(get_default_privtype(NULL), i); |
3488 | 4.38k | session->securityPrivProtoLen = i; |
3489 | 4.38k | } |
3490 | | |
3491 | 4.38k | if ((in_session->securityAuthKeyLen <= 0) && |
3492 | 4.38k | ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3493 | 4.38k | NETSNMP_DS_LIB_AUTHMASTERKEY)))) { |
3494 | 0 | size_t buflen = sizeof(session->securityAuthKey); |
3495 | 0 | u_char *tmpp = session->securityAuthKey; |
3496 | 0 | session->securityAuthKeyLen = 0; |
3497 | | /* it will be a hex string */ |
3498 | 0 | if (!snmp_hex_to_binary(&tmpp, &buflen, |
3499 | 0 | &session->securityAuthKeyLen, 0, cp)) { |
3500 | 0 | snmp_set_detail("error parsing authentication master key"); |
3501 | 0 | return SNMP_ERR_GENERR; |
3502 | 0 | } |
3503 | 4.38k | } else if ((in_session->securityAuthKeyLen <= 0) && |
3504 | 4.38k | ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3505 | 4.38k | NETSNMP_DS_LIB_AUTHPASSPHRASE)) || |
3506 | 4.38k | (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3507 | 4.38k | NETSNMP_DS_LIB_PASSPHRASE)))) { |
3508 | 0 | session->securityAuthKeyLen = USM_AUTH_KU_LEN; |
3509 | 0 | if (generate_Ku(session->securityAuthProto, |
3510 | 0 | session->securityAuthProtoLen, |
3511 | 0 | (u_char *) cp, strlen(cp), |
3512 | 0 | session->securityAuthKey, |
3513 | 0 | &session->securityAuthKeyLen) != SNMPERR_SUCCESS) { |
3514 | 0 | snmp_set_detail |
3515 | 0 | ("Error generating a key (Ku) from the supplied authentication pass phrase."); |
3516 | 0 | return SNMP_ERR_GENERR; |
3517 | 0 | } |
3518 | 0 | } |
3519 | | |
3520 | | |
3521 | 4.38k | if ((in_session->securityPrivKeyLen <= 0) && |
3522 | 4.38k | ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3523 | 4.38k | NETSNMP_DS_LIB_PRIVMASTERKEY)))) { |
3524 | 0 | size_t buflen = sizeof(session->securityPrivKey); |
3525 | 0 | u_char *tmpp = session->securityPrivKey; |
3526 | 0 | session->securityPrivKeyLen = 0; |
3527 | | /* it will be a hex string */ |
3528 | 0 | if (!snmp_hex_to_binary(&tmpp, &buflen, |
3529 | 0 | &session->securityPrivKeyLen, 0, cp)) { |
3530 | 0 | snmp_set_detail("error parsing encryption master key"); |
3531 | 0 | return SNMP_ERR_GENERR; |
3532 | 0 | } |
3533 | 4.38k | } else if ((in_session->securityPrivKeyLen <= 0) && |
3534 | 4.38k | ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3535 | 4.38k | NETSNMP_DS_LIB_PRIVPASSPHRASE)) || |
3536 | 4.38k | (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3537 | 4.38k | NETSNMP_DS_LIB_PASSPHRASE)))) { |
3538 | 0 | session->securityPrivKeyLen = USM_PRIV_KU_LEN; |
3539 | 0 | if (generate_Ku(session->securityAuthProto, |
3540 | 0 | session->securityAuthProtoLen, |
3541 | 0 | (u_char *) cp, strlen(cp), |
3542 | 0 | session->securityPrivKey, |
3543 | 0 | &session->securityPrivKeyLen) != SNMPERR_SUCCESS) { |
3544 | 0 | snmp_set_detail |
3545 | 0 | ("Error generating a key (Ku) from the supplied privacy pass phrase."); |
3546 | 0 | return SNMP_ERR_GENERR; |
3547 | 0 | } |
3548 | 0 | } |
3549 | | |
3550 | 4.38k | return SNMPERR_SUCCESS; |
3551 | 4.38k | } |
3552 | | |
3553 | | static int usm_build_user(struct usmUser **result, |
3554 | | const netsnmp_session *session) |
3555 | 0 | { |
3556 | 0 | struct usmUser *user; |
3557 | |
|
3558 | 0 | DEBUGMSGTL(("usm", "Building user %s...\n", session->securityName)); |
3559 | | /* |
3560 | | * user doesn't exist so we create and add it |
3561 | | */ |
3562 | 0 | user = calloc(1, sizeof(struct usmUser)); |
3563 | 0 | if (user == NULL) |
3564 | 0 | goto err; |
3565 | | |
3566 | | /* |
3567 | | * copy in the securityName |
3568 | | */ |
3569 | 0 | if (session->securityName) { |
3570 | 0 | user->name = strdup(session->securityName); |
3571 | 0 | user->secName = strdup(session->securityName); |
3572 | 0 | if (user->name == NULL || user->secName == NULL) |
3573 | 0 | goto err; |
3574 | 0 | } |
3575 | | |
3576 | | /* |
3577 | | * copy in the engineID |
3578 | | */ |
3579 | 0 | user->engineID = netsnmp_memdup(session->securityEngineID, |
3580 | 0 | session->securityEngineIDLen); |
3581 | 0 | if (session->securityEngineID && !user->engineID) |
3582 | 0 | goto err; |
3583 | 0 | user->engineIDLen = session->securityEngineIDLen; |
3584 | 0 | *result = user; |
3585 | 0 | return SNMPERR_SUCCESS; |
3586 | | |
3587 | 0 | err: |
3588 | 0 | usm_free_user(user); |
3589 | 0 | return SNMPERR_GENERR; |
3590 | 0 | } |
3591 | | |
3592 | | /* |
3593 | | * usm_create_user_from_session(netsnmp_session *session): |
3594 | | * |
3595 | | * creates a user in the usm table from the information in a session. |
3596 | | * If the user already exists, it is updated with the current |
3597 | | * information from the session, also update boot/time set |
3598 | | * |
3599 | | * Parameters: |
3600 | | * session -- IN: pointer to the session to use when creating the user. |
3601 | | * |
3602 | | * Returns: |
3603 | | * SNMPERR_SUCCESS |
3604 | | * SNMPERR_GENERR |
3605 | | */ |
3606 | | int |
3607 | | usm_create_user_from_session(netsnmp_session * session) |
3608 | 1.76k | { |
3609 | 1.76k | struct usmUser *user; |
3610 | 1.76k | int user_just_created = 0; |
3611 | 1.76k | char *cp; |
3612 | | |
3613 | | /* |
3614 | | * If boot/time supplied set it for this engineID. Do it from hook when |
3615 | | * creating user in case probe was sent by other means for example |
3616 | | * asynchronously. |
3617 | | */ |
3618 | 1.76k | if (!(session->flags & SNMP_FLAGS_TIME_CREATED) && |
3619 | 1.76k | (session->engineBoots || session->engineTime)) { |
3620 | 0 | set_enginetime(session->securityEngineID, |
3621 | 0 | session->securityEngineIDLen, |
3622 | 0 | session->engineBoots, session->engineTime, |
3623 | 0 | TRUE); |
3624 | 0 | session->flags |= SNMP_FLAGS_TIME_CREATED; |
3625 | 0 | } |
3626 | | |
3627 | | /* |
3628 | | * - don't create-another/copy-into user for this session by default |
3629 | | * - bail now (no error) if we don't have an engineID |
3630 | | */ |
3631 | 1.76k | if (SNMP_FLAGS_USER_CREATED == (session->flags & SNMP_FLAGS_USER_CREATED) || |
3632 | 1.76k | session->securityModel != SNMP_SEC_MODEL_USM || |
3633 | 1.76k | session->version != SNMP_VERSION_3 || |
3634 | 1.76k | session->securityNameLen == 0 || |
3635 | 1.76k | session->securityEngineIDLen == 0) |
3636 | 1.76k | return SNMPERR_SUCCESS; |
3637 | | |
3638 | 0 | DEBUGMSGTL(("usm", "no flag defined... continuing\n")); |
3639 | 0 | session->flags |= SNMP_FLAGS_USER_CREATED; |
3640 | | |
3641 | | /* |
3642 | | * now that we have the engineID, create an entry in the USM list |
3643 | | * for this user using the information in the session |
3644 | | */ |
3645 | 0 | if ((session->flags & SNMP_FLAGS_SESSION_USER) == 0) |
3646 | 0 | user = usm_get_user_from_list(session->securityEngineID, |
3647 | 0 | session->securityEngineIDLen, |
3648 | 0 | session->securityName, |
3649 | 0 | session->securityNameLen, |
3650 | 0 | usm_get_userList(), 0); |
3651 | 0 | else |
3652 | 0 | user = NULL; |
3653 | | |
3654 | |
|
3655 | 0 | if (user) { |
3656 | 0 | DEBUGMSGTL(("usm", "user exists x=%p\n", user)); |
3657 | 0 | } else { |
3658 | 0 | if (usm_build_user(&user, session) != SNMPERR_SUCCESS) |
3659 | 0 | return SNMPERR_GENERR; |
3660 | 0 | user_just_created = 1; |
3661 | 0 | } |
3662 | | |
3663 | | /* |
3664 | | * copy the auth protocol |
3665 | | */ |
3666 | 0 | if (user->authProtocol == NULL && session->securityAuthProto != NULL) { |
3667 | 0 | SNMP_FREE(user->authProtocol); |
3668 | 0 | user->authProtocol = |
3669 | 0 | snmp_duplicate_objid(session->securityAuthProto, |
3670 | 0 | session->securityAuthProtoLen); |
3671 | 0 | if (user->authProtocol == NULL) { |
3672 | 0 | usm_free_user(user); |
3673 | 0 | return SNMPERR_GENERR; |
3674 | 0 | } |
3675 | 0 | user->authProtocolLen = session->securityAuthProtoLen; |
3676 | 0 | } |
3677 | | |
3678 | | /* |
3679 | | * copy the priv protocol |
3680 | | */ |
3681 | 0 | if (user->privProtocol == NULL && session->securityPrivProto != NULL) { |
3682 | 0 | SNMP_FREE(user->privProtocol); |
3683 | 0 | user->privProtocol = |
3684 | 0 | snmp_duplicate_objid(session->securityPrivProto, |
3685 | 0 | session->securityPrivProtoLen); |
3686 | 0 | if (user->privProtocol == NULL) { |
3687 | 0 | usm_free_user(user); |
3688 | 0 | return SNMPERR_GENERR; |
3689 | 0 | } |
3690 | 0 | user->privProtocolLen = session->securityPrivProtoLen; |
3691 | 0 | } |
3692 | | |
3693 | | /* |
3694 | | * copy in the authentication Key. If not localized, localize it |
3695 | | */ |
3696 | 0 | if (user->authKey == NULL) { |
3697 | 0 | if (session->securityAuthLocalKey != NULL |
3698 | 0 | && session->securityAuthLocalKeyLen != 0) { |
3699 | | /* already localized key passed in. use it */ |
3700 | 0 | SNMP_FREE(user->authKey); |
3701 | 0 | user->authKey = netsnmp_memdup(session->securityAuthLocalKey, |
3702 | 0 | session->securityAuthLocalKeyLen); |
3703 | 0 | if (!user->authKey) { |
3704 | 0 | usm_free_user(user); |
3705 | 0 | return SNMPERR_GENERR; |
3706 | 0 | } |
3707 | 0 | user->authKeyLen = session->securityAuthLocalKeyLen; |
3708 | 0 | } else if (session->securityAuthKeyLen != 0) { |
3709 | 0 | SNMP_FREE(user->authKey); |
3710 | 0 | user->authKey = calloc(1, USM_LENGTH_KU_HASHBLOCK); |
3711 | 0 | user->authKeyLen = USM_LENGTH_KU_HASHBLOCK; |
3712 | 0 | if ((user->authKey == NULL) || |
3713 | 0 | generate_kul(user->authProtocol, user->authProtocolLen, |
3714 | 0 | user->engineID, user->engineIDLen, |
3715 | 0 | session->securityAuthKey, |
3716 | 0 | session->securityAuthKeyLen, user->authKey, |
3717 | 0 | &user->authKeyLen) != SNMPERR_SUCCESS) { |
3718 | 0 | usm_free_user(user); |
3719 | 0 | return SNMPERR_GENERR; |
3720 | 0 | } |
3721 | 0 | } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3722 | 0 | NETSNMP_DS_LIB_AUTHLOCALIZEDKEY))) { |
3723 | 0 | size_t buflen = USM_AUTH_KU_LEN; |
3724 | 0 | SNMP_FREE(user->authKey); |
3725 | 0 | user->authKey = (u_char *)malloc(buflen); /* max length needed */ |
3726 | 0 | user->authKeyLen = 0; |
3727 | | /* it will be a hex string */ |
3728 | 0 | if ((NULL == user->authKey) || |
3729 | 0 | !snmp_hex_to_binary(&user->authKey, &buflen, &user->authKeyLen, |
3730 | 0 | 0, cp)) { |
3731 | 0 | usm_free_user(user); |
3732 | 0 | return SNMPERR_GENERR; |
3733 | 0 | } |
3734 | 0 | } |
3735 | 0 | } |
3736 | | |
3737 | | /* |
3738 | | * copy in the privacy Key. If not localized, localize it |
3739 | | */ |
3740 | 0 | if (user->privKey == NULL) { |
3741 | | /** save buffer size in case we need to extend key */ |
3742 | 0 | int keyBufSize = USM_PRIV_KU_LEN; |
3743 | |
|
3744 | 0 | DEBUGMSGTL(("usm", "copying privKey\n")); |
3745 | 0 | if (session->securityPrivLocalKey != NULL |
3746 | 0 | && session->securityPrivLocalKeyLen != 0) { |
3747 | | /* already localized key passed in. use it */ |
3748 | 0 | SNMP_FREE(user->privKey); |
3749 | 0 | user->privKey = netsnmp_memdup(session->securityPrivLocalKey, |
3750 | 0 | session->securityPrivLocalKeyLen); |
3751 | 0 | if (!user->privKey) { |
3752 | 0 | usm_free_user(user); |
3753 | 0 | return SNMPERR_GENERR; |
3754 | 0 | } |
3755 | 0 | keyBufSize = user->privKeyLen = session->securityPrivLocalKeyLen; |
3756 | 0 | } else if (session->securityPrivKeyLen != 0) { |
3757 | 0 | SNMP_FREE(user->privKey); |
3758 | 0 | user->privKey = calloc(1, keyBufSize); |
3759 | 0 | user->privKeyLen = keyBufSize; |
3760 | 0 | if ((user->privKey == NULL) || |
3761 | 0 | generate_kul(user->authProtocol, user->authProtocolLen, |
3762 | 0 | user->engineID, user->engineIDLen, |
3763 | 0 | session->securityPrivKey, |
3764 | 0 | session->securityPrivKeyLen, user->privKey, |
3765 | 0 | &user->privKeyLen) != SNMPERR_SUCCESS) { |
3766 | 0 | usm_free_user(user); |
3767 | 0 | return SNMPERR_GENERR; |
3768 | 0 | } |
3769 | 0 | } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
3770 | 0 | NETSNMP_DS_LIB_PRIVLOCALIZEDKEY))) { |
3771 | 0 | size_t buflen = keyBufSize; |
3772 | 0 | user->privKey = (u_char *)malloc(buflen); /* max length needed */ |
3773 | 0 | user->privKeyLen = 0; |
3774 | | /* it will be a hex string */ |
3775 | 0 | if ((NULL == user->privKey) || |
3776 | 0 | !snmp_hex_to_binary(&user->privKey, &buflen, &user->privKeyLen, |
3777 | 0 | 0, cp)) { |
3778 | 0 | usm_free_user(user); |
3779 | 0 | return SNMPERR_GENERR; |
3780 | 0 | } |
3781 | 0 | } |
3782 | 0 | if (usm_extend_user_kul(user, keyBufSize) != SNMPERR_SUCCESS) { |
3783 | 0 | usm_free_user(user); |
3784 | 0 | return SNMPERR_GENERR; |
3785 | 0 | } |
3786 | 0 | } |
3787 | | |
3788 | 0 | if (user_just_created) { |
3789 | | /* |
3790 | | * add the user into the database |
3791 | | */ |
3792 | 0 | user->userStatus = RS_ACTIVE; |
3793 | 0 | user->userStorageType = ST_READONLY; |
3794 | |
|
3795 | 0 | if (session->flags & SNMP_FLAGS_SESSION_USER) { |
3796 | 0 | if (session->sessUser) |
3797 | 0 | usm_free_user(session->sessUser); |
3798 | 0 | session->sessUser = user; |
3799 | 0 | } else { |
3800 | 0 | usm_add_user(user); |
3801 | 0 | } |
3802 | 0 | } |
3803 | 0 | DEBUGMSGTL(("9:usm", "user created and stored in %s\n", session->flags & |
3804 | 0 | SNMP_FLAGS_SESSION_USER ? "session" : "local store")); |
3805 | |
|
3806 | 0 | return SNMPERR_SUCCESS; |
3807 | | |
3808 | |
|
3809 | 0 | } |
3810 | | |
3811 | | /* A wrapper around the hook */ |
3812 | | static int |
3813 | | usm_create_user_from_session_hook(struct session_list *slp, |
3814 | | netsnmp_session *session) |
3815 | 0 | { |
3816 | 0 | DEBUGMSGTL(("usm", "potentially bootstrapping the USM table from session data\n")); |
3817 | 0 | return usm_create_user_from_session(session); |
3818 | 0 | } |
3819 | | |
3820 | | static int |
3821 | | usm_build_probe_pdu(netsnmp_pdu **pdu, struct usmUser **sessUser) |
3822 | 0 | { |
3823 | 0 | struct usmUser *user; |
3824 | | |
3825 | | /* |
3826 | | * create the pdu |
3827 | | */ |
3828 | 0 | if (!pdu) |
3829 | 0 | return -1; |
3830 | 0 | *pdu = snmpv3_probe_usm_pdu_create(); |
3831 | 0 | if (!(*pdu)) |
3832 | 0 | return -1; |
3833 | | |
3834 | | /* |
3835 | | * create the empty user |
3836 | | */ |
3837 | 0 | if (sessUser && *sessUser) |
3838 | 0 | user = *sessUser; |
3839 | 0 | else |
3840 | 0 | user = usm_get_user2(NULL, 0, (*pdu)->securityName, |
3841 | 0 | (*pdu)->securityNameLen); |
3842 | 0 | if (user == NULL) { |
3843 | 0 | user = calloc(1, sizeof(struct usmUser)); |
3844 | 0 | if (user == NULL) { |
3845 | 0 | snmp_free_pdu(*pdu); |
3846 | 0 | *pdu = (netsnmp_pdu *) NULL; |
3847 | 0 | return -1; |
3848 | 0 | } |
3849 | 0 | user->name = strdup((*pdu)->securityName); |
3850 | 0 | user->secName = strdup((*pdu)->securityName); |
3851 | 0 | user->authProtocolLen = OID_LENGTH(usmNoAuthProtocol); |
3852 | 0 | user->authProtocol = |
3853 | 0 | snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen); |
3854 | 0 | user->privProtocolLen = OID_LENGTH(usmNoPrivProtocol); |
3855 | 0 | user->privProtocol = |
3856 | 0 | snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen); |
3857 | 0 | if (sessUser) |
3858 | 0 | *sessUser = user; |
3859 | 0 | else |
3860 | 0 | usm_add_user(user); |
3861 | 0 | } |
3862 | 0 | return 0; |
3863 | 0 | } |
3864 | | |
3865 | | static int usm_discover_engineid(struct session_list *slp, |
3866 | | netsnmp_session *session) |
3867 | 0 | { |
3868 | 0 | netsnmp_pdu *pdu = NULL, *response = NULL; |
3869 | 0 | struct usmUser **user; |
3870 | 0 | int status, i; |
3871 | |
|
3872 | 0 | user = session->flags & SNMP_FLAGS_SESSION_USER ? &session->sessUser : NULL; |
3873 | 0 | if (usm_build_probe_pdu(&pdu, user) != 0) { |
3874 | 0 | DEBUGMSGTL(("snmp_api", "unable to create probe PDU\n")); |
3875 | 0 | return SNMP_ERR_GENERR; |
3876 | 0 | } |
3877 | 0 | DEBUGMSGTL(("snmp_api", "probing for engineID...\n")); |
3878 | 0 | session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */ |
3879 | 0 | status = snmp_sess_synch_response(slp, pdu, &response); |
3880 | |
|
3881 | 0 | if ((response == NULL) && (status == STAT_SUCCESS)) { |
3882 | 0 | status = STAT_ERROR; |
3883 | 0 | } |
3884 | |
|
3885 | 0 | switch (status) { |
3886 | 0 | case STAT_SUCCESS: |
3887 | 0 | session->s_snmp_errno = SNMPERR_INVALID_MSG; /* XX?? */ |
3888 | 0 | DEBUGMSGTL(("snmp_sess_open", |
3889 | 0 | "error: expected Report as response to probe: %s (%ld)\n", |
3890 | 0 | snmp_errstring(response->errstat), |
3891 | 0 | response->errstat)); |
3892 | 0 | break; |
3893 | 0 | case STAT_ERROR: /* this is what we expected -> Report == STAT_ERROR */ |
3894 | 0 | session->s_snmp_errno = SNMPERR_UNKNOWN_ENG_ID; |
3895 | 0 | break; |
3896 | 0 | case STAT_TIMEOUT: |
3897 | 0 | session->s_snmp_errno = SNMPERR_TIMEOUT; |
3898 | 0 | break; |
3899 | 0 | default: |
3900 | 0 | DEBUGMSGTL(("snmp_sess_open", |
3901 | 0 | "unable to connect with remote engine: %s (%d)\n", |
3902 | 0 | snmp_api_errstring(session->s_snmp_errno), |
3903 | 0 | session->s_snmp_errno)); |
3904 | 0 | break; |
3905 | 0 | } |
3906 | | |
3907 | 0 | if (slp->session->securityEngineIDLen == 0) { |
3908 | 0 | DEBUGMSGTL(("snmp_api", |
3909 | 0 | "unable to determine remote engine ID\n")); |
3910 | | /* clear the flag so that probe occurs on next inform */ |
3911 | 0 | session->flags &= ~SNMP_FLAGS_DONT_PROBE; |
3912 | 0 | return SNMP_ERR_GENERR; |
3913 | 0 | } |
3914 | | |
3915 | 0 | session->s_snmp_errno = SNMPERR_SUCCESS; |
3916 | 0 | if (snmp_get_do_debugging()) { |
3917 | 0 | DEBUGMSGTL(("snmp_sess_open", |
3918 | 0 | " probe found engineID: ")); |
3919 | 0 | for (i = 0; i < slp->session->securityEngineIDLen; i++) |
3920 | 0 | DEBUGMSG(("snmp_sess_open", "%02x", |
3921 | 0 | slp->session->securityEngineID[i])); |
3922 | 0 | DEBUGMSG(("snmp_sess_open", "\n")); |
3923 | 0 | } |
3924 | | |
3925 | | /* |
3926 | | * if boot/time supplied set it for this engineID |
3927 | | */ |
3928 | 0 | if (session->engineBoots || session->engineTime) { |
3929 | 0 | set_enginetime(session->securityEngineID, |
3930 | 0 | session->securityEngineIDLen, |
3931 | 0 | session->engineBoots, session->engineTime, |
3932 | 0 | TRUE); |
3933 | 0 | session->flags |= SNMP_FLAGS_TIME_CREATED; |
3934 | 0 | } |
3935 | 0 | return SNMPERR_SUCCESS; |
3936 | 0 | } |
3937 | | |
3938 | | static int |
3939 | | usm_lookup_alg_type(const char *str, const usm_alg_type_t *types) |
3940 | 0 | { |
3941 | 0 | int i, l; |
3942 | 0 | l = strlen(str); |
3943 | 0 | for (i = 0; types[i].label; ++i) { |
3944 | 0 | if (0 == strncasecmp(types[i].label, str, l)) |
3945 | 0 | return types[i].value; |
3946 | 0 | } |
3947 | | |
3948 | 0 | return -1; |
3949 | 0 | } |
3950 | | |
3951 | | static const char * |
3952 | | usm_lookup_alg_str(int value, const usm_alg_type_t *types) |
3953 | 0 | { |
3954 | 0 | int i; |
3955 | 0 | for (i = 0; types[i].label; ++i) |
3956 | 0 | if (value == types[i].value) |
3957 | 0 | return types[i].label; |
3958 | | |
3959 | 0 | return NULL; |
3960 | 0 | } |
3961 | | |
3962 | | int |
3963 | | usm_lookup_auth_type(const char *str) |
3964 | 0 | { |
3965 | 0 | return usm_lookup_alg_type(str, usm_auth_type ); |
3966 | 0 | } |
3967 | | |
3968 | | int |
3969 | | usm_lookup_priv_type(const char *str) |
3970 | 0 | { |
3971 | 0 | return usm_lookup_alg_type(str, usm_priv_type ); |
3972 | 0 | } |
3973 | | |
3974 | | const char * |
3975 | | usm_lookup_auth_str(int value) |
3976 | 0 | { |
3977 | 0 | return usm_lookup_alg_str(value, usm_auth_type ); |
3978 | 0 | } |
3979 | | |
3980 | | const char * |
3981 | | usm_lookup_priv_str(int value) |
3982 | 0 | { |
3983 | 0 | return usm_lookup_alg_str(value, usm_priv_type ); |
3984 | 0 | } |
3985 | | |
3986 | | static void |
3987 | | clear_user_list(void) |
3988 | 5.25k | { |
3989 | 5.25k | struct usmUser *tmp = userList, *next = NULL; |
3990 | | |
3991 | 5.25k | while (tmp != NULL) { |
3992 | 0 | next = tmp->next; |
3993 | 0 | usm_free_user(tmp); |
3994 | 0 | tmp = next; |
3995 | 0 | } |
3996 | 5.25k | userList = NULL; |
3997 | | |
3998 | 5.25k | } |
3999 | | |
4000 | | #ifndef NETSNMP_NO_WRITE_SUPPORT |
4001 | | /* |
4002 | | * take a given user and clone the security info into another |
4003 | | */ |
4004 | | struct usmUser * |
4005 | | usm_cloneFrom_user(struct usmUser *from, struct usmUser *to) |
4006 | 0 | { |
4007 | 0 | to->flags = from->flags; |
4008 | | |
4009 | | /* |
4010 | | * copy the authProtocol oid row pointer |
4011 | | */ |
4012 | 0 | SNMP_FREE(to->authProtocol); |
4013 | |
|
4014 | 0 | if ((to->authProtocol = |
4015 | 0 | snmp_duplicate_objid(from->authProtocol, |
4016 | 0 | from->authProtocolLen)) != NULL) |
4017 | 0 | to->authProtocolLen = from->authProtocolLen; |
4018 | 0 | else |
4019 | 0 | to->authProtocolLen = 0; |
4020 | | |
4021 | | |
4022 | | /* |
4023 | | * copy the authKey |
4024 | | */ |
4025 | 0 | SNMP_FREE(to->authKey); |
4026 | |
|
4027 | 0 | if (from->authKeyLen > 0 && |
4028 | 0 | (to->authKey = (u_char *) malloc(from->authKeyLen)) |
4029 | 0 | != NULL) { |
4030 | 0 | to->authKeyLen = from->authKeyLen; |
4031 | 0 | memcpy(to->authKey, from->authKey, to->authKeyLen); |
4032 | 0 | } else { |
4033 | 0 | to->authKey = NULL; |
4034 | 0 | to->authKeyLen = 0; |
4035 | 0 | } |
4036 | | |
4037 | | /* |
4038 | | * copy the authKeyKu |
4039 | | */ |
4040 | 0 | SNMP_FREE(to->authKeyKu); |
4041 | |
|
4042 | 0 | if (from->authKeyKuLen > 0 && |
4043 | 0 | (to->authKeyKu = (u_char *) malloc(from->authKeyKuLen)) != NULL) { |
4044 | 0 | to->authKeyKuLen = from->authKeyKuLen; |
4045 | 0 | memcpy(to->authKeyKu, from->authKeyKu, to->authKeyKuLen); |
4046 | 0 | } else { |
4047 | 0 | to->authKeyKu = NULL; |
4048 | 0 | to->authKeyKuLen = 0; |
4049 | 0 | } |
4050 | | |
4051 | | |
4052 | | /* |
4053 | | * copy the privProtocol oid row pointer |
4054 | | */ |
4055 | 0 | SNMP_FREE(to->privProtocol); |
4056 | |
|
4057 | 0 | if ((to->privProtocol = |
4058 | 0 | snmp_duplicate_objid(from->privProtocol, |
4059 | 0 | from->privProtocolLen)) != NULL) |
4060 | 0 | to->privProtocolLen = from->privProtocolLen; |
4061 | 0 | else |
4062 | 0 | to->privProtocolLen = 0; |
4063 | | |
4064 | | /* |
4065 | | * copy the privKey |
4066 | | */ |
4067 | 0 | SNMP_FREE(to->privKey); |
4068 | |
|
4069 | 0 | if (from->privKeyLen > 0 && |
4070 | 0 | (to->privKey = (u_char *) malloc(from->privKeyLen)) |
4071 | 0 | != NULL) { |
4072 | 0 | to->privKeyLen = from->privKeyLen; |
4073 | 0 | memcpy(to->privKey, from->privKey, to->privKeyLen); |
4074 | 0 | } else { |
4075 | 0 | to->privKey = NULL; |
4076 | 0 | to->privKeyLen = 0; |
4077 | 0 | } |
4078 | | |
4079 | | /* |
4080 | | * copy the privKeyKu |
4081 | | */ |
4082 | 0 | SNMP_FREE(to->privKeyKu); |
4083 | 0 | if (from->privKeyKuLen > 0 && |
4084 | 0 | (to->privKeyKu = (u_char *) malloc(from->privKeyKuLen)) != NULL) { |
4085 | 0 | to->privKeyKuLen = from->privKeyKuLen; |
4086 | 0 | memcpy(to->privKeyKu, from->privKeyKu, to->privKeyKuLen); |
4087 | 0 | } else { |
4088 | 0 | to->privKeyKu = NULL; |
4089 | 0 | to->privKeyKuLen = 0; |
4090 | 0 | } |
4091 | 0 | return to; |
4092 | 0 | } |
4093 | | #endif /* NETSNMP_NO_WRITE_SUPPORT */ |
4094 | | |
4095 | | /* |
4096 | | * usm_create_user(void): |
4097 | | * create a default empty user, instantiating only the auth/priv |
4098 | | * protocols to noAuth and noPriv OID pointers |
4099 | | */ |
4100 | | struct usmUser * |
4101 | | usm_create_user(void) |
4102 | 2.62k | { |
4103 | 2.62k | struct usmUser *newUser; |
4104 | | |
4105 | | /* |
4106 | | * create the new user |
4107 | | */ |
4108 | 2.62k | newUser = calloc(1, sizeof(struct usmUser)); |
4109 | 2.62k | if (newUser == NULL) |
4110 | 0 | return NULL; |
4111 | | |
4112 | | /* |
4113 | | * fill the auth/priv protocols |
4114 | | */ |
4115 | 2.62k | if ((newUser->authProtocol = |
4116 | 2.62k | snmp_duplicate_objid(usmNoAuthProtocol, |
4117 | 2.62k | OID_LENGTH(usmNoAuthProtocol))) == |
4118 | 2.62k | NULL) |
4119 | 0 | return usm_free_user(newUser); |
4120 | 2.62k | newUser->authProtocolLen = OID_LENGTH(usmNoAuthProtocol); |
4121 | | |
4122 | 2.62k | if ((newUser->privProtocol = |
4123 | 2.62k | snmp_duplicate_objid(usmNoPrivProtocol, |
4124 | 2.62k | OID_LENGTH(usmNoPrivProtocol))) == |
4125 | 2.62k | NULL) |
4126 | 0 | return usm_free_user(newUser); |
4127 | 2.62k | newUser->privProtocolLen = OID_LENGTH(usmNoPrivProtocol); |
4128 | | |
4129 | | /* |
4130 | | * set the storage type to nonvolatile, and the status to ACTIVE |
4131 | | */ |
4132 | 2.62k | newUser->userStorageType = ST_NONVOLATILE; |
4133 | 2.62k | newUser->userStatus = RS_ACTIVE; |
4134 | 2.62k | return newUser; |
4135 | | |
4136 | 2.62k | } /* end usm_clone_user() */ |
4137 | | |
4138 | | /* |
4139 | | * usm_create_initial_user(void): |
4140 | | * creates an initial user, filled with the defaults defined in the |
4141 | | * USM document. |
4142 | | */ |
4143 | | static struct usmUser * |
4144 | | usm_create_initial_user(const char *name, |
4145 | | const oid * authProtocol, size_t authProtocolLen, |
4146 | | const oid * privProtocol, size_t privProtocolLen) |
4147 | 2.62k | { |
4148 | 2.62k | struct usmUser *newUser = usm_create_user(); |
4149 | 2.62k | if (newUser == NULL) |
4150 | 0 | return NULL; |
4151 | | |
4152 | 2.62k | if ((newUser->name = strdup(name)) == NULL) |
4153 | 0 | return usm_free_user(newUser); |
4154 | | |
4155 | 2.62k | if ((newUser->secName = strdup(name)) == NULL) |
4156 | 0 | return usm_free_user(newUser); |
4157 | | |
4158 | 2.62k | if ((newUser->engineID = |
4159 | 2.62k | snmpv3_generate_engineID(&newUser->engineIDLen)) == NULL) |
4160 | 0 | return usm_free_user(newUser); |
4161 | | |
4162 | 2.62k | if ((newUser->cloneFrom = (oid *) malloc(sizeof(oid) * 2)) == NULL) |
4163 | 0 | return usm_free_user(newUser); |
4164 | 2.62k | newUser->cloneFrom[0] = 0; |
4165 | 2.62k | newUser->cloneFrom[1] = 0; |
4166 | 2.62k | newUser->cloneFromLen = 2; |
4167 | | |
4168 | 2.62k | SNMP_FREE(newUser->privProtocol); |
4169 | 2.62k | if ((newUser->privProtocol = snmp_duplicate_objid(privProtocol, |
4170 | 2.62k | privProtocolLen)) == |
4171 | 2.62k | NULL) { |
4172 | 0 | return usm_free_user(newUser); |
4173 | 0 | } |
4174 | 2.62k | newUser->privProtocolLen = privProtocolLen; |
4175 | | |
4176 | 2.62k | SNMP_FREE(newUser->authProtocol); |
4177 | 2.62k | if ((newUser->authProtocol = snmp_duplicate_objid(authProtocol, |
4178 | 2.62k | authProtocolLen)) == |
4179 | 2.62k | NULL) { |
4180 | 0 | return usm_free_user(newUser); |
4181 | 0 | } |
4182 | 2.62k | newUser->authProtocolLen = authProtocolLen; |
4183 | | |
4184 | 2.62k | newUser->userStatus = RS_ACTIVE; |
4185 | 2.62k | newUser->userStorageType = ST_READONLY; |
4186 | | |
4187 | 2.62k | return newUser; |
4188 | 2.62k | } |
4189 | | |
4190 | | /* |
4191 | | * usm_save_user(): saves a user to the persistent cache |
4192 | | */ |
4193 | | static void |
4194 | | usm_save_user(struct usmUser *user, const char *token, const char *type) |
4195 | 0 | { |
4196 | 0 | char line[4096]; |
4197 | 0 | char *cptr; |
4198 | |
|
4199 | 0 | memset(line, 0, sizeof(line)); |
4200 | |
|
4201 | 0 | sprintf(line, "%s %d %d ", token, user->userStatus, |
4202 | 0 | user->userStorageType); |
4203 | 0 | cptr = &line[strlen(line)]; /* the NULL */ |
4204 | 0 | cptr = |
4205 | 0 | read_config_save_octet_string(cptr, user->engineID, |
4206 | 0 | user->engineIDLen); |
4207 | 0 | *cptr++ = ' '; |
4208 | 0 | cptr = read_config_save_octet_string(cptr, (u_char *) user->name, |
4209 | 0 | (user->name == NULL) ? 0 : |
4210 | 0 | strlen(user->name)); |
4211 | 0 | *cptr++ = ' '; |
4212 | 0 | cptr = read_config_save_octet_string(cptr, (u_char *) user->secName, |
4213 | 0 | (user->secName == NULL) ? 0 : |
4214 | 0 | strlen(user->secName)); |
4215 | 0 | *cptr++ = ' '; |
4216 | 0 | cptr = |
4217 | 0 | read_config_save_objid(cptr, user->cloneFrom, user->cloneFromLen); |
4218 | 0 | *cptr++ = ' '; |
4219 | 0 | cptr = read_config_save_objid(cptr, user->authProtocol, |
4220 | 0 | user->authProtocolLen); |
4221 | 0 | *cptr++ = ' '; |
4222 | 0 | cptr = |
4223 | 0 | read_config_save_octet_string(cptr, user->authKey, |
4224 | 0 | user->authKeyLen); |
4225 | 0 | *cptr++ = ' '; |
4226 | 0 | cptr = read_config_save_objid(cptr, user->privProtocol, |
4227 | 0 | user->privProtocolLen); |
4228 | 0 | *cptr++ = ' '; |
4229 | 0 | cptr = |
4230 | 0 | read_config_save_octet_string(cptr, user->privKey, |
4231 | 0 | user->privKeyLen); |
4232 | 0 | *cptr++ = ' '; |
4233 | 0 | cptr = read_config_save_octet_string(cptr, user->userPublicString, |
4234 | 0 | user->userPublicStringLen); |
4235 | |
|
4236 | 0 | read_config_store(type, line); |
4237 | 0 | } |
4238 | | |
4239 | | static void |
4240 | | usm_save_users_from_list(struct usmUser *puserList, const char *token, |
4241 | | const char *type) |
4242 | 2.62k | { |
4243 | 2.62k | struct usmUser *uptr; |
4244 | 2.62k | for (uptr = puserList; uptr != NULL; uptr = uptr->next) { |
4245 | 0 | if (uptr->userStorageType == ST_NONVOLATILE) |
4246 | 0 | usm_save_user(uptr, token, type); |
4247 | 0 | } |
4248 | 2.62k | } |
4249 | | |
4250 | | /* |
4251 | | * usm_save_users(): saves a list of users to the persistent cache |
4252 | | */ |
4253 | | static void |
4254 | | usm_save_users(const char *token, const char *type) |
4255 | 2.62k | { |
4256 | 2.62k | usm_save_users_from_list(userList, token, type); |
4257 | 2.62k | } |
4258 | | |
4259 | | /* |
4260 | | * this is a callback that can store all known users based on a |
4261 | | * previously registered application ID |
4262 | | */ |
4263 | | static int |
4264 | | usm_store_users(int majorID, int minorID, void *serverarg, void *clientarg) |
4265 | 2.62k | { |
4266 | | /* |
4267 | | * figure out our application name |
4268 | | */ |
4269 | 2.62k | char *appname = (char *) clientarg; |
4270 | 2.62k | if (appname == NULL) { |
4271 | 2.62k | appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
4272 | 2.62k | NETSNMP_DS_LIB_APPTYPE); |
4273 | 2.62k | } |
4274 | | |
4275 | | /* |
4276 | | * save the user base |
4277 | | */ |
4278 | 2.62k | usm_save_users("usmUser", appname); |
4279 | | |
4280 | | /* |
4281 | | * never fails |
4282 | | */ |
4283 | 2.62k | return SNMPERR_SUCCESS; |
4284 | 2.62k | } |
4285 | | |
4286 | | /** |
4287 | | * usm_read_user(): reads in a line containing a saved user profile |
4288 | | * and returns a pointer to a newly created struct usmUser. |
4289 | | * |
4290 | | * @param[in] line Line of text containing a saved user profile |
4291 | | * |
4292 | | * @return A pointer to the newly-created struct usmUser if |
4293 | | * parsing succeeded; NULL if an error occurred. |
4294 | | */ |
4295 | | static struct usmUser * |
4296 | | usm_read_user(const char *line) |
4297 | 0 | { |
4298 | 0 | struct usmUser *user; |
4299 | 0 | size_t len, proper_length, privtype; |
4300 | |
|
4301 | 0 | user = usm_create_user(); |
4302 | 0 | if (user == NULL) |
4303 | 0 | return NULL; |
4304 | | |
4305 | 0 | user->userStatus = atoi(line); |
4306 | 0 | line = skip_token_const(line); |
4307 | 0 | if (line == NULL) { |
4308 | 0 | DEBUGMSGTL(("usm", "Configuration line missing userStorageType\n")); |
4309 | 0 | usm_free_user(user); |
4310 | 0 | return NULL; |
4311 | 0 | } |
4312 | | |
4313 | 0 | user->userStorageType = atoi(line); |
4314 | 0 | line = skip_token_const(line); |
4315 | 0 | if (line == NULL) { |
4316 | 0 | DEBUGMSGTL(("usm", "Configuration line missing engineID\n")); |
4317 | 0 | usm_free_user(user); |
4318 | 0 | return NULL; |
4319 | 0 | } |
4320 | 0 | line = read_config_read_octet_string_const(line, &user->engineID, |
4321 | 0 | &user->engineIDLen); |
4322 | |
|
4323 | 0 | if (line == NULL) { |
4324 | 0 | DEBUGMSGTL(("usm", "Configuration line missing name\n")); |
4325 | 0 | usm_free_user(user); |
4326 | 0 | return NULL; |
4327 | 0 | } |
4328 | 0 | line = read_config_read_octet_string(line, (u_char **) & user->name, |
4329 | 0 | &len); |
4330 | 0 | if (line == NULL) { |
4331 | 0 | DEBUGMSGTL(("usm", "Configuration line missing security name\n")); |
4332 | 0 | usm_free_user(user); |
4333 | 0 | return NULL; |
4334 | 0 | } |
4335 | 0 | line = read_config_read_octet_string(line, (u_char **) & user->secName, |
4336 | 0 | &len); |
4337 | 0 | SNMP_FREE(user->cloneFrom); |
4338 | 0 | user->cloneFromLen = 0; |
4339 | |
|
4340 | 0 | if (line == NULL) { |
4341 | 0 | DEBUGMSGTL(("usm", "Configuration line missing clone from\n")); |
4342 | 0 | usm_free_user(user); |
4343 | 0 | return NULL; |
4344 | 0 | } |
4345 | 0 | line = read_config_read_objid_const(line, &user->cloneFrom, |
4346 | 0 | &user->cloneFromLen); |
4347 | |
|
4348 | 0 | SNMP_FREE(user->authProtocol); |
4349 | 0 | user->authProtocolLen = 0; |
4350 | |
|
4351 | 0 | if (line == NULL) { |
4352 | 0 | DEBUGMSGTL(("usm", "Configuration line missing authentication protocol\n")); |
4353 | 0 | usm_free_user(user); |
4354 | 0 | return NULL; |
4355 | 0 | } |
4356 | 0 | line = read_config_read_objid_const(line, &user->authProtocol, |
4357 | 0 | &user->authProtocolLen); |
4358 | |
|
4359 | 0 | if (line == NULL) { |
4360 | 0 | DEBUGMSGTL(("usm", "Configuration line missing authentication key\n")); |
4361 | 0 | usm_free_user(user); |
4362 | 0 | return NULL; |
4363 | 0 | } |
4364 | 0 | line = read_config_read_octet_string_const(line, &user->authKey, |
4365 | 0 | &user->authKeyLen); |
4366 | 0 | SNMP_FREE(user->privProtocol); |
4367 | 0 | user->privProtocolLen = 0; |
4368 | |
|
4369 | 0 | if (line == NULL) { |
4370 | 0 | DEBUGMSGTL(("usm", "Configuration line missing privacy protocol\n")); |
4371 | 0 | usm_free_user(user); |
4372 | 0 | return NULL; |
4373 | 0 | } |
4374 | 0 | line = read_config_read_objid_const(line, &user->privProtocol, |
4375 | 0 | &user->privProtocolLen); |
4376 | |
|
4377 | 0 | if (line == NULL) { |
4378 | 0 | DEBUGMSGTL(("usm", "Configuration line missing privacy key\n")); |
4379 | 0 | usm_free_user(user); |
4380 | 0 | return NULL; |
4381 | 0 | } |
4382 | 0 | line = read_config_read_octet_string(line, &user->privKey, |
4383 | 0 | &user->privKeyLen); |
4384 | |
|
4385 | 0 | privtype = sc_get_privtype(user->privProtocol, user->privProtocolLen); |
4386 | 0 | proper_length = sc_get_proper_priv_length_bytype(privtype); |
4387 | 0 | if (USM_CREATE_USER_PRIV_DES == privtype) |
4388 | 0 | proper_length *= 2; /* ?? we store salt with key */ |
4389 | | /* For backwards compatibility */ |
4390 | 0 | if (user->privKeyLen > proper_length) { |
4391 | 0 | user->privKeyLen = proper_length; |
4392 | 0 | } |
4393 | |
|
4394 | 0 | if (line == NULL) { |
4395 | 0 | DEBUGMSGTL(("usm", "Configuration line missing public string\n")); |
4396 | 0 | usm_free_user(user); |
4397 | 0 | return NULL; |
4398 | 0 | } |
4399 | 0 | line = read_config_read_octet_string(line, &user->userPublicString, |
4400 | 0 | &user->userPublicStringLen); |
4401 | | |
4402 | | /* |
4403 | | * set the lcd entry for this engineID to the minimum boots/time |
4404 | | * values so that its a known engineid and won't return a report pdu. |
4405 | | * This is mostly important when receiving v3 traps so that the usm |
4406 | | * will at least continue processing them. |
4407 | | * Note: We do this at the end so that it only runs if the parsing |
4408 | | * was successful |
4409 | | */ |
4410 | 0 | set_enginetime(user->engineID, user->engineIDLen, 1, 0, 0); |
4411 | |
|
4412 | 0 | return user; |
4413 | 0 | } |
4414 | | |
4415 | | /* |
4416 | | * snmpd.conf parsing routines |
4417 | | */ |
4418 | | void |
4419 | | usm_parse_config_usmUser(const char *token, char *line) |
4420 | 0 | { |
4421 | 0 | struct usmUser *uptr; |
4422 | |
|
4423 | 0 | uptr = usm_read_user(line); |
4424 | 0 | if ( uptr) |
4425 | 0 | usm_add_user(uptr); |
4426 | 0 | } |
4427 | | |
4428 | | /*******************************************************************-o-****** |
4429 | | * usm_set_password |
4430 | | * |
4431 | | * Parameters: |
4432 | | * *token |
4433 | | * *line |
4434 | | * |
4435 | | * |
4436 | | * format: userSetAuthPass secname engineIDLen engineID pass |
4437 | | * or: userSetPrivPass secname engineIDLen engineID pass |
4438 | | * or: userSetAuthKey secname engineIDLen engineID KuLen Ku |
4439 | | * or: userSetPrivKey secname engineIDLen engineID KuLen Ku |
4440 | | * or: userSetAuthLocalKey secname engineIDLen engineID KulLen Kul |
4441 | | * or: userSetPrivLocalKey secname engineIDLen engineID KulLen Kul |
4442 | | * |
4443 | | * type is: 1=passphrase; 2=Ku; 3=Kul. |
4444 | | * |
4445 | | * |
4446 | | * ASSUMES Passwords are null-terminated printable strings. |
4447 | | */ |
4448 | | static void |
4449 | | usm_set_password(const char *token, char *line) |
4450 | 0 | { |
4451 | 0 | char *cp; |
4452 | 0 | char nameBuf[SNMP_MAXBUF]; |
4453 | 0 | u_char *engineID = NULL; |
4454 | 0 | size_t engineIDLen = 0; |
4455 | 0 | struct usmUser *user; |
4456 | |
|
4457 | 0 | cp = copy_nword(line, nameBuf, sizeof(nameBuf)); |
4458 | 0 | if (cp == NULL) { |
4459 | 0 | config_perror("invalid name specifier"); |
4460 | 0 | return; |
4461 | 0 | } |
4462 | | |
4463 | 0 | DEBUGMSGTL(("usm", "comparing: %s and %s\n", cp, WILDCARDSTRING)); |
4464 | 0 | if (strncmp(cp, WILDCARDSTRING, strlen(WILDCARDSTRING)) == 0) { |
4465 | | /* |
4466 | | * match against all engineIDs we know about |
4467 | | */ |
4468 | 0 | cp = skip_token(cp); |
4469 | 0 | for (user = userList; user != NULL; user = user->next) { |
4470 | 0 | if (user->secName && strcmp(user->secName, nameBuf) == 0) { |
4471 | 0 | usm_set_user_password(user, token, cp); |
4472 | 0 | } |
4473 | 0 | } |
4474 | 0 | } else { |
4475 | 0 | cp = read_config_read_octet_string(cp, &engineID, &engineIDLen); |
4476 | 0 | if (cp == NULL) { |
4477 | 0 | config_perror("invalid engineID specifier"); |
4478 | 0 | SNMP_FREE(engineID); |
4479 | 0 | return; |
4480 | 0 | } |
4481 | | |
4482 | 0 | user = usm_get_user(engineID, engineIDLen, nameBuf); |
4483 | 0 | if (user == NULL) { |
4484 | 0 | config_perror("not a valid user/engineID pair"); |
4485 | 0 | SNMP_FREE(engineID); |
4486 | 0 | return; |
4487 | 0 | } |
4488 | 0 | usm_set_user_password(user, token, cp); |
4489 | 0 | SNMP_FREE(engineID); |
4490 | 0 | } |
4491 | 0 | } |
4492 | | |
4493 | | /* |
4494 | | * uses the rest of LINE to configure USER's password of type TOKEN |
4495 | | */ |
4496 | | void |
4497 | | usm_set_user_password(struct usmUser *user, const char *token, char *line) |
4498 | 0 | { |
4499 | 0 | char *cp = line; |
4500 | 0 | u_char *engineID = user->engineID; |
4501 | 0 | size_t engineIDLen = user->engineIDLen; |
4502 | |
|
4503 | 0 | u_char **key; |
4504 | 0 | size_t *keyLen; |
4505 | 0 | u_char userKey[SNMP_MAXBUF_SMALL]; |
4506 | 0 | size_t userKeyLen = SNMP_MAXBUF_SMALL; |
4507 | 0 | u_char *userKeyP = userKey; |
4508 | 0 | int type, ret; |
4509 | | |
4510 | | /* |
4511 | | * Retrieve the "old" key and set the key type. |
4512 | | */ |
4513 | 0 | if (!token) { |
4514 | 0 | return; |
4515 | 0 | } else if (strcmp(token, "userSetAuthPass") == 0) { |
4516 | 0 | key = &user->authKey; |
4517 | 0 | keyLen = &user->authKeyLen; |
4518 | 0 | type = 0; |
4519 | 0 | } else if (strcmp(token, "userSetPrivPass") == 0) { |
4520 | 0 | key = &user->privKey; |
4521 | 0 | keyLen = &user->privKeyLen; |
4522 | 0 | type = 0; |
4523 | 0 | } else if (strcmp(token, "userSetAuthKey") == 0) { |
4524 | 0 | key = &user->authKey; |
4525 | 0 | keyLen = &user->authKeyLen; |
4526 | 0 | type = 1; |
4527 | 0 | } else if (strcmp(token, "userSetPrivKey") == 0) { |
4528 | 0 | key = &user->privKey; |
4529 | 0 | keyLen = &user->privKeyLen; |
4530 | 0 | type = 1; |
4531 | 0 | } else if (strcmp(token, "userSetAuthLocalKey") == 0) { |
4532 | 0 | key = &user->authKey; |
4533 | 0 | keyLen = &user->authKeyLen; |
4534 | 0 | type = 2; |
4535 | 0 | } else if (strcmp(token, "userSetPrivLocalKey") == 0) { |
4536 | 0 | key = &user->privKey; |
4537 | 0 | keyLen = &user->privKeyLen; |
4538 | 0 | type = 2; |
4539 | 0 | } else { |
4540 | | /* |
4541 | | * no old key, or token was not recognized |
4542 | | */ |
4543 | 0 | return; |
4544 | 0 | } |
4545 | | |
4546 | 0 | if (*key) { |
4547 | | /* |
4548 | | * (destroy and) free the old key |
4549 | | */ |
4550 | 0 | memset(*key, 0, *keyLen); |
4551 | 0 | SNMP_FREE(*key); |
4552 | 0 | } |
4553 | |
|
4554 | 0 | if (type == 0) { |
4555 | | /* |
4556 | | * convert the password into a key |
4557 | | */ |
4558 | 0 | if (cp == NULL) { |
4559 | 0 | config_perror("missing user password"); |
4560 | 0 | return; |
4561 | 0 | } |
4562 | 0 | ret = generate_Ku(user->authProtocol, user->authProtocolLen, |
4563 | 0 | (u_char *) cp, strlen(cp), userKey, &userKeyLen); |
4564 | |
|
4565 | 0 | if (ret != SNMPERR_SUCCESS) { |
4566 | 0 | config_perror("setting key failed (in sc_genKu())"); |
4567 | 0 | return; |
4568 | 0 | } |
4569 | | /* save master key */ |
4570 | 0 | if (user->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { |
4571 | 0 | if (userKey == user->privKey) { |
4572 | 0 | user->privKeyKu = netsnmp_memdup(userKey, userKeyLen); |
4573 | 0 | user->privKeyKuLen = userKeyLen; |
4574 | 0 | } else if (userKey == user->authKey) { |
4575 | 0 | user->authKeyKu = netsnmp_memdup(userKey, userKeyLen); |
4576 | 0 | user->authKeyKuLen = userKeyLen; |
4577 | 0 | } |
4578 | 0 | } |
4579 | 0 | } else if (type == 1) { |
4580 | 0 | cp = read_config_read_octet_string(cp, &userKeyP, &userKeyLen); |
4581 | |
|
4582 | 0 | if (cp == NULL) { |
4583 | 0 | config_perror("invalid user key"); |
4584 | 0 | return; |
4585 | 0 | } |
4586 | 0 | } |
4587 | | |
4588 | 0 | if (type < 2) { |
4589 | 0 | *key = (u_char *) malloc(SNMP_MAXBUF_SMALL); |
4590 | 0 | *keyLen = SNMP_MAXBUF_SMALL; |
4591 | 0 | ret = generate_kul(user->authProtocol, user->authProtocolLen, |
4592 | 0 | engineID, engineIDLen, |
4593 | 0 | userKey, userKeyLen, *key, keyLen); |
4594 | 0 | if (ret != SNMPERR_SUCCESS) { |
4595 | 0 | config_perror("setting key failed (in generate_kul())"); |
4596 | 0 | return; |
4597 | 0 | } |
4598 | | |
4599 | | /* |
4600 | | * (destroy and) free the old key |
4601 | | */ |
4602 | 0 | memset(userKey, 0, sizeof(userKey)); |
4603 | |
|
4604 | 0 | } else { |
4605 | | /* |
4606 | | * the key is given, copy it in |
4607 | | */ |
4608 | 0 | cp = read_config_read_octet_string(cp, key, keyLen); |
4609 | |
|
4610 | 0 | if (cp == NULL) { |
4611 | 0 | config_perror("invalid localized user key"); |
4612 | 0 | return; |
4613 | 0 | } |
4614 | 0 | } |
4615 | | |
4616 | 0 | if (key == &user->privKey) { |
4617 | 0 | ret = usm_extend_user_kul(user, *keyLen); |
4618 | 0 | if (SNMPERR_SUCCESS != ret) { |
4619 | 0 | config_perror("error extending localized user key"); |
4620 | 0 | return; |
4621 | 0 | } |
4622 | 0 | } |
4623 | 0 | } /* end usm_set_password() */ |
4624 | | |
4625 | | /* |
4626 | | * create a usm user from a string. |
4627 | | * |
4628 | | * The format for the string is described in the createUser |
4629 | | * section of the snmpd.conf man page. |
4630 | | * |
4631 | | * On success, a pointer to the created usmUser struct is returned. |
4632 | | * On error, a NULL pointer is returned. In this case, if a pointer to a |
4633 | | * char pointer is provided in errorMsg, an error string is returned. |
4634 | | * This error string points to a static message, and should not be |
4635 | | * freed. |
4636 | | */ |
4637 | | static struct usmUser * |
4638 | | usm_create_usmUser_from_string(char *line, const char **errorMsg) |
4639 | 0 | { |
4640 | 0 | char *cp; |
4641 | 0 | const char *dummy; |
4642 | 0 | char buf[SNMP_MAXBUF_MEDIUM]; |
4643 | 0 | struct usmUser *newuser; |
4644 | 0 | u_char userKey[SNMP_MAXBUF_SMALL], *tmpp; |
4645 | 0 | size_t userKeyLen = SNMP_MAXBUF_SMALL; |
4646 | 0 | size_t privKeySize; |
4647 | 0 | size_t ret; |
4648 | 0 | int ret2, properLen, properPrivKeyLen; |
4649 | 0 | const oid *def_auth_prot, *def_priv_prot; |
4650 | 0 | size_t def_auth_prot_len, def_priv_prot_len; |
4651 | 0 | const netsnmp_priv_alg_info *pai; |
4652 | |
|
4653 | 0 | def_auth_prot = get_default_authtype(&def_auth_prot_len); |
4654 | 0 | def_priv_prot = get_default_privtype(&def_priv_prot_len); |
4655 | |
|
4656 | 0 | if (NULL == line) |
4657 | 0 | return NULL; |
4658 | | |
4659 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
4660 | | DEBUGMSGTL(("usmUser", "new user %s\n", line)); /* logs passphrases */ |
4661 | | #endif |
4662 | | |
4663 | 0 | if (NULL == errorMsg) |
4664 | 0 | errorMsg = &dummy; |
4665 | 0 | *errorMsg = NULL; /* no errors yet */ |
4666 | |
|
4667 | 0 | newuser = usm_create_user(); |
4668 | 0 | if (newuser == NULL) { |
4669 | 0 | *errorMsg = "malloc failure creating new user"; |
4670 | 0 | goto fail; |
4671 | 0 | } |
4672 | | |
4673 | | /* |
4674 | | * READ: Security Name |
4675 | | */ |
4676 | 0 | cp = copy_nword(line, buf, sizeof(buf)); |
4677 | | |
4678 | | /* |
4679 | | * check for (undocumented) 'keep master key' flag. so far, this is |
4680 | | * just used for users for informs (who need non-localized keys). |
4681 | | */ |
4682 | 0 | if (strcmp(buf, "-M") == 0) { |
4683 | 0 | newuser->flags |= USMUSER_FLAG_KEEP_MASTER_KEY; |
4684 | 0 | cp = copy_nword(cp, buf, sizeof(buf)); |
4685 | 0 | } |
4686 | | |
4687 | | /* |
4688 | | * might be a -e ENGINEID argument |
4689 | | */ |
4690 | 0 | if (strcmp(buf, "-e") == 0) { |
4691 | 0 | size_t ebuf_len = 32, eout_len = 0; |
4692 | 0 | u_char *ebuf = (u_char *) malloc(ebuf_len); |
4693 | |
|
4694 | 0 | if (ebuf == NULL) { |
4695 | 0 | *errorMsg = "malloc failure processing -e flag"; |
4696 | 0 | goto fail; |
4697 | 0 | } |
4698 | | |
4699 | | /* |
4700 | | * Get the specified engineid from the line. |
4701 | | */ |
4702 | 0 | cp = copy_nword(cp, buf, sizeof(buf)); |
4703 | 0 | if (!snmp_hex_to_binary(&ebuf, &ebuf_len, &eout_len, 1, buf)) { |
4704 | 0 | *errorMsg = "invalid EngineID argument to -e"; |
4705 | 0 | SNMP_FREE(ebuf); |
4706 | 0 | goto fail; |
4707 | 0 | } |
4708 | | |
4709 | 0 | newuser->engineID = ebuf; |
4710 | 0 | newuser->engineIDLen = eout_len; |
4711 | 0 | cp = copy_nword(cp, buf, sizeof(buf)); |
4712 | 0 | } else { |
4713 | 0 | newuser->engineID = snmpv3_generate_engineID(&ret); |
4714 | 0 | if (ret == 0) { |
4715 | 0 | goto fail; |
4716 | 0 | } |
4717 | 0 | newuser->engineIDLen = ret; |
4718 | 0 | } |
4719 | | |
4720 | 0 | newuser->secName = strdup(buf); |
4721 | 0 | newuser->name = strdup(buf); |
4722 | |
|
4723 | 0 | if (!cp) { |
4724 | | #ifdef NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV |
4725 | | /** no passwords ok iff defaults are noauth/nopriv */ |
4726 | | if (snmp_oid_compare(usmNoAuthProtocol, OID_LENGTH(usmNoAuthProtocol), |
4727 | | def_auth_prot, def_auth_prot_len) != 0) { |
4728 | | *errorMsg = "no authentication pass phrase"; |
4729 | | goto fail; |
4730 | | } |
4731 | | if (snmp_oid_compare(usmNoPrivProtocol, OID_LENGTH(usmNoPrivProtocol), |
4732 | | def_priv_prot, def_priv_prot_len) != 0) { |
4733 | | *errorMsg = "no privacy pass phrase"; |
4734 | | goto fail; |
4735 | | } |
4736 | | #endif /* NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV */ |
4737 | 0 | goto add; /* no authentication or privacy type */ |
4738 | 0 | } |
4739 | | |
4740 | | /* |
4741 | | * READ: Authentication Type |
4742 | | */ |
4743 | 0 | newuser->authProtocol[0] = 0; |
4744 | 0 | cp = copy_nword(cp, buf, sizeof(buf)); |
4745 | | /* If no authentication protocol was specified, or it was explicitly |
4746 | | * set to use the default, use the default auth protocol |
4747 | | */ |
4748 | 0 | if (buf[0] == '\0' || strcmp(buf, "default") == 0) { |
4749 | 0 | SNMP_FREE(newuser->authProtocol); |
4750 | 0 | if (!def_auth_prot) { |
4751 | 0 | *errorMsg = "def_auth_prot == NULL"; |
4752 | 0 | goto fail; |
4753 | 0 | } |
4754 | 0 | newuser->authProtocol = snmp_duplicate_objid(def_auth_prot, |
4755 | 0 | def_auth_prot_len); |
4756 | 0 | if (newuser->authProtocol == NULL) { |
4757 | 0 | *errorMsg = "malloc failed"; |
4758 | 0 | goto fail; |
4759 | 0 | } |
4760 | 0 | newuser->authProtocolLen = def_auth_prot_len; |
4761 | 0 | } else { |
4762 | 0 | const oid *auth_prot; |
4763 | 0 | int auth_type = usm_lookup_auth_type(buf); |
4764 | 0 | if (auth_type < 0) { |
4765 | 0 | *errorMsg = "unknown authProtocol"; |
4766 | 0 | goto fail; |
4767 | 0 | } |
4768 | 0 | auth_prot = sc_get_auth_oid(auth_type, &newuser->authProtocolLen); |
4769 | 0 | if (auth_prot) { |
4770 | 0 | SNMP_FREE(newuser->authProtocol); |
4771 | 0 | newuser->authProtocol = |
|