| /* Portions of this file are subject to the following copyright(s). See |
| * the Net-SNMP's COPYING file for more details and other copyrights |
| * that may apply: |
| */ |
| /* |
| * Portions of this file are copyrighted by: |
| * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms specified in the COPYING file |
| * distributed with the Net-SNMP package. |
| */ |
| |
| /* |
| * scapi.c |
| * |
| */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| |
| #include <sys/types.h> |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #ifdef HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_DMALLOC_H |
| #include <dmalloc.h> |
| #endif |
| |
| #include <net-snmp/types.h> |
| #include <net-snmp/output_api.h> |
| #include <net-snmp/utilities.h> |
| |
| #ifdef NETSNMP_USE_INTERNAL_MD5 |
| #include <net-snmp/library/md5.h> |
| #endif |
| #include <net-snmp/library/snmp_api.h> |
| #include <net-snmp/library/callback.h> |
| #include <net-snmp/library/snmp_secmod.h> |
| #include <net-snmp/library/snmpusm.h> |
| #include <net-snmp/library/keytools.h> |
| #include <net-snmp/library/scapi.h> |
| #include <net-snmp/library/mib.h> |
| #include <net-snmp/library/transform_oids.h> |
| |
| #ifdef NETSNMP_USE_OPENSSL |
| #include <openssl/hmac.h> |
| #include <openssl/evp.h> |
| #include <openssl/rand.h> |
| #include <openssl/des.h> |
| #ifdef HAVE_AES |
| #include <openssl/aes.h> |
| #endif |
| |
| #ifndef NETSNMP_DISABLE_DES |
| #ifdef STRUCT_DES_KS_STRUCT_HAS_WEAK_KEY |
| /* these are older names for newer structures that exist in openssl .9.7 */ |
| #define DES_key_schedule des_key_schedule |
| #define DES_cblock des_cblock |
| #define DES_key_sched des_key_sched |
| #define DES_ncbc_encrypt des_ncbc_encrypt |
| #define DES_cbc_encrypt des_cbc_encrypt |
| #define OLD_DES |
| #endif |
| #endif |
| |
| #endif /* HAVE_OPENSSL */ |
| |
| #ifdef NETSNMP_USE_PKCS11 |
| #include <security/cryptoki.h> |
| #endif |
| |
| #ifdef QUITFUN |
| #undef QUITFUN |
| #define QUITFUN(e, l) \ |
| if (e != SNMPERR_SUCCESS) { \ |
| rval = SNMPERR_SC_GENERAL_FAILURE; \ |
| goto l ; \ |
| } |
| #endif |
| |
| |
| /* |
| * sc_get_properlength(oid *hashtype, u_int hashtype_len): |
| * |
| * Given a hashing type ("hashtype" and its length hashtype_len), return |
| * the length of the hash result. |
| * |
| * Returns either the length or SNMPERR_GENERR for an unknown hashing type. |
| */ |
| int |
| sc_get_properlength(const oid * hashtype, u_int hashtype_len) |
| { |
| DEBUGTRACE; |
| /* |
| * Determine transform type hash length. |
| */ |
| #ifndef NETSNMP_DISABLE_MD5 |
| if (ISTRANSFORM(hashtype, HMACMD5Auth)) { |
| return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACMD5); |
| } else |
| #endif |
| if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { |
| return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACSHA1); |
| } |
| return SNMPERR_GENERR; |
| } |
| |
| int |
| sc_get_proper_priv_length(const oid * privtype, u_int privtype_len) |
| { |
| int properlength = 0; |
| #ifndef NETSNMP_DISABLE_DES |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); |
| } |
| #endif |
| #ifdef HAVE_AES |
| if (ISTRANSFORM(privtype, AESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); |
| } |
| #endif |
| return properlength; |
| } |
| |
| |
| /*******************************************************************-o-****** |
| * sc_init |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| */ |
| int |
| sc_init(void) |
| { |
| int rval = SNMPERR_SUCCESS; |
| |
| #ifndef NETSNMP_USE_OPENSSL |
| #ifdef NETSNMP_USE_INTERNAL_MD5 |
| struct timeval tv; |
| |
| DEBUGTRACE; |
| |
| gettimeofday(&tv, (struct timezone *) 0); |
| |
| srandom(tv.tv_sec ^ tv.tv_usec); |
| #elif NETSNMP_USE_PKCS11 |
| DEBUGTRACE; |
| rval = pkcs_init(); |
| #else |
| rval = SNMPERR_SC_NOT_CONFIGURED; |
| #endif /* NETSNMP_USE_INTERNAL_MD5 */ |
| /* |
| * XXX ogud: The only reason to do anything here with openssl is to |
| * * XXX ogud: seed random number generator |
| */ |
| #endif /* ifndef NETSNMP_USE_OPENSSL */ |
| return rval; |
| } /* end sc_init() */ |
| |
| /*******************************************************************-o-****** |
| * sc_random |
| * |
| * Parameters: |
| * *buf Pre-allocated buffer. |
| * *buflen Size of buffer. |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| */ |
| int |
| sc_random(u_char * buf, size_t * buflen) |
| #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| { |
| int rval = SNMPERR_SUCCESS; |
| #ifdef NETSNMP_USE_INTERNAL_MD5 |
| int i; |
| int rndval; |
| u_char *ucp = buf; |
| #endif |
| |
| DEBUGTRACE; |
| |
| #ifdef NETSNMP_USE_OPENSSL |
| RAND_bytes(buf, *buflen); /* will never fail */ |
| #elif NETSNMP_USE_PKCS11 /* NETSNMP_USE_PKCS11 */ |
| pkcs_random(buf, *buflen); |
| #else /* NETSNMP_USE_INTERNAL_MD5 */ |
| /* |
| * fill the buffer with random integers. Note that random() |
| * is defined in config.h and may not be truly the random() |
| * system call if something better existed |
| */ |
| rval = *buflen - *buflen % sizeof(rndval); |
| for (i = 0; i < rval; i += sizeof(rndval)) { |
| rndval = random(); |
| memcpy(ucp, &rndval, sizeof(rndval)); |
| ucp += sizeof(rndval); |
| } |
| |
| rndval = random(); |
| memcpy(ucp, &rndval, *buflen % sizeof(rndval)); |
| |
| rval = SNMPERR_SUCCESS; |
| #endif /* NETSNMP_USE_OPENSSL */ |
| return rval; |
| |
| } /* end sc_random() */ |
| |
| #else |
| _SCAPI_NOT_CONFIGURED |
| #endif /* */ |
| /*******************************************************************-o-****** |
| * sc_generate_keyed_hash |
| * |
| * Parameters: |
| * authtype Type of authentication transform. |
| * authtypelen |
| * *key Pointer to key (Kul) to use in keyed hash. |
| * keylen Length of key in bytes. |
| * *message Pointer to the message to hash. |
| * msglen Length of the message. |
| * *MAC Will be returned with allocated bytes containg hash. |
| * *maclen Length of the hash buffer in bytes; also indicates |
| * whether the MAC should be truncated. |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMPERR_GENERR All errs |
| * |
| * |
| * A hash of the first msglen bytes of message using a keyed hash defined |
| * by authtype is created and stored in MAC. MAC is ASSUMED to be a buffer |
| * of at least maclen bytes. If the length of the hash is greater than |
| * maclen, it is truncated to fit the buffer. If the length of the hash is |
| * less than maclen, maclen set to the number of hash bytes generated. |
| * |
| * ASSUMED that the number of hash bits is a multiple of 8. |
| */ |
| int |
| sc_generate_keyed_hash(const oid * authtype, size_t authtypelen, |
| u_char * key, u_int keylen, |
| u_char * message, u_int msglen, |
| u_char * MAC, size_t * maclen) |
| #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| { |
| int rval = SNMPERR_SUCCESS; |
| int properlength; |
| |
| u_char buf[SNMP_MAXBUF_SMALL]; |
| #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| size_t buf_len = sizeof(buf); |
| #endif |
| |
| DEBUGTRACE; |
| |
| #ifdef NETSNMP_ENABLE_TESTING_CODE |
| { |
| int i; |
| DEBUGMSG(("sc_generate_keyed_hash", |
| "sc_generate_keyed_hash(): key=0x")); |
| for (i = 0; i < keylen; i++) |
| DEBUGMSG(("sc_generate_keyed_hash", "%02x", key[i] & 0xff)); |
| DEBUGMSG(("sc_generate_keyed_hash", " (%d)\n", keylen)); |
| } |
| #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
| |
| /* |
| * Sanity check. |
| */ |
| if (!authtype || !key || !message || !MAC || !maclen |
| || (keylen <= 0) || (msglen <= 0) || (*maclen <= 0) |
| || (authtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| |
| properlength = sc_get_properlength(authtype, authtypelen); |
| if (properlength == SNMPERR_GENERR) |
| return properlength; |
| |
| if (((int) keylen < properlength)) { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| #ifdef NETSNMP_USE_OPENSSL |
| /* |
| * Determine transform type. |
| */ |
| #ifndef NETSNMP_DISABLE_MD5 |
| if (ISTRANSFORM(authtype, HMACMD5Auth)) |
| HMAC(EVP_md5(), key, keylen, message, msglen, buf, &buf_len); |
| else |
| #endif |
| if (ISTRANSFORM(authtype, HMACSHA1Auth)) |
| HMAC(EVP_sha1(), key, keylen, message, msglen, buf, &buf_len); |
| else { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| if ((int)buf_len != properlength) { |
| QUITFUN(rval, sc_generate_keyed_hash_quit); |
| } |
| if ((int)*maclen > buf_len) |
| *maclen = buf_len; |
| memcpy(MAC, buf, *maclen); |
| |
| #elif NETSNMP_USE_PKCS11 /* NETSNMP_USE_PKCS11 */ |
| |
| #ifndef NETSNMP_DISABLE_MD5 |
| if (ISTRANSFORM(authtype, HMACMD5Auth)) { |
| if (pkcs_sign(CKM_MD5_HMAC,key, keylen, message, |
| msglen, buf, &buf_len) != SNMPERR_SUCCESS) { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| } else |
| #endif |
| if (ISTRANSFORM(authtype, HMACSHA1Auth)) { |
| if (pkcs_sign(CKM_SHA_1_HMAC,key, keylen, message, |
| msglen, buf, &buf_len) != SNMPERR_SUCCESS) { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| } else { |
| QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); |
| } |
| |
| if (buf_len != properlength) { |
| QUITFUN(rval, sc_generate_keyed_hash_quit); |
| } |
| if (*maclen > buf_len) |
| *maclen = buf_len; |
| memcpy(MAC, buf, *maclen); |
| |
| #else /* NETSNMP_USE_INTERNAL_MD5 */ |
| if ((int) *maclen > properlength) |
| *maclen = properlength; |
| if (MDsign(message, msglen, MAC, *maclen, key, keylen)) { |
| rval = SNMPERR_GENERR; |
| goto sc_generate_keyed_hash_quit; |
| } |
| #endif /* NETSNMP_USE_OPENSSL */ |
| |
| #ifdef NETSNMP_ENABLE_TESTING_CODE |
| { |
| char *s; |
| int len = binary_to_hex(MAC, *maclen, &s); |
| |
| DEBUGMSGTL(("scapi", "Full v3 message hash: %s\n", s)); |
| SNMP_ZERO(s, len); |
| SNMP_FREE(s); |
| } |
| #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
| |
| sc_generate_keyed_hash_quit: |
| memset(buf, 0, SNMP_MAXBUF_SMALL); |
| return rval; |
| } /* end sc_generate_keyed_hash() */ |
| |
| #else |
| _SCAPI_NOT_CONFIGURED |
| #endif /* */ |
| /* |
| * sc_hash(): a generic wrapper around whatever hashing package we are using. |
| * |
| * IN: |
| * hashtype - oid pointer to a hash type |
| * hashtypelen - length of oid pointer |
| * buf - u_char buffer to be hashed |
| * buf_len - integer length of buf data |
| * MAC_len - length of the passed MAC buffer size. |
| * |
| * OUT: |
| * MAC - pre-malloced space to store hash output. |
| * MAC_len - length of MAC output to the MAC buffer. |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMP_SC_GENERAL_FAILURE Any error. |
| */ |
| int |
| sc_hash(const oid * hashtype, size_t hashtypelen, u_char * buf, |
| size_t buf_len, u_char * MAC, size_t * MAC_len) |
| #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| { |
| #if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| int rval = SNMPERR_SUCCESS; |
| unsigned int tmp_len; |
| #endif |
| int ret; |
| |
| #ifdef NETSNMP_USE_OPENSSL |
| const EVP_MD *hashfn; |
| EVP_MD_CTX *cptr; |
| #endif |
| |
| DEBUGTRACE; |
| |
| if (hashtype == NULL || hashtypelen < 0 || buf == NULL || |
| buf_len <= 0 || MAC == NULL || MAC_len == NULL ) |
| return (SNMPERR_GENERR); |
| ret = sc_get_properlength(hashtype, hashtypelen); |
| if (( ret < 0 ) || (*MAC_len < ret )) |
| return (SNMPERR_GENERR); |
| |
| #ifdef NETSNMP_USE_OPENSSL |
| /* |
| * Determine transform type. |
| */ |
| #ifndef NETSNMP_DISABLE_MD5 |
| if (ISTRANSFORM(hashtype, HMACMD5Auth)) { |
| hashfn = (const EVP_MD *) EVP_md5(); |
| } else |
| #endif |
| if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { |
| hashfn = (const EVP_MD *) EVP_sha1(); |
| } else { |
| return (SNMPERR_GENERR); |
| } |
| |
| /** initialize the pointer */ |
| #ifdef HAVE_EVP_MD_CTX_CREATE |
| cptr = EVP_MD_CTX_create(); |
| #else |
| cptr = malloc(sizeof(*cptr)); |
| #if defined(OLD_DES) |
| memset(cptr, 0, sizeof(*cptr)); |
| #else |
| EVP_MD_CTX_init(cptr); |
| #endif |
| #endif |
| EVP_DigestInit(cptr, hashfn); |
| |
| /** pass the data */ |
| EVP_DigestUpdate(cptr, buf, buf_len); |
| |
| /** do the final pass */ |
| EVP_DigestFinal(cptr, MAC, &tmp_len); |
| *MAC_len = tmp_len; |
| #ifdef HAVE_EVP_MD_CTX_DESTROY |
| EVP_MD_CTX_destroy(cptr); |
| #else |
| #if !defined(OLD_DES) |
| EVP_MD_CTX_cleanup(cptr); |
| #endif |
| free(cptr); |
| #endif |
| return (rval); |
| #elif NETSNMP_USE_PKCS11 /* NETSNMP_USE_PKCS11 */ |
| |
| #ifndef NETSNMP_DISABLE_MD5 |
| if (ISTRANSFORM(hashtype, HMACMD5Auth)) { |
| rval = pkcs_digest(CKM_MD5, buf, buf_len, MAC, &tmp_len); |
| *MAC_len = tmp_len; |
| } else |
| #endif |
| if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { |
| rval = pkcs_digest(CKM_SHA_1, buf, buf_len, MAC, &tmp_len); |
| *MAC_len = tmp_len; |
| } else { |
| return (SNMPERR_GENERR); |
| } |
| |
| return (rval); |
| |
| #else /* NETSNMP_USE_INTERNAL_MD5 */ |
| |
| if (MDchecksum(buf, buf_len, MAC, *MAC_len)) { |
| return SNMPERR_GENERR; |
| } |
| if (*MAC_len > 16) |
| *MAC_len = 16; |
| return SNMPERR_SUCCESS; |
| |
| #endif /* NETSNMP_USE_OPENSSL */ |
| } |
| #else /* !defined(NETSNMP_USE_OPENSSL) && !defined(NETSNMP_USE_INTERNAL_MD5) */ |
| _SCAPI_NOT_CONFIGURED |
| #endif /* !defined(NETSNMP_USE_OPENSSL) && !defined(NETSNMP_USE_INTERNAL_MD5) */ |
| /*******************************************************************-o-****** |
| * sc_check_keyed_hash |
| * |
| * Parameters: |
| * authtype Transform type of authentication hash. |
| * *key Key bits in a string of bytes. |
| * keylen Length of key in bytes. |
| * *message Message for which to check the hash. |
| * msglen Length of message. |
| * *MAC Given hash. |
| * maclen Length of given hash; indicates truncation if it is |
| * shorter than the normal size of output for |
| * given hash transform. |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMP_SC_GENERAL_FAILURE Any error |
| * |
| * |
| * Check the hash given in MAC against the hash of message. If the length |
| * of MAC is less than the length of the transform hash output, only maclen |
| * bytes are compared. The length of MAC cannot be greater than the |
| * length of the hash transform output. |
| */ |
| int |
| sc_check_keyed_hash(const oid * authtype, size_t authtypelen, |
| u_char * key, u_int keylen, |
| u_char * message, u_int msglen, |
| u_char * MAC, u_int maclen) |
| #if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11) |
| { |
| int rval = SNMPERR_SUCCESS; |
| size_t buf_len = SNMP_MAXBUF_SMALL; |
| |
| u_char buf[SNMP_MAXBUF_SMALL]; |
| |
| DEBUGTRACE; |
| |
| #ifdef NETSNMP_ENABLE_TESTING_CODE |
| { |
| int i; |
| DEBUGMSG(("scapi", "sc_check_keyed_hash(): key=0x")); |
| for (i = 0; i < keylen; i++) |
| DEBUGMSG(("scapi", "%02x", key[i] & 0xff)); |
| DEBUGMSG(("scapi", " (%d)\n", keylen)); |
| } |
| #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
| |
| /* |
| * Sanity check. |
| */ |
| if (!authtype || !key || !message || !MAC |
| || (keylen <= 0) || (msglen <= 0) || (maclen <= 0) |
| || (authtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); |
| } |
| |
| if (maclen != USM_MD5_AND_SHA_AUTH_LEN) { |
| QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); |
| } |
| |
| /* |
| * Generate a full hash of the message, then compare |
| * the result with the given MAC which may shorter than |
| * the full hash length. |
| */ |
| rval = sc_generate_keyed_hash(authtype, authtypelen, |
| key, keylen, |
| message, msglen, buf, &buf_len); |
| QUITFUN(rval, sc_check_keyed_hash_quit); |
| |
| if (maclen > msglen) { |
| QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); |
| |
| } else if (memcmp(buf, MAC, maclen) != 0) { |
| QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); |
| } |
| |
| |
| sc_check_keyed_hash_quit: |
| memset(buf, 0, SNMP_MAXBUF_SMALL); |
| |
| return rval; |
| |
| } /* end sc_check_keyed_hash() */ |
| |
| #else |
| _SCAPI_NOT_CONFIGURED |
| #endif /* NETSNMP_USE_INTERNAL_MD5 */ |
| /*******************************************************************-o-****** |
| * sc_encrypt |
| * |
| * Parameters: |
| * privtype Type of privacy cryptographic transform. |
| * *key Key bits for crypting. |
| * keylen Length of key (buffer) in bytes. |
| * *iv IV bits for crypting. |
| * ivlen Length of iv (buffer) in bytes. |
| * *plaintext Plaintext to crypt. |
| * ptlen Length of plaintext. |
| * *ciphertext Ciphertext to crypt. |
| * *ctlen Length of ciphertext. |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported. |
| * SNMPERR_SC_GENERAL_FAILURE Any other error |
| * |
| * |
| * Encrypt plaintext into ciphertext using key and iv. |
| * |
| * ctlen contains actual number of crypted bytes in ciphertext upon |
| * successful return. |
| */ |
| int |
| sc_encrypt(const oid * privtype, size_t privtypelen, |
| u_char * key, u_int keylen, |
| u_char * iv, u_int ivlen, |
| u_char * plaintext, u_int ptlen, |
| u_char * ciphertext, size_t * ctlen) |
| #if defined(NETSNMP_USE_OPENSSL) |
| { |
| int rval = SNMPERR_SUCCESS; |
| u_int properlength = 0, properlength_iv = 0; |
| u_char pad_block[128]; /* bigger than anything I need */ |
| u_char my_iv[128]; /* ditto */ |
| int pad, plast, pad_size = 0; |
| int have_trans; |
| #ifndef NETSNMP_DISABLE_DES |
| #ifdef OLD_DES |
| DES_key_schedule key_sch; |
| #else |
| DES_key_schedule key_sched_store; |
| DES_key_schedule *key_sch = &key_sched_store; |
| #endif |
| DES_cblock key_struct; |
| #endif |
| #ifdef HAVE_AES |
| AES_KEY aes_key; |
| int new_ivlen = 0; |
| #endif |
| |
| DEBUGTRACE; |
| |
| /* |
| * Sanity check. |
| */ |
| #if !defined(NETSNMP_ENABLE_SCAPI_AUTHPRIV) |
| snmp_log(LOG_ERR, "Encryption support not enabled.\n"); |
| return SNMPERR_SC_NOT_CONFIGURED; |
| #endif |
| |
| if (!privtype || !key || !iv || !plaintext || !ciphertext || !ctlen |
| || (keylen <= 0) || (ivlen <= 0) || (ptlen <= 0) || (*ctlen <= 0) |
| || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } else if (ptlen > *ctlen) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| #ifdef NETSNMP_ENABLE_TESTING_CODE |
| { |
| size_t buf_len = 128, out_len = 0; |
| u_char *buf = (u_char *) malloc(buf_len); |
| |
| if (buf != NULL) { |
| if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, |
| iv, ivlen)) { |
| DEBUGMSGTL(("scapi", "encrypt: IV: %s/", buf)); |
| } else { |
| DEBUGMSGTL(("scapi", "encrypt: IV: %s [TRUNCATED]/", buf)); |
| } |
| out_len = 0; |
| if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, |
| key, keylen)) { |
| DEBUGMSG(("scapi", "%s\n", buf)); |
| } else { |
| DEBUGMSG(("scapi", "%s [TRUNCATED]\n", buf)); |
| } |
| out_len = 0; |
| if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, |
| plaintext, 16)) { |
| DEBUGMSGTL(("scapi", "encrypt: string: %s\n", buf)); |
| } else { |
| DEBUGMSGTL(("scapi", "encrypt: string: %s [TRUNCATED]\n", |
| buf)); |
| } |
| free(buf); |
| } else { |
| DEBUGMSGTL(("scapi", |
| "encrypt: malloc fail for debug output\n")); |
| } |
| } |
| #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
| |
| |
| /* |
| * Determine privacy transform. |
| */ |
| have_trans = 0; |
| #ifndef NETSNMP_DISABLE_DES |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); |
| pad_size = properlength; |
| have_trans = 1; |
| } |
| #endif |
| #ifdef HAVE_AES |
| if (ISTRANSFORM(privtype, AESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_AES_IV); |
| have_trans = 1; |
| } |
| #endif |
| if (!have_trans) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| |
| if ((keylen < properlength) || (ivlen < properlength_iv)) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| |
| memset(my_iv, 0, sizeof(my_iv)); |
| |
| #ifndef NETSNMP_DISABLE_DES |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| |
| /* |
| * now calculate the padding needed |
| */ |
| pad = pad_size - (ptlen % pad_size); |
| plast = (int) ptlen - (pad_size - pad); |
| if (pad == pad_size) |
| pad = 0; |
| if (ptlen + pad > *ctlen) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); /* not enough space */ |
| } |
| if (pad > 0) { /* copy data into pad block if needed */ |
| memcpy(pad_block, plaintext + plast, pad_size - pad); |
| memset(&pad_block[pad_size - pad], pad, pad); /* filling in padblock */ |
| } |
| |
| memcpy(key_struct, key, sizeof(key_struct)); |
| (void) DES_key_sched(&key_struct, key_sch); |
| |
| memcpy(my_iv, iv, ivlen); |
| /* |
| * encrypt the data |
| */ |
| DES_ncbc_encrypt(plaintext, ciphertext, plast, key_sch, |
| (DES_cblock *) my_iv, DES_ENCRYPT); |
| if (pad > 0) { |
| /* |
| * then encrypt the pad block |
| */ |
| DES_ncbc_encrypt(pad_block, ciphertext + plast, pad_size, |
| key_sch, (DES_cblock *) my_iv, DES_ENCRYPT); |
| *ctlen = plast + pad_size; |
| } else { |
| *ctlen = plast; |
| } |
| } |
| #endif |
| #ifdef HAVE_AES |
| if (ISTRANSFORM(privtype, AESPriv)) { |
| (void) AES_set_encrypt_key(key, properlength*8, &aes_key); |
| |
| memcpy(my_iv, iv, ivlen); |
| /* |
| * encrypt the data |
| */ |
| AES_cfb128_encrypt(plaintext, ciphertext, ptlen, |
| &aes_key, my_iv, &new_ivlen, AES_ENCRYPT); |
| *ctlen = ptlen; |
| } |
| #endif |
| sc_encrypt_quit: |
| /* |
| * clear memory just in case |
| */ |
| memset(my_iv, 0, sizeof(my_iv)); |
| memset(pad_block, 0, sizeof(pad_block)); |
| #ifndef NETSNMP_DISABLE_DES |
| memset(key_struct, 0, sizeof(key_struct)); |
| #ifdef OLD_DES |
| memset(&key_sch, 0, sizeof(key_sch)); |
| #else |
| memset(&key_sched_store, 0, sizeof(key_sched_store)); |
| #endif |
| #endif |
| #ifdef HAVE_AES |
| memset(&aes_key,0,sizeof(aes_key)); |
| #endif |
| return rval; |
| |
| } /* end sc_encrypt() */ |
| #elif defined(NETSNMP_USE_PKCS11) |
| { |
| int rval = SNMPERR_SUCCESS; |
| u_int properlength, properlength_iv; |
| u_char pkcs_des_key[8]; |
| |
| DEBUGTRACE; |
| |
| /* |
| * Sanity check. |
| */ |
| #if !defined(NETSNMP_ENABLE_SCAPI_AUTHPRIV) |
| snmp_log(LOG_ERR, "Encryption support not enabled.\n"); |
| return SNMPERR_SC_NOT_CONFIGURED; |
| #endif |
| |
| if (!privtype || !key || !iv || !plaintext || !ciphertext || !ctlen |
| || (keylen <= 0) || (ivlen <= 0) || (ptlen <= 0) || (*ctlen <= 0) |
| || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } else if (ptlen > *ctlen) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| |
| /* |
| * Determine privacy transform. |
| */ |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); |
| } else { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| |
| if ((keylen < properlength) || (ivlen < properlength_iv)) { |
| QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); |
| } |
| |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| memset(pkcs_des_key, 0, sizeof(pkcs_des_key)); |
| memcpy(pkcs_des_key, key, sizeof(pkcs_des_key)); |
| rval = pkcs_encrpyt(CKM_DES_CBC, pkcs_des_key, |
| sizeof(pkcs_des_key), iv, ivlen, plaintext, ptlen, |
| ciphertext, ctlen); |
| } |
| |
| sc_encrypt_quit: |
| return rval; |
| } |
| #else |
| { |
| # if NETSNMP_USE_INTERNAL_MD5 |
| { |
| snmp_log(LOG_ERR, "Encryption support not enabled.\n"); |
| DEBUGMSGTL(("scapi", "Encrypt function not defined.\n")); |
| return SNMPERR_SC_GENERAL_FAILURE; |
| } |
| |
| # else |
| _SCAPI_NOT_CONFIGURED |
| # endif /* NETSNMP_USE_INTERNAL_MD5 */ |
| } |
| #endif /* */ |
| |
| |
| |
| /*******************************************************************-o-****** |
| * sc_decrypt |
| * |
| * Parameters: |
| * privtype |
| * *key |
| * keylen |
| * *iv |
| * ivlen |
| * *ciphertext |
| * ctlen |
| * *plaintext |
| * *ptlen |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported. |
| * SNMPERR_SC_GENERAL_FAILURE Any other error |
| * |
| * |
| * Decrypt ciphertext into plaintext using key and iv. |
| * |
| * ptlen contains actual number of plaintext bytes in plaintext upon |
| * successful return. |
| */ |
| int |
| sc_decrypt(const oid * privtype, size_t privtypelen, |
| u_char * key, u_int keylen, |
| u_char * iv, u_int ivlen, |
| u_char * ciphertext, u_int ctlen, |
| u_char * plaintext, size_t * ptlen) |
| #ifdef NETSNMP_USE_OPENSSL |
| { |
| |
| int rval = SNMPERR_SUCCESS; |
| u_char my_iv[128]; |
| #ifndef NETSNMP_DISABLE_DES |
| #ifdef OLD_DES |
| DES_key_schedule key_sch; |
| #else |
| DES_key_schedule key_sched_store; |
| DES_key_schedule *key_sch = &key_sched_store; |
| #endif |
| DES_cblock key_struct; |
| #endif |
| u_int properlength = 0, properlength_iv = 0; |
| int have_transform; |
| #ifdef HAVE_AES |
| int new_ivlen = 0; |
| AES_KEY aes_key; |
| #endif |
| |
| DEBUGTRACE; |
| |
| if (!privtype || !key || !iv || !plaintext || !ciphertext || !ptlen |
| || (ctlen <= 0) || (*ptlen <= 0) || (*ptlen < ctlen) |
| || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| #ifdef NETSNMP_ENABLE_TESTING_CODE |
| { |
| size_t buf_len = 128, out_len = 0; |
| u_char *buf = (u_char *) malloc(buf_len); |
| |
| if (buf != NULL) { |
| if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, |
| iv, ivlen)) { |
| DEBUGMSGTL(("scapi", "decrypt: IV: %s/", buf)); |
| } else { |
| DEBUGMSGTL(("scapi", "decrypt: IV: %s [TRUNCATED]/", buf)); |
| } |
| out_len = 0; |
| if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, |
| key, keylen)) { |
| DEBUGMSG(("scapi", "%s\n", buf)); |
| } else { |
| DEBUGMSG(("scapi", "%s\n", buf)); |
| } |
| free(buf); |
| } else { |
| DEBUGMSGTL(("scapi", |
| "decrypt: malloc fail for debug output\n")); |
| } |
| } |
| #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
| |
| /* |
| * Determine privacy transform. |
| */ |
| have_transform = 0; |
| #ifndef NETSNMP_DISABLE_DES |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); |
| have_transform = 1; |
| } |
| #endif |
| #ifdef HAVE_AES |
| if (ISTRANSFORM(privtype, AESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_AES_IV); |
| have_transform = 1; |
| } |
| #endif |
| if (!have_transform) { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| |
| if ((keylen < properlength) || (ivlen < properlength_iv)) { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| |
| memset(my_iv, 0, sizeof(my_iv)); |
| #ifndef NETSNMP_DISABLE_DES |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| memcpy(key_struct, key, sizeof(key_struct)); |
| (void) DES_key_sched(&key_struct, key_sch); |
| |
| memcpy(my_iv, iv, ivlen); |
| DES_cbc_encrypt(ciphertext, plaintext, ctlen, key_sch, |
| (DES_cblock *) my_iv, DES_DECRYPT); |
| *ptlen = ctlen; |
| } |
| #endif |
| #ifdef HAVE_AES |
| if (ISTRANSFORM(privtype, AESPriv)) { |
| (void) AES_set_encrypt_key(key, properlength*8, &aes_key); |
| |
| memcpy(my_iv, iv, ivlen); |
| /* |
| * encrypt the data |
| */ |
| AES_cfb128_encrypt(ciphertext, plaintext, ctlen, |
| &aes_key, my_iv, &new_ivlen, AES_DECRYPT); |
| *ptlen = ctlen; |
| } |
| #endif |
| |
| /* |
| * exit cond |
| */ |
| sc_decrypt_quit: |
| #ifndef NETSNMP_DISABLE_DES |
| #ifdef OLD_DES |
| memset(&key_sch, 0, sizeof(key_sch)); |
| #else |
| memset(&key_sched_store, 0, sizeof(key_sched_store)); |
| #endif |
| memset(key_struct, 0, sizeof(key_struct)); |
| #endif |
| memset(my_iv, 0, sizeof(my_iv)); |
| return rval; |
| } /* USE OPEN_SSL */ |
| #elif NETSNMP_USE_PKCS11 /* USE PKCS */ |
| { |
| int rval = SNMPERR_SUCCESS; |
| u_int properlength, properlength_iv; |
| u_char pkcs_des_key[8]; |
| |
| DEBUGTRACE; |
| |
| if (!privtype || !key || !iv || !plaintext || !ciphertext || !ptlen |
| || (ctlen <= 0) || (*ptlen <= 0) || (*ptlen < ctlen) |
| || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| |
| /* |
| * Determine privacy transform. |
| */ |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); |
| properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); |
| } else { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| |
| if ((keylen < properlength) || (ivlen < properlength_iv)) { |
| QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); |
| } |
| |
| if (ISTRANSFORM(privtype, DESPriv)) { |
| memset(pkcs_des_key, 0, sizeof(pkcs_des_key)); |
| memcpy(pkcs_des_key, key, sizeof(pkcs_des_key)); |
| rval = pkcs_decrpyt(CKM_DES_CBC, pkcs_des_key, |
| sizeof(pkcs_des_key), iv, ivlen, ciphertext, |
| ctlen, plaintext, ptlen); |
| *ptlen = ctlen; |
| } |
| |
| sc_decrypt_quit: |
| return rval; |
| } /* USE PKCS */ |
| #else |
| { |
| #if !defined(NETSNMP_ENABLE_SCAPI_AUTHPRIV) |
| snmp_log(LOG_ERR, "Encryption support not enabled.\n"); |
| return SNMPERR_SC_NOT_CONFIGURED; |
| #else |
| # if NETSNMP_USE_INTERNAL_MD5 |
| { |
| DEBUGMSGTL(("scapi", "Decryption function not defined.\n")); |
| return SNMPERR_SC_GENERAL_FAILURE; |
| } |
| |
| # else |
| _SCAPI_NOT_CONFIGURED |
| # endif /* NETSNMP_USE_INTERNAL_MD5 */ |
| #endif /* */ |
| } |
| #endif /* NETSNMP_USE_OPENSSL */ |