net-snmp
5.4.1
|
00001 /* Portions of this file are subject to the following copyright(s). See 00002 * the Net-SNMP's COPYING file for more details and other copyrights 00003 * that may apply: 00004 */ 00005 /* 00006 * Portions of this file are copyrighted by: 00007 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. 00008 * Use is subject to license terms specified in the COPYING file 00009 * distributed with the Net-SNMP package. 00010 */ 00011 00012 /* 00013 * keytools.c 00014 */ 00015 00016 #include <net-snmp/net-snmp-config.h> 00017 00018 #include <stdio.h> 00019 #include <sys/types.h> 00020 #if HAVE_WINSOCK_H 00021 #include <winsock.h> 00022 #endif 00023 #ifdef HAVE_NETINET_IN_H 00024 #include <netinet/in.h> 00025 #endif 00026 #ifdef HAVE_STDLIB_H 00027 #include <stdlib.h> 00028 #endif 00029 #if HAVE_STRING_H 00030 #include <string.h> 00031 #else 00032 #include <strings.h> 00033 #endif 00034 00035 #if HAVE_DMALLOC_H 00036 #include <dmalloc.h> 00037 #endif 00038 00039 #include <net-snmp/types.h> 00040 #include <net-snmp/output_api.h> 00041 #include <net-snmp/utilities.h> 00042 00043 #include <net-snmp/library/snmp_api.h> 00044 #ifdef NETSNMP_USE_OPENSSL 00045 # include <openssl/hmac.h> 00046 #else 00047 #ifdef NETSNMP_USE_INTERNAL_MD5 00048 #include <net-snmp/library/md5.h> 00049 #endif 00050 #endif 00051 00052 #ifdef NETSNMP_USE_PKCS11 00053 #include <security/cryptoki.h> 00054 #endif 00055 00056 #include <net-snmp/library/scapi.h> 00057 #include <net-snmp/library/keytools.h> 00058 00059 #include <net-snmp/library/transform_oids.h> 00060 00061 /*******************************************************************-o-****** 00062 * generate_Ku 00063 * 00064 * Parameters: 00065 * *hashtype MIB OID for the transform type for hashing. 00066 * hashtype_len Length of OID value. 00067 * *P Pre-allocated bytes of passpharase. 00068 * pplen Length of passphrase. 00069 * *Ku Buffer to contain Ku. 00070 * *kulen Length of Ku buffer. 00071 * 00072 * Returns: 00073 * SNMPERR_SUCCESS Success. 00074 * SNMPERR_GENERR All errors. 00075 * 00076 * 00077 * Convert a passphrase into a master user key, Ku, according to the 00078 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM) 00079 * as follows: 00080 * 00081 * Expand the passphrase to fill the passphrase buffer space, if necessary, 00082 * concatenation as many duplicates as possible of P to itself. If P is 00083 * larger than the buffer space, truncate it to fit. 00084 * 00085 * Then hash the result with the given hashtype transform. Return 00086 * the result as Ku. 00087 * 00088 * If successful, kulen contains the size of the hash written to Ku. 00089 * 00090 * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length 00091 * cause an error to be returned. 00092 * (Punt this check to the cmdline apps? XXX) 00093 */ 00094 int 00095 generate_Ku(const oid * hashtype, u_int hashtype_len, 00096 u_char * P, size_t pplen, u_char * Ku, size_t * kulen) 00097 #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) 00098 { 00099 int rval = SNMPERR_SUCCESS, 00100 nbytes = USM_LENGTH_EXPANDED_PASSPHRASE; 00101 00102 u_int i, pindex = 0; 00103 00104 u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp; 00105 00106 #ifdef NETSNMP_USE_OPENSSL 00107 EVP_MD_CTX *ctx = (EVP_MD_CTX *)malloc(sizeof(EVP_MD_CTX)); 00108 unsigned int tmp_len; 00109 #else 00110 MDstruct MD; 00111 #endif 00112 /* 00113 * Sanity check. 00114 */ 00115 if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0) 00116 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00117 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00118 } 00119 00120 if (pplen < USM_LENGTH_P_MIN) { 00121 snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " 00122 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); 00123 snmp_set_detail("The supplied password length is too short."); 00124 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00125 } 00126 00127 00128 /* 00129 * Setup for the transform type. 00130 */ 00131 #ifdef NETSNMP_USE_OPENSSL 00132 00133 #ifndef NETSNMP_DISABLE_MD5 00134 if (ISTRANSFORM(hashtype, HMACMD5Auth)) 00135 EVP_DigestInit(ctx, EVP_md5()); 00136 else 00137 #endif 00138 if (ISTRANSFORM(hashtype, HMACSHA1Auth)) 00139 EVP_DigestInit(ctx, EVP_sha1()); 00140 else { 00141 free(ctx); 00142 return (SNMPERR_GENERR); 00143 } 00144 #else 00145 MDbegin(&MD); 00146 #endif /* NETSNMP_USE_OPENSSL */ 00147 00148 while (nbytes > 0) { 00149 bufp = buf; 00150 for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) { 00151 *bufp++ = P[pindex++ % pplen]; 00152 } 00153 #ifdef NETSNMP_USE_OPENSSL 00154 EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK); 00155 #elif NETSNMP_USE_INTERNAL_MD5 00156 if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) { 00157 rval = SNMPERR_USM_ENCRYPTIONERROR; 00158 goto md5_fin; 00159 } 00160 #endif /* NETSNMP_USE_OPENSSL */ 00161 00162 nbytes -= USM_LENGTH_KU_HASHBLOCK; 00163 } 00164 00165 #ifdef NETSNMP_USE_OPENSSL 00166 tmp_len = *kulen; 00167 EVP_DigestFinal(ctx, (unsigned char *) Ku, &tmp_len); 00168 *kulen = tmp_len; 00169 /* 00170 * what about free() 00171 */ 00172 #elif NETSNMP_USE_INTERNAL_MD5 00173 if (MDupdate(&MD, buf, 0)) { 00174 rval = SNMPERR_USM_ENCRYPTIONERROR; 00175 goto md5_fin; 00176 } 00177 *kulen = sc_get_properlength(hashtype, hashtype_len); 00178 MDget(&MD, Ku, *kulen); 00179 md5_fin: 00180 memset(&MD, 0, sizeof(MD)); 00181 #endif /* NETSNMP_USE_INTERNAL_MD5 */ 00182 00183 00184 #ifdef NETSNMP_ENABLE_TESTING_CODE 00185 DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P)); 00186 for (i = 0; i < *kulen; i++) 00187 DEBUGMSG(("generate_Ku", "%02x", Ku[i])); 00188 DEBUGMSG(("generate_Ku", "\n")); 00189 #endif /* NETSNMP_ENABLE_TESTING_CODE */ 00190 00191 00192 generate_Ku_quit: 00193 memset(buf, 0, sizeof(buf)); 00194 #ifdef NETSNMP_USE_OPENSSL 00195 free(ctx); 00196 #endif 00197 return rval; 00198 00199 } /* end generate_Ku() */ 00200 #elif NETSNMP_USE_PKCS11 00201 { 00202 int rval = SNMPERR_SUCCESS; 00203 00204 /* 00205 * Sanity check. 00206 */ 00207 if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0) 00208 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00209 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00210 } 00211 00212 if (pplen < USM_LENGTH_P_MIN) { 00213 snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " 00214 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); 00215 snmp_set_detail("The supplied password length is too short."); 00216 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00217 } 00218 00219 /* 00220 * Setup for the transform type. 00221 */ 00222 00223 #ifndef NETSNMP_DISABLE_MD5 00224 if (ISTRANSFORM(hashtype, HMACMD5Auth)) 00225 return pkcs_generate_Ku(CKM_MD5, P, pplen, Ku, kulen); 00226 else 00227 #endif 00228 if (ISTRANSFORM(hashtype, HMACSHA1Auth)) 00229 return pkcs_generate_Ku(CKM_SHA_1, P, pplen, Ku, kulen); 00230 else { 00231 return (SNMPERR_GENERR); 00232 } 00233 00234 generate_Ku_quit: 00235 00236 return rval; 00237 } /* end generate_Ku() */ 00238 #else 00239 _KEYTOOLS_NOT_AVAILABLE 00240 #endif /* internal or openssl */ 00241 /*******************************************************************-o-****** 00242 * generate_kul 00243 * 00244 * Parameters: 00245 * *hashtype 00246 * hashtype_len 00247 * *engineID 00248 * engineID_len 00249 * *Ku Master key for a given user. 00250 * ku_len Length of Ku in bytes. 00251 * *Kul Localized key for a given user at engineID. 00252 * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT). 00253 * 00254 * Returns: 00255 * SNMPERR_SUCCESS Success. 00256 * SNMPERR_GENERR All errors. 00257 * 00258 * 00259 * Ku MUST be the proper length (currently fixed) for the given hashtype. 00260 * 00261 * Upon successful return, Kul contains the localized form of Ku at 00262 * engineID, and the length of the key is stored in kul_len. 00263 * 00264 * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and 00265 * originally documented in: 00266 * U. Blumenthal, N. C. Hien, B. Wijnen, 00267 * "Key Derivation for Network Management Applications", 00268 * IEEE Network Magazine, April/May issue, 1997. 00269 * 00270 * 00271 * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku). 00272 * 00273 * NOTE Localized keys for privacy transforms are generated via 00274 * the authentication transform held by the same usmUser. 00275 * 00276 * XXX An engineID of any length is accepted, even if larger than 00277 * what is spec'ed for the textual convention. 00278 */ 00279 int 00280 generate_kul(const oid * hashtype, u_int hashtype_len, 00281 u_char * engineID, size_t engineID_len, 00282 u_char * Ku, size_t ku_len, 00283 u_char * Kul, size_t * kul_len) 00284 #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) 00285 { 00286 int rval = SNMPERR_SUCCESS; 00287 u_int nbytes = 0; 00288 size_t properlength; 00289 int iproperlength; 00290 00291 u_char buf[SNMP_MAXBUF]; 00292 #ifdef NETSNMP_ENABLE_TESTING_CODE 00293 int i; 00294 #endif 00295 00296 00297 /* 00298 * Sanity check. 00299 */ 00300 if (!hashtype || !engineID || !Ku || !Kul || !kul_len 00301 || (engineID_len <= 0) || (ku_len <= 0) || (*kul_len <= 0) 00302 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00303 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00304 } 00305 00306 00307 iproperlength = sc_get_properlength(hashtype, hashtype_len); 00308 if (iproperlength == SNMPERR_GENERR) 00309 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00310 00311 properlength = (size_t) iproperlength; 00312 00313 if (((int) *kul_len < properlength) || ((int) ku_len < properlength)) { 00314 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00315 } 00316 00317 /* 00318 * Concatenate Ku and engineID properly, then hash the result. 00319 * Store it in Kul. 00320 */ 00321 nbytes = 0; 00322 memcpy(buf, Ku, properlength); 00323 nbytes += properlength; 00324 memcpy(buf + nbytes, engineID, engineID_len); 00325 nbytes += engineID_len; 00326 memcpy(buf + nbytes, Ku, properlength); 00327 nbytes += properlength; 00328 00329 rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len); 00330 00331 #ifdef NETSNMP_ENABLE_TESTING_CODE 00332 DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): ")); 00333 for (i = 0; i < *kul_len; i++) 00334 DEBUGMSG(("generate_kul", "%02x", Kul[i])); 00335 DEBUGMSG(("generate_kul", "keytools\n")); 00336 #endif /* NETSNMP_ENABLE_TESTING_CODE */ 00337 00338 QUITFUN(rval, generate_kul_quit); 00339 00340 00341 generate_kul_quit: 00342 return rval; 00343 00344 } /* end generate_kul() */ 00345 00346 #else 00347 _KEYTOOLS_NOT_AVAILABLE 00348 #endif /* internal or openssl */ 00349 /*******************************************************************-o-****** 00350 * encode_keychange 00351 * 00352 * Parameters: 00353 * *hashtype MIB OID for the hash transform type. 00354 * hashtype_len Length of the MIB OID hash transform type. 00355 * *oldkey Old key that is used to encodes the new key. 00356 * oldkey_len Length of oldkey in bytes. 00357 * *newkey New key that is encoded using the old key. 00358 * newkey_len Length of new key in bytes. 00359 * *kcstring Buffer to contain the KeyChange TC string. 00360 * *kcstring_len Length of kcstring buffer. 00361 * 00362 * Returns: 00363 * SNMPERR_SUCCESS Success. 00364 * SNMPERR_GENERR All errors. 00365 * 00366 * 00367 * Uses oldkey and acquired random bytes to encode newkey into kcstring 00368 * according to the rules of the KeyChange TC described in RFC 2274, Section 5. 00369 * 00370 * Upon successful return, *kcstring_len contains the length of the 00371 * encoded string. 00372 * 00373 * ASSUMES Old and new key are always equal to each other, although 00374 * this may be less than the transform type hash output 00375 * output length (eg, using KeyChange for a DESPriv key when 00376 * the user also uses SHA1Auth). This also implies that the 00377 * hash placed in the second 1/2 of the key change string 00378 * will be truncated before the XOR'ing when the hash output is 00379 * larger than that 1/2 of the key change string. 00380 * 00381 * *kcstring_len will be returned as exactly twice that same 00382 * length though the input buffer may be larger. 00383 * 00384 * XXX FIX: Does not handle varibable length keys. 00385 * XXX FIX: Does not handle keys larger than the hash algorithm used. 00386 */ 00387 int 00388 encode_keychange(const oid * hashtype, u_int hashtype_len, 00389 u_char * oldkey, size_t oldkey_len, 00390 u_char * newkey, size_t newkey_len, 00391 u_char * kcstring, size_t * kcstring_len) 00392 #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) 00393 { 00394 int rval = SNMPERR_SUCCESS; 00395 size_t properlength; 00396 size_t nbytes = 0; 00397 00398 u_char *tmpbuf = NULL; 00399 00400 00401 /* 00402 * Sanity check. 00403 */ 00404 if (!hashtype || !oldkey || !newkey || !kcstring || !kcstring_len 00405 || (oldkey_len <= 0) || (newkey_len <= 0) || (*kcstring_len <= 0) 00406 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00407 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00408 } 00409 00410 /* 00411 * Setup for the transform type. 00412 */ 00413 properlength = sc_get_properlength(hashtype, hashtype_len); 00414 if (properlength == SNMPERR_GENERR) 00415 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00416 00417 if ((oldkey_len != newkey_len) || (*kcstring_len < (2 * oldkey_len))) { 00418 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00419 } 00420 00421 properlength = SNMP_MIN((int) oldkey_len, properlength); 00422 00423 /* 00424 * Use the old key and some random bytes to encode the new key 00425 * in the KeyChange TC format: 00426 * . Get random bytes (store in first half of kcstring), 00427 * . Hash (oldkey | random_bytes) (into second half of kcstring), 00428 * . XOR hash and newkey (into second half of kcstring). 00429 * 00430 * Getting the wrong number of random bytes is considered an error. 00431 */ 00432 nbytes = properlength; 00433 00434 #if defined(NETSNMP_ENABLE_TESTING_CODE) && defined(RANDOMZEROS) 00435 memset(kcstring, 0, nbytes); 00436 DEBUGMSG(("encode_keychange", 00437 "** Using all zero bits for \"random\" delta of )" 00438 "the keychange string! **\n")); 00439 #else /* !NETSNMP_ENABLE_TESTING_CODE */ 00440 rval = sc_random(kcstring, &nbytes); 00441 QUITFUN(rval, encode_keychange_quit); 00442 if ((int) nbytes != properlength) { 00443 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00444 } 00445 #endif /* !NETSNMP_ENABLE_TESTING_CODE */ 00446 00447 tmpbuf = (u_char *) malloc(properlength * 2); 00448 if (tmpbuf) { 00449 memcpy(tmpbuf, oldkey, properlength); 00450 memcpy(tmpbuf + properlength, kcstring, properlength); 00451 00452 *kcstring_len -= properlength; 00453 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2, 00454 kcstring + properlength, kcstring_len); 00455 00456 QUITFUN(rval, encode_keychange_quit); 00457 00458 *kcstring_len = (properlength * 2); 00459 00460 kcstring += properlength; 00461 nbytes = 0; 00462 while ((int) (nbytes++) < properlength) { 00463 *kcstring++ ^= *newkey++; 00464 } 00465 } 00466 00467 encode_keychange_quit: 00468 if (rval != SNMPERR_SUCCESS) 00469 memset(kcstring, 0, *kcstring_len); 00470 SNMP_FREE(tmpbuf); 00471 00472 return rval; 00473 00474 } /* end encode_keychange() */ 00475 00476 #else 00477 _KEYTOOLS_NOT_AVAILABLE 00478 #endif /* internal or openssl */ 00479 /*******************************************************************-o-****** 00480 * decode_keychange 00481 * 00482 * Parameters: 00483 * *hashtype MIB OID of the hash transform to use. 00484 * hashtype_len Length of the hash transform MIB OID. 00485 * *oldkey Old key that is used to encode the new key. 00486 * oldkey_len Length of oldkey in bytes. 00487 * *kcstring Encoded KeyString buffer containing the new key. 00488 * kcstring_len Length of kcstring in bytes. 00489 * *newkey Buffer to hold the extracted new key. 00490 * *newkey_len Length of newkey in bytes. 00491 * 00492 * Returns: 00493 * SNMPERR_SUCCESS Success. 00494 * SNMPERR_GENERR All errors. 00495 * 00496 * 00497 * Decodes a string of bits encoded according to the KeyChange TC described 00498 * in RFC 2274, Section 5. The new key is extracted from *kcstring with 00499 * the aid of the old key. 00500 * 00501 * Upon successful return, *newkey_len contains the length of the new key. 00502 * 00503 * 00504 * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer, 00505 * although this length may be less than the hash transform 00506 * output. Thus the new key length will be equal to the old 00507 * key length. 00508 */ 00509 /* 00510 * XXX: if the newkey is not long enough, it should be freed and remalloced 00511 */ 00512 int 00513 decode_keychange(const oid * hashtype, u_int hashtype_len, 00514 u_char * oldkey, size_t oldkey_len, 00515 u_char * kcstring, size_t kcstring_len, 00516 u_char * newkey, size_t * newkey_len) 00517 #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) 00518 { 00519 int rval = SNMPERR_SUCCESS; 00520 size_t properlength = 0; 00521 int iproperlength = 0; 00522 u_int nbytes = 0; 00523 00524 u_char *bufp, tmp_buf[SNMP_MAXBUF]; 00525 size_t tmp_buf_len = SNMP_MAXBUF; 00526 u_char *tmpbuf = NULL; 00527 00528 00529 00530 /* 00531 * Sanity check. 00532 */ 00533 if (!hashtype || !oldkey || !kcstring || !newkey || !newkey_len 00534 || (oldkey_len <= 0) || (kcstring_len <= 0) || (*newkey_len <= 0) 00535 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00536 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00537 } 00538 00539 00540 /* 00541 * Setup for the transform type. 00542 */ 00543 iproperlength = sc_get_properlength(hashtype, hashtype_len); 00544 if (iproperlength == SNMPERR_GENERR) 00545 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00546 00547 properlength = (size_t) iproperlength; 00548 00549 if (((oldkey_len * 2) != kcstring_len) || (*newkey_len < oldkey_len)) { 00550 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00551 } 00552 00553 properlength = oldkey_len; 00554 *newkey_len = properlength; 00555 00556 /* 00557 * Use the old key and the given KeyChange TC string to recover 00558 * the new key: 00559 * . Hash (oldkey | random_bytes) (into newkey), 00560 * . XOR hash and encoded (second) half of kcstring (into newkey). 00561 */ 00562 tmpbuf = (u_char *) malloc(properlength * 2); 00563 if (tmpbuf) { 00564 memcpy(tmpbuf, oldkey, properlength); 00565 memcpy(tmpbuf + properlength, kcstring, properlength); 00566 00567 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2, 00568 tmp_buf, &tmp_buf_len); 00569 QUITFUN(rval, decode_keychange_quit); 00570 00571 memcpy(newkey, tmp_buf, properlength); 00572 bufp = kcstring + properlength; 00573 nbytes = 0; 00574 while ((int) (nbytes++) < properlength) { 00575 *newkey++ ^= *bufp++; 00576 } 00577 } 00578 00579 decode_keychange_quit: 00580 if (rval != SNMPERR_SUCCESS) { 00581 memset(newkey, 0, properlength); 00582 } 00583 memset(tmp_buf, 0, SNMP_MAXBUF); 00584 if (tmpbuf != NULL) 00585 SNMP_FREE(tmpbuf); 00586 00587 return rval; 00588 00589 } /* end decode_keychange() */ 00590 00591 #else 00592 _KEYTOOLS_NOT_AVAILABLE 00593 #endif /* internal or openssl */