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