net-snmp  5.4.1
keytools.c
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 */