/src/net-snmp/snmplib/keytools.c
Line | Count | Source |
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 | | /* |
18 | | * keytools.c |
19 | | */ |
20 | | |
21 | | #include <net-snmp/net-snmp-config.h> |
22 | | #include <net-snmp/net-snmp-features.h> |
23 | | |
24 | | #include <stdio.h> |
25 | | #include <sys/types.h> |
26 | | #ifdef HAVE_NETINET_IN_H |
27 | | #include <netinet/in.h> |
28 | | #endif |
29 | | #ifdef HAVE_STDLIB_H |
30 | | #include <stdlib.h> |
31 | | #endif |
32 | | #ifdef HAVE_STRING_H |
33 | | #include <string.h> |
34 | | #else |
35 | | #include <strings.h> |
36 | | #endif |
37 | | |
38 | | #ifdef HAVE_UNISTD_H |
39 | | #include <unistd.h> |
40 | | #endif |
41 | | |
42 | | #include <math.h> |
43 | | |
44 | | #include <net-snmp/types.h> |
45 | | #include <net-snmp/output_api.h> |
46 | | #include <net-snmp/utilities.h> |
47 | | |
48 | | #include <net-snmp/library/snmp_api.h> |
49 | | #ifdef NETSNMP_USE_OPENSSL |
50 | | # include <openssl/hmac.h> |
51 | | #else |
52 | | #ifdef NETSNMP_USE_INTERNAL_MD5 |
53 | | #include <net-snmp/library/md5.h> |
54 | | #endif |
55 | | #endif |
56 | | #ifdef NETSNMP_USE_INTERNAL_CRYPTO |
57 | | #include <net-snmp/library/openssl_md5.h> |
58 | | #include <net-snmp/library/openssl_sha.h> |
59 | | #endif |
60 | | |
61 | | #ifdef NETSNMP_USE_PKCS11 |
62 | | #include <security/cryptoki.h> |
63 | | #endif |
64 | | |
65 | | #include <net-snmp/library/scapi.h> |
66 | | #include <net-snmp/library/keytools.h> |
67 | | |
68 | | #include <net-snmp/library/transform_oids.h> |
69 | | |
70 | | #include <net-snmp/library/snmp_secmod.h> |
71 | | #include <net-snmp/library/snmpusm.h> |
72 | | |
73 | | netsnmp_feature_child_of(usm_support, libnetsnmp); |
74 | | netsnmp_feature_child_of(usm_keytools, usm_support); |
75 | | |
76 | | #ifndef NETSNMP_FEATURE_REMOVE_USM_KEYTOOLS |
77 | | |
78 | | /*******************************************************************-o-****** |
79 | | * generate_Ku |
80 | | * |
81 | | * Parameters: |
82 | | * *hashtype MIB OID for the transform type for hashing. |
83 | | * hashtype_len Length of OID value. |
84 | | * *P Pre-allocated bytes of passphrase. |
85 | | * pplen Length of passphrase. |
86 | | * *Ku Buffer to contain Ku. |
87 | | * *kulen Length of Ku buffer. |
88 | | * |
89 | | * Returns: |
90 | | * SNMPERR_SUCCESS Success. |
91 | | * SNMPERR_GENERR All errors. |
92 | | * |
93 | | * |
94 | | * Convert a passphrase into a master user key, Ku, according to the |
95 | | * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM) |
96 | | * as follows: |
97 | | * |
98 | | * Expand the passphrase to fill the passphrase buffer space, if necessary, |
99 | | * concatenation as many duplicates as possible of P to itself. If P is |
100 | | * larger than the buffer space, truncate it to fit. |
101 | | * |
102 | | * Then hash the result with the given hashtype transform. Return |
103 | | * the result as Ku. |
104 | | * |
105 | | * If successful, kulen contains the size of the hash written to Ku. |
106 | | * |
107 | | * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length |
108 | | * cause an error to be returned. |
109 | | * (Punt this check to the cmdline apps? XXX) |
110 | | */ |
111 | | int |
112 | | generate_Ku(const oid * hashtype, u_int hashtype_len, |
113 | | const u_char * P, size_t pplen, u_char * Ku, size_t * kulen) |
114 | | #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_CRYPTO) |
115 | 0 | { |
116 | 0 | int rval = SNMPERR_SUCCESS, |
117 | 0 | nbytes = USM_LENGTH_EXPANDED_PASSPHRASE, auth_type; |
118 | | #if !defined(NETSNMP_USE_OPENSSL) && \ |
119 | | defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_INTERNAL_CRYPTO) |
120 | | int ret; |
121 | | #endif |
122 | |
|
123 | 0 | u_int i, pindex = 0; |
124 | |
|
125 | 0 | u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp; |
126 | |
|
127 | 0 | #ifdef NETSNMP_USE_OPENSSL |
128 | 0 | EVP_MD_CTX *ctx = NULL; |
129 | 0 | const EVP_MD *hashfn = NULL; |
130 | | #elif defined(NETSNMP_USE_INTERNAL_CRYPTO) |
131 | | SHA_CTX csha1; |
132 | | MD5_CTX cmd5; |
133 | | char cryptotype = 0; |
134 | | #define TYPE_MD5 1 |
135 | | #define TYPE_SHA1 2 |
136 | | #else |
137 | | MDstruct MD; |
138 | | #endif |
139 | | /* |
140 | | * Sanity check. |
141 | | */ |
142 | 0 | if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)) { |
143 | 0 | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
144 | 0 | } |
145 | | |
146 | 0 | if (pplen < USM_LENGTH_P_MIN) { |
147 | 0 | snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " |
148 | 0 | "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); |
149 | 0 | snmp_set_detail("The supplied password length is too short."); |
150 | 0 | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
151 | 0 | } |
152 | | |
153 | 0 | auth_type = sc_get_authtype(hashtype, hashtype_len); |
154 | 0 | if (SNMPERR_GENERR == auth_type) { |
155 | 0 | snmp_log(LOG_ERR, "Error: unknown authtype"); |
156 | 0 | snmp_set_detail("unknown authtype"); |
157 | 0 | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
158 | 0 | } |
159 | | |
160 | | /* |
161 | | * Setup for the transform type. |
162 | | */ |
163 | 0 | #ifdef NETSNMP_USE_OPENSSL |
164 | | |
165 | 0 | if (*kulen < EVP_MAX_MD_SIZE) { |
166 | 0 | snmp_log(LOG_ERR, "Internal Error: ku buffer too small (min=%d).\n", |
167 | 0 | EVP_MAX_MD_SIZE); |
168 | 0 | snmp_set_detail("Internal Error: ku buffer too small."); |
169 | 0 | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
170 | 0 | } |
171 | | |
172 | | /** get hash function */ |
173 | 0 | hashfn = sc_get_openssl_hashfn(auth_type); |
174 | 0 | if (NULL == hashfn) { |
175 | 0 | snmp_log(LOG_ERR, "Error: no hashfn for authtype"); |
176 | 0 | snmp_set_detail("no hashfn for authtype"); |
177 | 0 | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
178 | 0 | } |
179 | | |
180 | 0 | #if defined(HAVE_EVP_MD_CTX_NEW) |
181 | 0 | ctx = EVP_MD_CTX_new(); |
182 | | #elif defined(HAVE_EVP_MD_CTX_CREATE) |
183 | | ctx = EVP_MD_CTX_create(); |
184 | | #elif defined(HAVE_VOID_EVP_MD_CTX_INIT) |
185 | | ctx = malloc(sizeof(*ctx)); |
186 | | if (ctx) |
187 | | EVP_MD_CTX_init(ctx); |
188 | | #else |
189 | | ctx = malloc(sizeof(*ctx)); |
190 | | if (!ctx || !EVP_MD_CTX_init(ctx)) { |
191 | | rval = SNMPERR_GENERR; |
192 | | goto generate_Ku_quit; |
193 | | } |
194 | | #endif |
195 | 0 | if (!ctx || !EVP_DigestInit(ctx, hashfn)) { |
196 | 0 | rval = SNMPERR_GENERR; |
197 | 0 | goto generate_Ku_quit; |
198 | 0 | } |
199 | | |
200 | | #elif defined(NETSNMP_USE_INTERNAL_CRYPTO) |
201 | | #ifndef NETSNMP_DISABLE_MD5 |
202 | | if (NETSNMP_USMAUTH_HMACMD5 == auth_type) { |
203 | | if (!MD5_Init(&cmd5)) |
204 | | return SNMPERR_GENERR; |
205 | | cryptotype = TYPE_MD5; |
206 | | } else |
207 | | #endif |
208 | | if (NETSNMP_USMAUTH_HMACSHA1 == auth_type) { |
209 | | if (!SHA1_Init(&csha1)) |
210 | | return SNMPERR_GENERR; |
211 | | cryptotype = TYPE_SHA1; |
212 | | } else { |
213 | | return (SNMPERR_GENERR); |
214 | | } |
215 | | #else |
216 | | MDbegin(&MD); |
217 | | #endif /* NETSNMP_USE_OPENSSL */ |
218 | | |
219 | 0 | while (nbytes > 0) { |
220 | 0 | bufp = buf; |
221 | 0 | for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) { |
222 | 0 | *bufp++ = P[pindex++ % pplen]; |
223 | 0 | } |
224 | 0 | #ifdef NETSNMP_USE_OPENSSL |
225 | 0 | if (!EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK)) { |
226 | 0 | rval = SNMPERR_USM_ENCRYPTIONERROR; |
227 | 0 | goto generate_Ku_quit; |
228 | 0 | } |
229 | | #elif defined(NETSNMP_USE_INTERNAL_CRYPTO) |
230 | | if (TYPE_SHA1 == cryptotype) { |
231 | | rval = !SHA1_Update(&csha1, buf, USM_LENGTH_KU_HASHBLOCK); |
232 | | } else { |
233 | | rval = !MD5_Update(&cmd5, buf, USM_LENGTH_KU_HASHBLOCK); |
234 | | } |
235 | | if (rval != 0) { |
236 | | return SNMPERR_USM_ENCRYPTIONERROR; |
237 | | } |
238 | | #elif defined(NETSNMP_USE_INTERNAL_MD5) |
239 | | if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) { |
240 | | rval = SNMPERR_USM_ENCRYPTIONERROR; |
241 | | goto md5_fin; |
242 | | } |
243 | | #endif /* NETSNMP_USE_OPENSSL */ |
244 | 0 | nbytes -= USM_LENGTH_KU_HASHBLOCK; |
245 | 0 | } |
246 | | |
247 | 0 | #ifdef NETSNMP_USE_OPENSSL |
248 | 0 | { |
249 | 0 | unsigned int tmp_len; |
250 | |
|
251 | 0 | tmp_len = *kulen; |
252 | 0 | if (!EVP_DigestFinal(ctx, (unsigned char *) Ku, &tmp_len)) { |
253 | 0 | rval = SNMPERR_USM_ENCRYPTIONERROR; |
254 | 0 | goto generate_Ku_quit; |
255 | 0 | } |
256 | 0 | *kulen = tmp_len; |
257 | | /* |
258 | | * what about free() |
259 | | */ |
260 | 0 | } |
261 | | #elif defined(NETSNMP_USE_INTERNAL_CRYPTO) |
262 | | if (TYPE_SHA1 == cryptotype) { |
263 | | SHA1_Final(Ku, &csha1); |
264 | | } else { |
265 | | MD5_Final(Ku, &cmd5); |
266 | | } |
267 | | ret = sc_get_properlength(hashtype, hashtype_len); |
268 | | if (ret == SNMPERR_GENERR) |
269 | | return SNMPERR_GENERR; |
270 | | *kulen = ret; |
271 | | #elif defined(NETSNMP_USE_INTERNAL_MD5) |
272 | | if (MDupdate(&MD, buf, 0)) { |
273 | | rval = SNMPERR_USM_ENCRYPTIONERROR; |
274 | | goto md5_fin; |
275 | | } |
276 | | ret = sc_get_properlength(hashtype, hashtype_len); |
277 | | if (ret == SNMPERR_GENERR) |
278 | | return SNMPERR_GENERR; |
279 | | *kulen = ret; |
280 | | MDget(&MD, Ku, *kulen); |
281 | | md5_fin: |
282 | | memset(&MD, 0, sizeof(MD)); |
283 | | #endif /* NETSNMP_USE_INTERNAL_MD5 */ |
284 | | |
285 | | |
286 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
287 | | DEBUGMSGTL(("generate_Ku", "generating Ku (%s from %s): ", |
288 | | usm_lookup_auth_str(auth_type), P)); |
289 | | for (i = 0; i < *kulen; i++) |
290 | | DEBUGMSG(("generate_Ku", "%02x", Ku[i])); |
291 | | DEBUGMSG(("generate_Ku", "\n")); |
292 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
293 | | |
294 | | |
295 | 0 | generate_Ku_quit: |
296 | 0 | memset(buf, 0, sizeof(buf)); |
297 | 0 | #ifdef NETSNMP_USE_OPENSSL |
298 | 0 | if (ctx) { |
299 | 0 | #if defined(HAVE_EVP_MD_CTX_FREE) |
300 | 0 | EVP_MD_CTX_free(ctx); |
301 | | #elif defined(HAVE_EVP_MD_CTX_DESTROY) |
302 | | EVP_MD_CTX_destroy(ctx); |
303 | | #else |
304 | | EVP_MD_CTX_cleanup(ctx); |
305 | | free(ctx); |
306 | | #endif |
307 | 0 | } |
308 | 0 | #endif |
309 | 0 | return rval; |
310 | |
|
311 | 0 | } /* end generate_Ku() */ |
312 | | #elif defined(NETSNMP_USE_PKCS11) |
313 | | { |
314 | | int rval = SNMPERR_SUCCESS, auth_type;; |
315 | | |
316 | | /* |
317 | | * Sanity check. |
318 | | */ |
319 | | if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)) { |
320 | | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
321 | | } |
322 | | |
323 | | if (pplen < USM_LENGTH_P_MIN) { |
324 | | snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " |
325 | | "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); |
326 | | snmp_set_detail("The supplied password length is too short."); |
327 | | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
328 | | } |
329 | | |
330 | | auth_type = sc_get_authtype(hashtype, hashtype_len); |
331 | | if (SNMPERR_GENERR == auth_type) { |
332 | | snmp_log(LOG_ERR, "Error: unknown authtype"); |
333 | | snmp_set_detail("unknown authtype"); |
334 | | QUITFUN(SNMPERR_GENERR, generate_Ku_quit); |
335 | | } |
336 | | |
337 | | |
338 | | /* |
339 | | * Setup for the transform type. |
340 | | */ |
341 | | |
342 | | #ifndef NETSNMP_DISABLE_MD5 |
343 | | if (NETSNMP_USMAUTH_HMACMD5 == auth_type) |
344 | | return pkcs_generate_Ku(CKM_MD5, P, pplen, Ku, kulen); |
345 | | else |
346 | | #endif |
347 | | if (NETSNMP_USMAUTH_HMACSHA1 == auth_type) |
348 | | return pkcs_generate_Ku(CKM_SHA_1, P, pplen, Ku, kulen); |
349 | | else { |
350 | | return (SNMPERR_GENERR); |
351 | | } |
352 | | |
353 | | generate_Ku_quit: |
354 | | |
355 | | return rval; |
356 | | } /* end generate_Ku() */ |
357 | | #else |
358 | | _KEYTOOLS_NOT_AVAILABLE |
359 | | #endif /* internal or openssl */ |
360 | | /*******************************************************************-o-****** |
361 | | * generate_kul |
362 | | * |
363 | | * Parameters: |
364 | | * *hashtype |
365 | | * hashtype_len |
366 | | * *engineID |
367 | | * engineID_len |
368 | | * *Ku Master key for a given user. |
369 | | * ku_len Length of Ku in bytes. |
370 | | * *Kul Localized key for a given user at engineID. |
371 | | * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT). |
372 | | * |
373 | | * Returns: |
374 | | * SNMPERR_SUCCESS Success. |
375 | | * SNMPERR_GENERR All errors. |
376 | | * |
377 | | * |
378 | | * Ku MUST be the proper length (currently fixed) for the given hashtype. |
379 | | * |
380 | | * Upon successful return, Kul contains the localized form of Ku at |
381 | | * engineID, and the length of the key is stored in kul_len. |
382 | | * |
383 | | * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and |
384 | | * originally documented in: |
385 | | * U. Blumenthal, N. C. Hien, B. Wijnen, |
386 | | * "Key Derivation for Network Management Applications", |
387 | | * IEEE Network Magazine, April/May issue, 1997. |
388 | | * |
389 | | * |
390 | | * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku). |
391 | | * |
392 | | * NOTE Localized keys for privacy transforms are generated via |
393 | | * the authentication transform held by the same usmUser. |
394 | | * |
395 | | * XXX An engineID of any length is accepted, even if larger than |
396 | | * what is spec'ed for the textual convention. |
397 | | */ |
398 | | int |
399 | | generate_kul(const oid * hashtype, u_int hashtype_len, |
400 | | const u_char * engineID, size_t engineID_len, |
401 | | const u_char * Ku, size_t ku_len, |
402 | | u_char * Kul, size_t * kul_len) |
403 | | #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO) |
404 | 0 | { |
405 | 0 | int rval = SNMPERR_SUCCESS, auth_type; |
406 | 0 | u_int nbytes = 0; |
407 | 0 | size_t properlength; |
408 | 0 | int iproperlength; |
409 | |
|
410 | 0 | u_char buf[SNMP_MAXBUF]; |
411 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
412 | | int i; |
413 | | #endif |
414 | | |
415 | | |
416 | | /* |
417 | | * Sanity check. |
418 | | */ |
419 | 0 | if (!hashtype || !engineID || !Ku || !Kul || !kul_len |
420 | 0 | || (engineID_len <= 0) || (ku_len <= 0) || (*kul_len <= 0)) { |
421 | 0 | QUITFUN(SNMPERR_GENERR, generate_kul_quit); |
422 | 0 | } |
423 | | |
424 | 0 | auth_type = sc_get_authtype(hashtype, hashtype_len); |
425 | 0 | if (SNMPERR_GENERR == auth_type) |
426 | 0 | QUITFUN(SNMPERR_GENERR, generate_kul_quit); |
427 | |
|
428 | 0 | iproperlength = sc_get_proper_auth_length_bytype(auth_type); |
429 | 0 | if (iproperlength == SNMPERR_GENERR) |
430 | 0 | QUITFUN(SNMPERR_GENERR, generate_kul_quit); |
431 | |
|
432 | 0 | properlength = (size_t) iproperlength; |
433 | |
|
434 | 0 | if ((*kul_len < properlength) || (ku_len < properlength) || |
435 | 0 | (engineID_len > sizeof(buf)) || |
436 | 0 | (2 * properlength > sizeof(buf) - engineID_len)) { |
437 | 0 | QUITFUN(SNMPERR_GENERR, generate_kul_quit); |
438 | 0 | } |
439 | | |
440 | | /* |
441 | | * Concatenate Ku and engineID properly, then hash the result. |
442 | | * Store it in Kul. |
443 | | */ |
444 | 0 | nbytes = 0; |
445 | 0 | memcpy(buf, Ku, properlength); |
446 | 0 | nbytes += properlength; |
447 | 0 | memcpy(buf + nbytes, engineID, engineID_len); |
448 | 0 | nbytes += engineID_len; |
449 | 0 | memcpy(buf + nbytes, Ku, properlength); |
450 | 0 | nbytes += properlength; |
451 | |
|
452 | 0 | rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len); |
453 | |
|
454 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
455 | | DEBUGMSGTL(("generate_kul", "generating Kul (%s from Ku): ", |
456 | | usm_lookup_auth_str(auth_type) )); |
457 | | for (i = 0; i < *kul_len; i++) |
458 | | DEBUGMSG(("generate_kul", "%02x", Kul[i])); |
459 | | DEBUGMSG(("generate_kul", "keytools\n")); |
460 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
461 | |
|
462 | 0 | QUITFUN(rval, generate_kul_quit); |
463 | | |
464 | |
|
465 | 0 | generate_kul_quit: |
466 | 0 | return rval; |
467 | |
|
468 | 0 | } /* end generate_kul() */ |
469 | | |
470 | | #else |
471 | | _KEYTOOLS_NOT_AVAILABLE |
472 | | #endif /* internal or openssl */ |
473 | | |
474 | | /*******************************************************************-o-****** |
475 | | * _kul_extend_blumenthal |
476 | | * |
477 | | * Parameters: |
478 | | * *hashoid MIB OID for the hash transform type. |
479 | | * hashoid_len Length of the MIB OID hash transform type. |
480 | | * *origKul original kul (localized; IN/OUT) |
481 | | * *origKulLen Length of original kul in bytes (IN/OUT) |
482 | | * origKulSize Size of original kul buffer |
483 | | * needKeyLen Size needed for key |
484 | | * |
485 | | * Returns: |
486 | | * SNMPERR_SUCCESS Success. |
487 | | * SNMPERR_GENERR All errors. |
488 | | */ |
489 | | #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 |
490 | | static int |
491 | | _kul_extend_blumenthal(int needKeyLen, oid *hashoid, u_int hashoid_len, |
492 | | u_char *origKul, size_t *origKulLen, int origKulSize) |
493 | 0 | { |
494 | 0 | u_char newKul[USM_LENGTH_KU_HASHBLOCK]; |
495 | 0 | size_t newKulLen; |
496 | 0 | int count, hashBits, hashBytes, authtype, i, saveLen; |
497 | |
|
498 | 0 | DEBUGMSGTL(("usm:extend_kul", " blumenthal called\n")); |
499 | |
|
500 | 0 | if (NULL == hashoid || NULL == origKul || NULL == origKulLen || |
501 | 0 | needKeyLen > origKulSize) { |
502 | 0 | DEBUGMSGTL(("usm:extend_kul", "bad parameters\n")); |
503 | 0 | return SNMPERR_GENERR; |
504 | 0 | } |
505 | | |
506 | 0 | authtype = sc_get_authtype(hashoid, hashoid_len); |
507 | 0 | if (authtype < 0 ) { |
508 | 0 | DEBUGMSGTL(("usm:extend_kul", "unknown authtype\n")); |
509 | 0 | return SNMPERR_GENERR; |
510 | 0 | } |
511 | | |
512 | 0 | saveLen = *origKulLen; |
513 | 0 | needKeyLen -= *origKulLen; /* subtract bytes we already have */ |
514 | | |
515 | | /* |
516 | | * 3.1.2.1: |
517 | | * 1)Let Hnnn() the hash function of the authentication protocol for |
518 | | * the user U on the SNMP authoritative engine E. nnn being the size |
519 | | * of the output of the hash function (e.g. nnn=128 bits for MD5, or |
520 | | * nnn=160 bits for SHA1). |
521 | | */ |
522 | 0 | hashBytes = sc_get_proper_auth_length_bytype(authtype); |
523 | 0 | hashBits = 8 * hashBytes; |
524 | | |
525 | | /* 3.1.2.1: |
526 | | * 2)Set c = ceil ( 256 / nnn ) |
527 | | */ |
528 | 0 | count = ceil( (double)256 / (double)hashBits ); |
529 | 0 | DEBUGMSGTL(("9:usm:extend_kul:blumenthal", "count ceiling %d\n", count)); |
530 | | |
531 | | /* 3.1.2.1: |
532 | | * 3)For i = 1, 2, ..., c |
533 | | * a.Set Kul = Kul || Hnnn(Kul); Where Hnnn() is the hash |
534 | | * function of the authentication protocol defined for that user |
535 | | */ |
536 | 0 | for(i = 0; i < count; ++i) { |
537 | 0 | int copyLen, rc; |
538 | |
|
539 | 0 | newKulLen = sizeof(newKul); |
540 | 0 | rc = sc_hash_type( authtype, origKul, *origKulLen, newKul, &newKulLen); |
541 | 0 | if (SNMPERR_SUCCESS != rc) { |
542 | 0 | DEBUGMSGTL(("usm:extend_kul", "error from sc_hash_type\n")); |
543 | 0 | return SNMPERR_GENERR; |
544 | 0 | } |
545 | | |
546 | 0 | copyLen = SNMP_MIN(needKeyLen, newKulLen); |
547 | 0 | memcpy(origKul + *origKulLen, newKul, copyLen); |
548 | 0 | needKeyLen -= copyLen; |
549 | 0 | *origKulLen += copyLen; |
550 | | |
551 | | /** not part of the draft, but stop if we already have enough bits */ |
552 | 0 | if (needKeyLen <= 0) |
553 | 0 | break; |
554 | 0 | } |
555 | | |
556 | 0 | DEBUGMSGTL(("usm:extend_kul:blumenthal", |
557 | 0 | "orig len %d, new len %" NETSNMP_PRIz "d\n", |
558 | 0 | saveLen, *origKulLen)); |
559 | |
|
560 | 0 | return SNMPERR_SUCCESS; |
561 | 0 | } |
562 | | #endif /* NETSNMP_DRAFT_BLUMENTHAL_AES_04 */ |
563 | | |
564 | | /*******************************************************************-o-****** |
565 | | * _kul_extend_reeder |
566 | | * |
567 | | * Parameters: |
568 | | * *hashoid MIB OID for the hash transform type. |
569 | | * hashoid_len Length of the MIB OID hash transform type. |
570 | | * *engineID engineID |
571 | | * engineIDLen Length of engineID |
572 | | * *origKul original kul (localized; IN/OUT) |
573 | | * *origKulLen Length of original kul in bytes (IN/OUT) |
574 | | * origKulSize Size of original kul buffer |
575 | | * |
576 | | * Returns: |
577 | | * SNMPERR_SUCCESS Success. |
578 | | * SNMPERR_GENERR All errors. |
579 | | */ |
580 | | #if defined(NETSNMP_DRAFT_REEDER_3DES) || defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) |
581 | | static int |
582 | | _kul_extend_reeder(int needKeyLen, oid *hashoid, u_int hashoid_len, |
583 | | u_char *engineID, int engineIDLen, |
584 | | u_char *origKul, size_t *origKulLen, int origKulSize) |
585 | 0 | { |
586 | 0 | u_char newKu[USM_LENGTH_KU_HASHBLOCK], newKul[USM_LENGTH_KU_HASHBLOCK]; |
587 | 0 | size_t newKuLen, newKulLen, saveLen; |
588 | 0 | int authType, copylen; |
589 | |
|
590 | 0 | DEBUGMSGTL(("usm:extend_kul", " reeder called\n")); |
591 | |
|
592 | 0 | if (NULL == hashoid || NULL == engineID || NULL == origKul || |
593 | 0 | NULL == origKulLen || needKeyLen > origKulSize) |
594 | 0 | return SNMPERR_GENERR; |
595 | | |
596 | 0 | authType = sc_get_authtype(hashoid, hashoid_len); |
597 | 0 | if (SNMPERR_GENERR == authType) |
598 | 0 | return SNMPERR_GENERR; |
599 | 0 | newKulLen = sc_get_proper_auth_length_bytype(authType); |
600 | 0 | saveLen = *origKulLen; |
601 | 0 | needKeyLen -= *origKulLen; /* subtract bytes we already have */ |
602 | 0 | while (needKeyLen > 0) { |
603 | |
|
604 | 0 | newKuLen = sizeof(newKu); |
605 | | /* |
606 | | * hash the existing key using the passphrase to Ku algorithm. |
607 | | * [ Ku' = Ku(kul) ] |
608 | | */ |
609 | 0 | if (generate_Ku(hashoid, hashoid_len, origKul, *origKulLen, |
610 | 0 | newKu, &newKuLen) != SNMPERR_SUCCESS) |
611 | 0 | return SNMPERR_GENERR; |
612 | | |
613 | | /* |
614 | | * localize the new key generated from current localized key |
615 | | * and append it to the current localized key. |
616 | | * [ kul' = kul || kul(Ku') ] |
617 | | */ |
618 | 0 | newKulLen = sizeof(newKul); |
619 | 0 | if(generate_kul(hashoid, hashoid_len, engineID, engineIDLen, |
620 | 0 | newKu, newKuLen, newKul, |
621 | 0 | &newKulLen) != SNMPERR_SUCCESS) |
622 | 0 | return SNMPERR_GENERR; |
623 | | |
624 | 0 | copylen = SNMP_MIN(needKeyLen, newKulLen); |
625 | 0 | memcpy(origKul + *origKulLen, newKul, copylen); |
626 | 0 | needKeyLen -= copylen; |
627 | 0 | *origKulLen += copylen; |
628 | 0 | } |
629 | | |
630 | 0 | DEBUGMSGTL(("usm:extend_kul:reeder", |
631 | 0 | "orig len %" NETSNMP_PRIz "d, new len %" NETSNMP_PRIz "d\n", |
632 | 0 | saveLen, *origKulLen)); |
633 | 0 | return SNMPERR_SUCCESS; |
634 | 0 | } |
635 | | #endif /* NETSNMP_DRAFT_REEDER_3DES || NETSNMP_DRAFT_BLUMENTHAL_AES_04 */ |
636 | | |
637 | | /*******************************************************************-o-****** |
638 | | * netsnmp_extend_key |
639 | | * |
640 | | * Extend a kul buffer to the needed key size. if the passed kulBuf |
641 | | * is not large enough, a new one will be allocated and the old one |
642 | | * will be freed. |
643 | | * |
644 | | * Parameters: |
645 | | * neededKeyLen The neede key length |
646 | | * *hashoid MIB OID for the hash transform type. |
647 | | * hashoid_len Length of the MIB OID hash transform type. |
648 | | * privType Privacy algorithm type |
649 | | * engineID engineID |
650 | | * engineIDLen Length of engineID |
651 | | * **kulBuf Pointer to a buffer pointer |
652 | | * *kulBufLen Length of current kul buffer |
653 | | * kulBufSize Allocated size of current kul buffer |
654 | | * |
655 | | * OUT: |
656 | | * **kulBuf New kulBuf pointer (if it needed to be expanded) |
657 | | * *kulBufLen Length of new kul buffer |
658 | | * |
659 | | * Returns: |
660 | | * SNMPERR_SUCCESS Success. |
661 | | * SNMPERR_GENERR All errors. |
662 | | */ |
663 | | int |
664 | | netsnmp_extend_kul(u_int needKeyLen, oid *hashoid, u_int hashoid_len, |
665 | | int privType, u_char *engineID, u_int engineIDLen, |
666 | | u_char **kulBuf, size_t *kulBufLen, u_int kulBufSize) |
667 | 0 | { |
668 | 0 | int ret; |
669 | 0 | u_char *newKul; |
670 | 0 | size_t newKulLen; |
671 | |
|
672 | 0 | DEBUGMSGTL(("9:usm:extend_kul", " called\n")); |
673 | |
|
674 | 0 | if (*kulBufLen >= needKeyLen) { |
675 | 0 | DEBUGMSGTL(("usm:extend_kul", " key already big enough\n")); |
676 | 0 | return SNMPERR_SUCCESS; /* already have enough key material */ |
677 | 0 | } |
678 | | |
679 | 0 | switch (privType & (USM_PRIV_MASK_ALG | USM_PRIV_MASK_VARIANT)) { |
680 | 0 | #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 |
681 | 0 | case USM_CREATE_USER_PRIV_AES192: |
682 | 0 | case USM_CREATE_USER_PRIV_AES256: |
683 | 0 | break; |
684 | 0 | #endif |
685 | 0 | #if defined(NETSNMP_DRAFT_REEDER_3DES) || defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) |
686 | 0 | case USM_CREATE_USER_PRIV_3DES: |
687 | 0 | break; |
688 | 0 | #endif |
689 | 0 | default: |
690 | 0 | DEBUGMSGTL(("usm:extend_kul", |
691 | 0 | "no extension method defined for priv type 0x%x\n", |
692 | 0 | privType)); |
693 | 0 | return SNMPERR_SUCCESS; |
694 | 0 | } |
695 | | |
696 | 0 | DEBUGMSGTL(("usm:extend_kul", " have %" NETSNMP_PRIz "d bytes; need %d\n", |
697 | 0 | *kulBufLen, needKeyLen)); |
698 | |
|
699 | 0 | if (kulBufSize < needKeyLen) { |
700 | 0 | newKul = calloc(1, needKeyLen); |
701 | 0 | if (NULL == newKul) |
702 | 0 | return SNMPERR_GENERR; |
703 | 0 | memcpy(newKul, *kulBuf, *kulBufLen); |
704 | 0 | newKulLen = *kulBufLen; |
705 | 0 | kulBufSize = needKeyLen; |
706 | 0 | } |
707 | 0 | else { |
708 | 0 | newKul = *kulBuf; |
709 | 0 | newKulLen = *kulBufLen; |
710 | 0 | } |
711 | | |
712 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
713 | | DEBUGIF("9:usm:extend_kul") { |
714 | | int i; |
715 | | DEBUGMSG(("9:usm:extend_kul", |
716 | | "key: key=0x")); |
717 | | for (i = 0; i < newKulLen; i++) |
718 | | DEBUGMSG(("9:usm:extend_kul", "%02x", newKul[i] & 0xff)); |
719 | | DEBUGMSG(("9:usm:extend_kul", " (%ld)\n", newKulLen)); |
720 | | } |
721 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
722 | 0 | ret = SNMPERR_SUCCESS; /* most privTypes don't need extended kul */ |
723 | 0 | switch (privType & (USM_PRIV_MASK_ALG | USM_PRIV_MASK_VARIANT)) { |
724 | 0 | #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 |
725 | 0 | case USM_CREATE_USER_PRIV_AES192: |
726 | 0 | case USM_CREATE_USER_PRIV_AES256: |
727 | 0 | { |
728 | 0 | int reeder = privType & USM_AES_REEDER_FLAG; |
729 | 0 | if (reeder) |
730 | 0 | ret = _kul_extend_reeder(needKeyLen, hashoid, hashoid_len, |
731 | 0 | engineID, engineIDLen, |
732 | 0 | newKul, &newKulLen, kulBufSize); |
733 | 0 | else |
734 | 0 | ret = _kul_extend_blumenthal(needKeyLen, hashoid, hashoid_len, |
735 | 0 | newKul, &newKulLen, kulBufSize); |
736 | 0 | } |
737 | 0 | break; |
738 | 0 | #endif |
739 | | #if defined(NETSNMP_DRAFT_REEDER_3DES) |
740 | | case USM_CREATE_USER_PRIV_3DES: |
741 | | ret = _kul_extend_reeder(needKeyLen, hashoid, hashoid_len, |
742 | | engineID, engineIDLen, |
743 | | newKul, &newKulLen, kulBufSize); |
744 | | break; |
745 | | #endif |
746 | 0 | default: |
747 | 0 | DEBUGMSGTL(("usm:extend_kul", |
748 | 0 | "unknown priv type 0x%x\n", privType)); |
749 | 0 | ret = SNMPERR_GENERR; |
750 | 0 | } |
751 | | |
752 | 0 | if (SNMPERR_SUCCESS == ret) { |
753 | 0 | *kulBufLen = newKulLen; |
754 | 0 | if (newKul != *kulBuf) { |
755 | 0 | free(*kulBuf); |
756 | 0 | *kulBuf = newKul; |
757 | 0 | } |
758 | 0 | } |
759 | 0 | else { |
760 | 0 | if (newKul != *kulBuf) |
761 | 0 | free(newKul); |
762 | 0 | } |
763 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
764 | | DEBUGIF("9:usm:extend_kul") { |
765 | | int i; |
766 | | DEBUGMSG(("usm:extend_kul", |
767 | | "key: key=0x")); |
768 | | for (i = 0; i < newKulLen; i++) |
769 | | DEBUGMSG(("usm:extend_kul", "%02x", newKul[i] & 0xff)); |
770 | | DEBUGMSG(("usm:extend_kul", " (%ld)\n", newKulLen)); |
771 | | } |
772 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
773 | |
|
774 | 0 | return ret; |
775 | 0 | } |
776 | | |
777 | | /*******************************************************************-o-****** |
778 | | * encode_keychange |
779 | | * |
780 | | * Parameters: |
781 | | * *hashtype MIB OID for the hash transform type. |
782 | | * hashtype_len Length of the MIB OID hash transform type. |
783 | | * *oldkey Old key that is used to encodes the new key. |
784 | | * oldkey_len Length of oldkey in bytes. |
785 | | * *newkey New key that is encoded using the old key. |
786 | | * newkey_len Length of new key in bytes. |
787 | | * *kcstring Buffer to contain the KeyChange TC string. |
788 | | * *kcstring_len Length of kcstring buffer. |
789 | | * |
790 | | * Returns: |
791 | | * SNMPERR_SUCCESS Success. |
792 | | * SNMPERR_GENERR All errors. |
793 | | * |
794 | | * |
795 | | * Uses oldkey and acquired random bytes to encode newkey into kcstring |
796 | | * according to the rules of the KeyChange TC described in RFC 2274, Section 5. |
797 | | * |
798 | | * Upon successful return, *kcstring_len contains the length of the |
799 | | * encoded string. |
800 | | * |
801 | | * ASSUMES Old and new key are always equal to each other, although |
802 | | * this may be less than the transform type hash output |
803 | | * output length (eg, using KeyChange for a DESPriv key when |
804 | | * the user also uses SHA1Auth). This also implies that the |
805 | | * hash placed in the second 1/2 of the key change string |
806 | | * will be truncated before the XOR'ing when the hash output is |
807 | | * larger than that 1/2 of the key change string. |
808 | | * |
809 | | * *kcstring_len will be returned as exactly twice that same |
810 | | * length though the input buffer may be larger. |
811 | | * |
812 | | * XXX FIX: Does not handle variable length keys. |
813 | | * XXX FIX: Does not handle keys larger than the hash algorithm used. |
814 | | * |
815 | | KeyChange ::= TEXTUAL-CONVENTION |
816 | | STATUS current |
817 | | DESCRIPTION |
818 | | "Every definition of an object with this syntax must identify |
819 | | a protocol P, a secret key K, and a hash algorithm H |
820 | | that produces output of L octets. |
821 | | |
822 | | The object's value is a manager-generated, partially-random |
823 | | value which, when modified, causes the value of the secret |
824 | | key K, to be modified via a one-way function. |
825 | | |
826 | | The value of an instance of this object is the concatenation |
827 | | of two components: first a 'random' component and then a |
828 | | 'delta' component. |
829 | | |
830 | | The lengths of the random and delta components |
831 | | are given by the corresponding value of the protocol P; |
832 | | if P requires K to be a fixed length, the length of both the |
833 | | random and delta components is that fixed length; if P |
834 | | allows the length of K to be variable up to a particular |
835 | | maximum length, the length of the random component is that |
836 | | maximum length and the length of the delta component is any |
837 | | length less than or equal to that maximum length. |
838 | | For example, usmHMACMD5AuthProtocol requires K to be a fixed |
839 | | length of 16 octets and L - of 16 octets. |
840 | | usmHMACSHAAuthProtocol requires K to be a fixed length of |
841 | | 20 octets and L - of 20 octets. Other protocols may define |
842 | | other sizes, as deemed appropriate. |
843 | | |
844 | | When a requester wants to change the old key K to a new |
845 | | key keyNew on a remote entity, the 'random' component is |
846 | | obtained from either a true random generator, or from a |
847 | | pseudorandom generator, and the 'delta' component is |
848 | | computed as follows: |
849 | | |
850 | | - a temporary variable is initialized to the existing value |
851 | | of K; |
852 | | - if the length of the keyNew is greater than L octets, |
853 | | then: |
854 | | - the random component is appended to the value of the |
855 | | temporary variable, and the result is input to the |
856 | | the hash algorithm H to produce a digest value, and |
857 | | the temporary variable is set to this digest value; |
858 | | - the value of the temporary variable is XOR-ed with |
859 | | the first (next) L-octets (16 octets in case of MD5) |
860 | | of the keyNew to produce the first (next) L-octets |
861 | | (16 octets in case of MD5) of the 'delta' component. |
862 | | - the above two steps are repeated until the unused |
863 | | portion of the keyNew component is L octets or less, |
864 | | - the random component is appended to the value of the |
865 | | temporary variable, and the result is input to the |
866 | | hash algorithm H to produce a digest value; |
867 | | - this digest value, truncated if necessary to be the same |
868 | | length as the unused portion of the keyNew, is XOR-ed |
869 | | with the unused portion of the keyNew to produce the |
870 | | (final portion of the) 'delta' component. |
871 | | |
872 | | For example, using MD5 as the hash algorithm H: |
873 | | |
874 | | iterations = (lenOfDelta - 1)/16; -- integer division |
875 | | temp = keyOld; |
876 | | for (i = 0; i < iterations; i++) { |
877 | | temp = MD5 (temp || random); |
878 | | delta[i*16 .. (i*16)+15] = |
879 | | temp XOR keyNew[i*16 .. (i*16)+15]; |
880 | | } |
881 | | temp = MD5 (temp || random); |
882 | | delta[i*16 .. lenOfDelta-1] = |
883 | | temp XOR keyNew[i*16 .. lenOfDelta-1]; |
884 | | |
885 | | The 'random' and 'delta' components are then concatenated as |
886 | | described above, and the resulting octet string is sent to |
887 | | the recipient as the new value of an instance of this object. |
888 | | |
889 | | At the receiver side, when an instance of this object is set |
890 | | to a new value, then a new value of K is computed as follows: |
891 | | |
892 | | - a temporary variable is initialized to the existing value |
893 | | of K; |
894 | | - if the length of the delta component is greater than L |
895 | | octets, then: |
896 | | - the random component is appended to the value of the |
897 | | temporary variable, and the result is input to the |
898 | | hash algorithm H to produce a digest value, and the |
899 | | temporary variable is set to this digest value; |
900 | | - the value of the temporary variable is XOR-ed with |
901 | | the first (next) L-octets (16 octets in case of MD5) |
902 | | of the delta component to produce the first (next) |
903 | | L-octets (16 octets in case of MD5) of the new value |
904 | | of K. |
905 | | - the above two steps are repeated until the unused |
906 | | portion of the delta component is L octets or less, |
907 | | - the random component is appended to the value of the |
908 | | temporary variable, and the result is input to the |
909 | | hash algorithm H to produce a digest value; |
910 | | - this digest value, truncated if necessary to be the same |
911 | | length as the unused portion of the delta component, is |
912 | | XOR-ed with the unused portion of the delta component to |
913 | | produce the (final portion of the) new value of K. |
914 | | |
915 | | For example, using MD5 as the hash algorithm H: |
916 | | |
917 | | iterations = (lenOfDelta - 1)/16; -- integer division |
918 | | temp = keyOld; |
919 | | for (i = 0; i < iterations; i++) { |
920 | | temp = MD5 (temp || random); |
921 | | keyNew[i*16 .. (i*16)+15] = |
922 | | temp XOR delta[i*16 .. (i*16)+15]; |
923 | | } |
924 | | temp = MD5 (temp || random); |
925 | | keyNew[i*16 .. lenOfDelta-1] = |
926 | | temp XOR delta[i*16 .. lenOfDelta-1]; |
927 | | |
928 | | The value of an object with this syntax, whenever it is |
929 | | retrieved by the management protocol, is always the zero |
930 | | length string. |
931 | | |
932 | | Note that the keyOld and keyNew are the localized keys. |
933 | | |
934 | | Note that it is probably wise that when an SNMP entity sends |
935 | | a SetRequest to change a key, that it keeps a copy of the old |
936 | | key until it has confirmed that the key change actually |
937 | | succeeded. |
938 | | " |
939 | | SYNTAX OCTET STRING |
940 | | * |
941 | | */ |
942 | | int |
943 | | encode_keychange(const oid * hashtype, u_int hashtype_len, |
944 | | u_char * oldkey, size_t oldkey_len, |
945 | | u_char * newkey, size_t newkey_len, |
946 | | u_char * kcstring, size_t * kcstring_len) |
947 | | #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO) |
948 | 0 | { |
949 | 0 | u_char tmpbuf[SNMP_MAXBUF_SMALL], digest[SNMP_MAXBUF_SMALL]; |
950 | 0 | u_char delta[SNMP_MAXBUF_SMALL]; |
951 | 0 | u_char *tmpp = tmpbuf, *randp; |
952 | 0 | int rval = SNMPERR_SUCCESS, auth_type; |
953 | 0 | int iauth_length, tmp_len; |
954 | 0 | size_t auth_length, rand_len, digest_len, delta_len = 0; |
955 | | |
956 | | /* |
957 | | * Sanity check. |
958 | | */ |
959 | 0 | if (!hashtype || !oldkey || !newkey || !kcstring |
960 | 0 | || (oldkey_len != newkey_len ) || (newkey_len == 0) |
961 | 0 | || (*kcstring_len < (2 * newkey_len))) { |
962 | 0 | QUITFUN(SNMPERR_GENERR, encode_keychange_quit); |
963 | 0 | } |
964 | | |
965 | | /* |
966 | | * Setup for the transform type. |
967 | | */ |
968 | 0 | auth_type = sc_get_authtype(hashtype, hashtype_len); |
969 | 0 | iauth_length = sc_get_proper_auth_length_bytype(auth_type); |
970 | 0 | if (iauth_length <= 0) |
971 | 0 | QUITFUN(SNMPERR_GENERR, encode_keychange_quit); |
972 | |
|
973 | 0 | if (oldkey_len > sizeof(tmpbuf) / 2 || |
974 | 0 | oldkey_len > sizeof(tmpbuf) - (size_t)iauth_length || |
975 | 0 | newkey_len > sizeof(delta)) { |
976 | 0 | QUITFUN(SNMPERR_GENERR, encode_keychange_quit); |
977 | 0 | } |
978 | | |
979 | 0 | auth_length = SNMP_MIN(oldkey_len, (size_t)iauth_length); |
980 | |
|
981 | 0 | DEBUGMSGTL(("encode_keychange", |
982 | 0 | "oldkey_len %" NETSNMP_PRIz "d, newkey_len %" NETSNMP_PRIz "d, auth_length %" NETSNMP_PRIz "d\n", |
983 | 0 | oldkey_len, newkey_len, auth_length)); |
984 | | |
985 | | /* |
986 | | * Use the old key and some random bytes to encode the new key |
987 | | * in the KeyChange TC format: |
988 | | * . Get random bytes (store in first half of kcstring), |
989 | | * . Hash (oldkey | random_bytes) (into second half of kcstring), |
990 | | * . XOR hash and newkey (into second half of kcstring). |
991 | | * |
992 | | * Getting the wrong number of random bytes is considered an error. |
993 | | */ |
994 | 0 | randp = kcstring; |
995 | 0 | rand_len = oldkey_len; |
996 | |
|
997 | 0 | memset(randp, 0x0, rand_len); |
998 | | /* |
999 | | * KeyChange ::= TEXTUAL-CONVENTION |
1000 | | * STATUS current |
1001 | | * DESCRIPTION |
1002 | | * [...] |
1003 | | * When a requester wants to change the old key K to a new |
1004 | | * key keyNew on a remote entity, the 'random' component is |
1005 | | * obtained from either a true random generator, or from a |
1006 | | * pseudorandom generator, ... |
1007 | | */ |
1008 | | #if defined(NETSNMP_ENABLE_TESTING_CODE) && defined(RANDOMZEROS) |
1009 | | memset(randp, 0, rand_len); |
1010 | | DEBUGMSG(("encode_keychange", |
1011 | | "** Using all zero bits for \"random\" delta of )" |
1012 | | "the keychange string! **\n")); |
1013 | | #else /* !NETSNMP_ENABLE_TESTING_CODE */ |
1014 | 0 | rval = sc_random(randp, &rand_len); |
1015 | 0 | QUITFUN(rval, encode_keychange_quit); |
1016 | 0 | if (rand_len != oldkey_len) { |
1017 | 0 | QUITFUN(SNMPERR_GENERR, encode_keychange_quit); |
1018 | 0 | } |
1019 | 0 | #endif /* !NETSNMP_ENABLE_TESTING_CODE */ |
1020 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1021 | | DEBUGIF("encode_keychange") { |
1022 | | int i; |
1023 | | DEBUGMSG(("encode_keychange", |
1024 | | "rand: key=0x")); |
1025 | | for (i = 0; i < rand_len; i++) |
1026 | | DEBUGMSG(("encode_keychange", "%02x", randp[i] & 0xff)); |
1027 | | DEBUGMSG(("encode_keychange", " (%ld)\n", rand_len)); |
1028 | | } |
1029 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
1030 | | |
1031 | | /* |
1032 | | * ... and the 'delta' component is |
1033 | | * computed as follows: |
1034 | | * |
1035 | | * - a temporary variable is initialized to the existing value |
1036 | | * of K; |
1037 | | */ |
1038 | 0 | memcpy(tmpbuf, oldkey, oldkey_len); |
1039 | 0 | tmp_len = oldkey_len; |
1040 | 0 | tmpp = tmpbuf + tmp_len; |
1041 | |
|
1042 | 0 | delta_len = 0; |
1043 | 0 | while (delta_len < newkey_len) { |
1044 | 0 | DEBUGMSGTL(("encode_keychange", "%" NETSNMP_PRIz "d < %" NETSNMP_PRIz "d\n", delta_len, |
1045 | 0 | newkey_len)); |
1046 | | |
1047 | | /* |
1048 | | * - the random component is appended to the value of the |
1049 | | * temporary variable, |
1050 | | */ |
1051 | 0 | memcpy(tmpp, randp, rand_len); |
1052 | 0 | tmp_len += rand_len; |
1053 | | |
1054 | | /* |
1055 | | * and the result is input to the |
1056 | | * the hash algorithm H to produce a digest value, and |
1057 | | * the temporary variable is set to this digest value; |
1058 | | */ |
1059 | 0 | digest_len = sizeof(digest); |
1060 | 0 | rval = sc_hash(hashtype, hashtype_len, tmpbuf, tmp_len, |
1061 | 0 | digest, &digest_len); |
1062 | 0 | QUITFUN(rval, encode_keychange_quit); |
1063 | 0 | DEBUGMSGTL(("encode_keychange", "digest_len %" NETSNMP_PRIz "d\n", digest_len)); |
1064 | |
|
1065 | 0 | memcpy(tmpbuf, digest, digest_len); |
1066 | 0 | tmp_len = digest_len; |
1067 | 0 | tmpp = tmpbuf; |
1068 | | /* |
1069 | | * - the value of the temporary variable is XOR-ed with |
1070 | | * the first (next) L-octets (16 octets in case of MD5) |
1071 | | * of the keyNew to produce the first (next) L-octets |
1072 | | * (16 octets in case of MD5) of the 'delta' component. |
1073 | | * - the above two steps are repeated until the unused |
1074 | | * portion of the keyNew component is L octets or less, |
1075 | | */ |
1076 | 0 | while ((delta_len < newkey_len) && (digest_len-- > 0)) { |
1077 | 0 | delta[delta_len] = *tmpp ^ newkey[delta_len]; |
1078 | 0 | DEBUGMSGTL(("encode_keychange", |
1079 | 0 | "d[%" NETSNMP_PRIz "d] 0x%0x = 0x%0x ^ 0x%0x\n", |
1080 | 0 | delta_len, delta[delta_len], *tmpp, newkey[delta_len])); |
1081 | 0 | ++tmpp; |
1082 | 0 | ++delta_len; |
1083 | 0 | } |
1084 | 0 | DEBUGMSGTL(("encode_keychange", "delta_len %" NETSNMP_PRIz "d\n", delta_len)); |
1085 | 0 | } |
1086 | | |
1087 | | /** copy results */ |
1088 | 0 | memcpy(kcstring + rand_len, delta, delta_len); |
1089 | 0 | *kcstring_len = rand_len + delta_len; |
1090 | |
|
1091 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1092 | | DEBUGIF("encode_keychange") { |
1093 | | int i; |
1094 | | DEBUGMSG(("encode_keychange", |
1095 | | "oldkey: key=0x")); |
1096 | | for (i = 0; i < oldkey_len; i++) |
1097 | | DEBUGMSG(("encode_keychange", "%02x", oldkey[i] & 0xff)); |
1098 | | DEBUGMSG(("encode_keychange", " (%ld)\n", oldkey_len)); |
1099 | | |
1100 | | DEBUGMSG(("encode_keychange", |
1101 | | "newkey: key=0x")); |
1102 | | for (i = 0; i < newkey_len; i++) |
1103 | | DEBUGMSG(("encode_keychange", "%02x", newkey[i] & 0xff)); |
1104 | | DEBUGMSG(("encode_keychange", " (%ld)\n", newkey_len)); |
1105 | | |
1106 | | DEBUGMSG(("encode_keychange", |
1107 | | "kcstring: key=0x")); |
1108 | | for (i = 0; i < *kcstring_len; i++) |
1109 | | DEBUGMSG(("encode_keychange", "%02x", kcstring[i] & 0xff)); |
1110 | | DEBUGMSG(("encode_keychange", " (%ld)\n", *kcstring_len)); |
1111 | | } |
1112 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
1113 | 0 | encode_keychange_quit: |
1114 | 0 | if (kcstring && rval != SNMPERR_SUCCESS) |
1115 | 0 | memset(kcstring, 0, *kcstring_len); |
1116 | 0 | memset(tmpbuf, 0, sizeof(tmpbuf)); |
1117 | 0 | memset(digest, 0, sizeof(digest)); |
1118 | 0 | memset(delta, 0, sizeof(delta)); |
1119 | |
|
1120 | 0 | return rval; |
1121 | |
|
1122 | 0 | } /* end encode_keychange() */ |
1123 | | |
1124 | | #else |
1125 | | _KEYTOOLS_NOT_AVAILABLE |
1126 | | #endif /* internal or openssl */ |
1127 | | /*******************************************************************-o-****** |
1128 | | * decode_keychange |
1129 | | * |
1130 | | * Parameters: |
1131 | | * *hashtype MIB OID of the hash transform to use. |
1132 | | * hashtype_len Length of the hash transform MIB OID. |
1133 | | * *oldkey Old key that is used to encode the new key. |
1134 | | * oldkey_len Length of oldkey in bytes. |
1135 | | * *kcstring Encoded KeyString buffer containing the new key. |
1136 | | * kcstring_len Length of kcstring in bytes. |
1137 | | * *newkey Buffer to hold the extracted new key. |
1138 | | * *newkey_len Length of newkey in bytes. |
1139 | | * |
1140 | | * Returns: |
1141 | | * SNMPERR_SUCCESS Success. |
1142 | | * SNMPERR_GENERR All errors. |
1143 | | * |
1144 | | * |
1145 | | * Decodes a string of bits encoded according to the KeyChange TC described |
1146 | | * in RFC 2274, Section 5. The new key is extracted from *kcstring with |
1147 | | * the aid of the old key. |
1148 | | * |
1149 | | * Upon successful return, *newkey_len contains the length of the new key. |
1150 | | * |
1151 | | * |
1152 | | * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer, |
1153 | | * although this length may not be equal to the hash transform |
1154 | | * output. Thus the new key length will be equal to the old |
1155 | | * key length. |
1156 | | * |
1157 | | KeyChange ::= TEXTUAL-CONVENTION |
1158 | | STATUS current |
1159 | | DESCRIPTION |
1160 | | "Every definition of an object with this syntax must identify |
1161 | | a protocol P, a secret key K, and a hash algorithm H |
1162 | | that produces output of L octets. |
1163 | | |
1164 | | The object's value is a manager-generated, partially-random |
1165 | | value which, when modified, causes the value of the secret |
1166 | | key K, to be modified via a one-way function. |
1167 | | |
1168 | | The value of an instance of this object is the concatenation |
1169 | | of two components: first a 'random' component and then a |
1170 | | 'delta' component. |
1171 | | |
1172 | | The lengths of the random and delta components |
1173 | | are given by the corresponding value of the protocol P; |
1174 | | if P requires K to be a fixed length, the length of both the |
1175 | | random and delta components is that fixed length; if P |
1176 | | allows the length of K to be variable up to a particular |
1177 | | maximum length, the length of the random component is that |
1178 | | maximum length and the length of the delta component is any |
1179 | | length less than or equal to that maximum length. |
1180 | | For example, usmHMACMD5AuthProtocol requires K to be a fixed |
1181 | | length of 16 octets and L - of 16 octets. |
1182 | | usmHMACSHAAuthProtocol requires K to be a fixed length of |
1183 | | 20 octets and L - of 20 octets. Other protocols may define |
1184 | | other sizes, as deemed appropriate. |
1185 | | |
1186 | | When a requester wants to change the old key K to a new |
1187 | | [... see encode_keychange above ...] |
1188 | | |
1189 | | At the receiver side, when an instance of this object is set |
1190 | | to a new value, then a new value of K is computed as follows: |
1191 | | |
1192 | | - a temporary variable is initialized to the existing value |
1193 | | of K; |
1194 | | - if the length of the delta component is greater than L |
1195 | | octets, then: |
1196 | | - the random component is appended to the value of the |
1197 | | temporary variable, and the result is input to the |
1198 | | hash algorithm H to produce a digest value, and the |
1199 | | temporary variable is set to this digest value; |
1200 | | - the value of the temporary variable is XOR-ed with |
1201 | | the first (next) L-octets (16 octets in case of MD5) |
1202 | | of the delta component to produce the first (next) |
1203 | | L-octets (16 octets in case of MD5) of the new value |
1204 | | of K. |
1205 | | - the above two steps are repeated until the unused |
1206 | | portion of the delta component is L octets or less, |
1207 | | - the random component is appended to the value of the |
1208 | | temporary variable, and the result is input to the |
1209 | | hash algorithm H to produce a digest value; |
1210 | | - this digest value, truncated if necessary to be the same |
1211 | | length as the unused portion of the delta component, is |
1212 | | XOR-ed with the unused portion of the delta component to |
1213 | | produce the (final portion of the) new value of K. |
1214 | | |
1215 | | For example, using MD5 as the hash algorithm H: |
1216 | | |
1217 | | iterations = (lenOfDelta - 1)/16; -- integer division |
1218 | | temp = keyOld; |
1219 | | for (i = 0; i < iterations; i++) { |
1220 | | temp = MD5 (temp || random); |
1221 | | keyNew[i*16 .. (i*16)+15] = |
1222 | | temp XOR delta[i*16 .. (i*16)+15]; |
1223 | | } |
1224 | | temp = MD5 (temp || random); |
1225 | | keyNew[i*16 .. lenOfDelta-1] = |
1226 | | temp XOR delta[i*16 .. lenOfDelta-1]; |
1227 | | |
1228 | | The value of an object with this syntax, whenever it is |
1229 | | retrieved by the management protocol, is always the zero |
1230 | | length string. |
1231 | | |
1232 | | Note that the keyOld and keyNew are the localized keys. |
1233 | | |
1234 | | Note that it is probably wise that when an SNMP entity sends |
1235 | | a SetRequest to change a key, that it keeps a copy of the old |
1236 | | key until it has confirmed that the key change actually |
1237 | | succeeded. |
1238 | | " |
1239 | | SYNTAX OCTET STRING |
1240 | | */ |
1241 | | /* |
1242 | | * XXX: if the newkey is not long enough, it should be freed and remalloced |
1243 | | */ |
1244 | | int |
1245 | | decode_keychange(const oid *hashtype, u_int hashtype_len, |
1246 | | const u_char *oldkey, size_t oldkey_len, |
1247 | | const u_char *kcstring, size_t kcstring_len, |
1248 | | u_char *newkey, size_t *newkey_len) |
1249 | | #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO) |
1250 | 0 | { |
1251 | 0 | int rval = SNMPERR_SUCCESS, auth_type; |
1252 | 0 | size_t hash_len = 0, key_len = 0; |
1253 | 0 | int ihash_len = 0; |
1254 | 0 | u_int nbytes = 0; |
1255 | |
|
1256 | 0 | const u_char *deltap; |
1257 | 0 | u_char hash[SNMP_MAXBUF]; |
1258 | 0 | size_t delta_len, tmpbuf_len; |
1259 | 0 | u_char *tmpbuf = NULL; |
1260 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1261 | | u_char *newkey_save = newkey; |
1262 | | #endif |
1263 | | |
1264 | | /* |
1265 | | * Sanity check. |
1266 | | */ |
1267 | 0 | if (!hashtype || !oldkey || !kcstring || !newkey || !newkey_len |
1268 | 0 | || (oldkey_len == 0) || (kcstring_len == 0) || (*newkey_len == 0)) { |
1269 | 0 | DEBUGMSGTL(("decode_keychange", "bad args\n")); |
1270 | 0 | QUITFUN(SNMPERR_GENERR, decode_keychange_quit); |
1271 | 0 | } |
1272 | | |
1273 | | /* |
1274 | | * Setup for the transform type. |
1275 | | */ |
1276 | 0 | auth_type = sc_get_authtype(hashtype, hashtype_len); |
1277 | 0 | ihash_len = sc_get_proper_auth_length_bytype(auth_type); |
1278 | 0 | if (ihash_len == SNMPERR_GENERR) { |
1279 | 0 | DEBUGMSGTL(("decode_keychange", "proper length err\n")); |
1280 | 0 | QUITFUN(SNMPERR_GENERR, decode_keychange_quit); |
1281 | 0 | } |
1282 | 0 | hash_len = (size_t) ihash_len; |
1283 | 0 | DEBUGMSGTL(("decode_keychange", |
1284 | 0 | "oldkey_len %" NETSNMP_PRIz "d, newkey_len %" NETSNMP_PRIz "d, kcstring_len %" NETSNMP_PRIz "d, hash_len %" NETSNMP_PRIz "d\n", |
1285 | 0 | oldkey_len, *newkey_len, kcstring_len, hash_len)); |
1286 | |
|
1287 | 0 | if (((oldkey_len * 2) != kcstring_len) || (*newkey_len < oldkey_len)) { |
1288 | 0 | DEBUGMSGTL(("decode_keychange", "keylen error\n")); |
1289 | 0 | QUITFUN(SNMPERR_GENERR, decode_keychange_quit); |
1290 | 0 | } |
1291 | | |
1292 | | /*********** handle hash len > keylen ******************/ |
1293 | | |
1294 | 0 | key_len = oldkey_len; |
1295 | 0 | *newkey_len = oldkey_len; |
1296 | |
|
1297 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1298 | | DEBUGIF("decode_keychange") { |
1299 | | int i; |
1300 | | DEBUGMSG(("decode_keychange", |
1301 | | "oldkey: key=0x")); |
1302 | | for (i = 0; i < oldkey_len; i++) |
1303 | | DEBUGMSG(("decode_keychange", "%02x", oldkey[i] & 0xff)); |
1304 | | DEBUGMSG(("decode_keychange", " (%ld)\n", oldkey_len)); |
1305 | | |
1306 | | DEBUGMSG(("decode_keychange", |
1307 | | "kcstring: key=0x")); |
1308 | | for (i = 0; i < kcstring_len; i++) |
1309 | | DEBUGMSG(("decode_keychange", "%02x", kcstring[i] & 0xff)); |
1310 | | DEBUGMSG(("decode_keychange", " (%ld)\n", kcstring_len)); |
1311 | | } |
1312 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
1313 | | |
1314 | | /* |
1315 | | * KeyChange ::= TEXTUAL-CONVENTION |
1316 | | * STATUS current |
1317 | | * DESCRIPTION |
1318 | | * [...] |
1319 | | * At the receiver side, when an instance of this object is set |
1320 | | * to a new value, then a new value of K is computed as follows: |
1321 | | * |
1322 | | * - a temporary variable is initialized to the existing value |
1323 | | * of K; |
1324 | | */ |
1325 | 0 | tmpbuf = (u_char *) malloc(key_len * 2); |
1326 | 0 | if (NULL == tmpbuf) { |
1327 | 0 | DEBUGMSGTL(("decode_keychange", "malloc failed\n")); |
1328 | 0 | QUITFUN(SNMPERR_GENERR, decode_keychange_quit); |
1329 | 0 | } |
1330 | 0 | memcpy(tmpbuf, oldkey, key_len); |
1331 | 0 | tmpbuf_len = key_len; |
1332 | | |
1333 | | /* |
1334 | | * key=0xe27c077c47d4cb4e4473aeac969ba9fa622486e0|d7440406892e0941175cb5ee |
1335 | | * key=0xe27c077c47d4cb4e4473aeac969ba9fa622486e0|4fa8e081a6cb2f089f40949c |
1336 | | */ |
1337 | 0 | delta_len = 0; |
1338 | 0 | deltap = kcstring + key_len; |
1339 | 0 | while (delta_len < key_len) { |
1340 | | |
1341 | | /* |
1342 | | * - if the length of the delta component is greater than L |
1343 | | * octets, then: |
1344 | | * - the random component is appended to the value of the |
1345 | | * temporary variable, ... |
1346 | | */ |
1347 | 0 | DEBUGMSGTL(("decode_keychange", |
1348 | 0 | "append random tmpbuf_len %" NETSNMP_PRIz "d key_len %" NETSNMP_PRIz "d\n", |
1349 | 0 | tmpbuf_len, key_len)); |
1350 | 0 | memcpy(tmpbuf + tmpbuf_len, kcstring, key_len); |
1351 | 0 | tmpbuf_len += key_len; |
1352 | | |
1353 | | /* |
1354 | | * ... and the result is input to the |
1355 | | * hash algorithm H to produce a digest value, ... |
1356 | | */ |
1357 | 0 | hash_len = sizeof(hash); |
1358 | 0 | DEBUGMSGTL(("decode_keychange", "get hash\n")); |
1359 | 0 | rval = sc_hash(hashtype, hashtype_len, tmpbuf, tmpbuf_len, |
1360 | 0 | hash, &hash_len); |
1361 | 0 | QUITFUN(rval, decode_keychange_quit); |
1362 | 0 | if (hash_len > key_len) { |
1363 | 0 | DEBUGMSGTL(("decode_keychange", |
1364 | 0 | "truncating hash to key_len\n")); |
1365 | 0 | hash_len = key_len; |
1366 | 0 | } |
1367 | | |
1368 | | /* |
1369 | | * ... and the |
1370 | | * temporary variable is set to this digest value; |
1371 | | */ |
1372 | 0 | DEBUGMSGTL(("decode_keychange", "copy %" NETSNMP_PRIz "d hash bytes to tmp\n", |
1373 | 0 | hash_len)); |
1374 | 0 | memcpy(tmpbuf, hash, hash_len); |
1375 | 0 | tmpbuf_len = hash_len; |
1376 | | |
1377 | | /* |
1378 | | * - the value of the temporary variable is XOR-ed with |
1379 | | * the first (next) L-octets (16 octets in case of MD5) |
1380 | | * of the delta component to produce the first (next) |
1381 | | * L-octets (16 octets in case of MD5) of the new value |
1382 | | * of K. |
1383 | | */ |
1384 | 0 | DEBUGMSGTL(("decode_keychange", |
1385 | 0 | "xor to get new key; hash_len %" NETSNMP_PRIz "d delta_len %" NETSNMP_PRIz "d\n", |
1386 | 0 | hash_len, delta_len)); |
1387 | 0 | nbytes = 0; |
1388 | 0 | while ((nbytes < hash_len) && (delta_len < key_len)) { |
1389 | 0 | newkey[delta_len] = tmpbuf[nbytes++] ^ deltap[delta_len]; |
1390 | 0 | ++delta_len; |
1391 | 0 | } |
1392 | 0 | } |
1393 | | |
1394 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
1395 | | DEBUGIF("decode_keychange") { |
1396 | | int i; |
1397 | | DEBUGMSG(("decode_keychange", |
1398 | | "newkey: key=0x")); |
1399 | | for (i = 0; i < *newkey_len; i++) |
1400 | | DEBUGMSG(("decode_keychange", "%02x", newkey_save[i] & 0xff)); |
1401 | | DEBUGMSG(("decode_keychange", " (%ld)\n", *newkey_len)); |
1402 | | } |
1403 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
1404 | | |
1405 | 0 | decode_keychange_quit: |
1406 | 0 | if (rval != SNMPERR_SUCCESS) { |
1407 | 0 | DEBUGMSGTL(("decode_keychange", "error %d\n", rval)); |
1408 | 0 | if (newkey) |
1409 | 0 | memset(newkey, 0, key_len); |
1410 | 0 | } |
1411 | 0 | memset(hash, 0, SNMP_MAXBUF); |
1412 | 0 | SNMP_FREE(tmpbuf); |
1413 | |
|
1414 | 0 | return rval; |
1415 | |
|
1416 | 0 | } /* end decode_keychange() */ |
1417 | | |
1418 | | #else |
1419 | | _KEYTOOLS_NOT_AVAILABLE |
1420 | | #endif /* internal or openssl */ |
1421 | | #endif /* NETSNMP_FEATURE_REMOVE_USM_KEYTOOLS */ |