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