1402 lines
49 KiB
C
1402 lines
49 KiB
C
/* 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.
|
|
*
|
|
* Portions of this file are copyrighted by:
|
|
* Copyright (c) 2016 VMware, Inc. All rights reserved.
|
|
* Use is subject to license terms specified in the COPYING file
|
|
* distributed with the Net-SNMP package.
|
|
*/
|
|
|
|
/*
|
|
* keytools.c
|
|
*/
|
|
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-features.h>
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#if HAVE_DMALLOC_H
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include <net-snmp/types.h>
|
|
#include <net-snmp/output_api.h>
|
|
#include <net-snmp/utilities.h>
|
|
|
|
#include <net-snmp/library/snmp_api.h>
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
# include <openssl/hmac.h>
|
|
#else
|
|
#ifdef NETSNMP_USE_INTERNAL_MD5
|
|
#include <net-snmp/library/md5.h>
|
|
#endif
|
|
#endif
|
|
#ifdef NETSNMP_USE_INTERNAL_CRYPTO
|
|
#include <net-snmp/library/openssl_md5.h>
|
|
#include <net-snmp/library/openssl_sha.h>
|
|
#endif
|
|
|
|
#ifdef NETSNMP_USE_PKCS11
|
|
#include <security/cryptoki.h>
|
|
#endif
|
|
|
|
#include <net-snmp/library/scapi.h>
|
|
#include <net-snmp/library/keytools.h>
|
|
|
|
#include <net-snmp/library/transform_oids.h>
|
|
|
|
#include <net-snmp/library/snmp_secmod.h>
|
|
#include <net-snmp/library/snmpusm.h>
|
|
|
|
netsnmp_feature_child_of(usm_support, libnetsnmp)
|
|
netsnmp_feature_child_of(usm_keytools, usm_support)
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_USM_KEYTOOLS
|
|
|
|
/*******************************************************************-o-******
|
|
* generate_Ku
|
|
*
|
|
* Parameters:
|
|
* *hashtype MIB OID for the transform type for hashing.
|
|
* hashtype_len Length of OID value.
|
|
* *P Pre-allocated bytes of passpharase.
|
|
* pplen Length of passphrase.
|
|
* *Ku Buffer to contain Ku.
|
|
* *kulen Length of Ku buffer.
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*
|
|
*
|
|
* Convert a passphrase into a master user key, Ku, according to the
|
|
* algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
|
|
* as follows:
|
|
*
|
|
* Expand the passphrase to fill the passphrase buffer space, if necessary,
|
|
* concatenation as many duplicates as possible of P to itself. If P is
|
|
* larger than the buffer space, truncate it to fit.
|
|
*
|
|
* Then hash the result with the given hashtype transform. Return
|
|
* the result as Ku.
|
|
*
|
|
* If successful, kulen contains the size of the hash written to Ku.
|
|
*
|
|
* NOTE Passphrases less than USM_LENGTH_P_MIN characters in length
|
|
* cause an error to be returned.
|
|
* (Punt this check to the cmdline apps? XXX)
|
|
*/
|
|
int
|
|
generate_Ku(const oid * hashtype, u_int hashtype_len,
|
|
const u_char * P, size_t pplen, u_char * Ku, size_t * kulen)
|
|
#if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
|
|
{
|
|
int rval = SNMPERR_SUCCESS,
|
|
nbytes = USM_LENGTH_EXPANDED_PASSPHRASE, auth_type;
|
|
#if !defined(NETSNMP_USE_OPENSSL) && \
|
|
defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
|
|
int ret;
|
|
#endif
|
|
|
|
u_int i, pindex = 0;
|
|
|
|
u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp;
|
|
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
EVP_MD_CTX *ctx = NULL;
|
|
const EVP_MD *hashfn = NULL;
|
|
#elif NETSNMP_USE_INTERNAL_CRYPTO
|
|
SHA_CTX csha1;
|
|
MD5_CTX cmd5;
|
|
char cryptotype = 0;
|
|
#define TYPE_MD5 1
|
|
#define TYPE_SHA1 2
|
|
#else
|
|
MDstruct MD;
|
|
#endif
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)) {
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
if (pplen < USM_LENGTH_P_MIN) {
|
|
snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
|
|
"requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
|
|
snmp_set_detail("The supplied password length is too short.");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
auth_type = sc_get_authtype(hashtype, hashtype_len);
|
|
if (SNMPERR_GENERR == auth_type) {
|
|
snmp_log(LOG_ERR, "Error: unknown authtype");
|
|
snmp_set_detail("unknown authtype");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
/*
|
|
* Setup for the transform type.
|
|
*/
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
|
|
if (*kulen < EVP_MAX_MD_SIZE) {
|
|
snmp_log(LOG_ERR, "Internal Error: ku buffer too small (min=%d).\n",
|
|
EVP_MAX_MD_SIZE);
|
|
snmp_set_detail("Internal Error: ku buffer too small.");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
/** get hash function */
|
|
hashfn = sc_get_openssl_hashfn(auth_type);
|
|
if (NULL == hashfn) {
|
|
snmp_log(LOG_ERR, "Error: no hashfn for authtype");
|
|
snmp_set_detail("no hashfn for authtype");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
#if defined(HAVE_EVP_MD_CTX_NEW)
|
|
ctx = EVP_MD_CTX_new();
|
|
#elif defined(HAVE_EVP_MD_CTX_CREATE)
|
|
ctx = EVP_MD_CTX_create();
|
|
#else
|
|
ctx = malloc(sizeof(*ctx));
|
|
if (!EVP_MD_CTX_init(ctx))
|
|
return SNMPERR_GENERR;
|
|
#endif
|
|
if (!EVP_DigestInit(ctx, hashfn))
|
|
return SNMPERR_GENERR;
|
|
|
|
#elif NETSNMP_USE_INTERNAL_CRYPTO
|
|
#ifndef NETSNMP_DISABLE_MD5
|
|
if (NETSNMP_USMAUTH_HMACMD5 == auth_type) {
|
|
if (!MD5_Init(&cmd5))
|
|
return SNMPERR_GENERR;
|
|
cryptotype = TYPE_MD5;
|
|
} else
|
|
#endif
|
|
if (NETSNMP_USMAUTH_HMACSHA1 == auth_type) {
|
|
if (!SHA1_Init(&csha1))
|
|
return SNMPERR_GENERR;
|
|
cryptotype = TYPE_SHA1;
|
|
} else {
|
|
return (SNMPERR_GENERR);
|
|
}
|
|
#else
|
|
MDbegin(&MD);
|
|
#endif /* NETSNMP_USE_OPENSSL */
|
|
|
|
while (nbytes > 0) {
|
|
bufp = buf;
|
|
for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
|
|
*bufp++ = P[pindex++ % pplen];
|
|
}
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
|
|
#elif NETSNMP_USE_INTERNAL_CRYPTO
|
|
if (TYPE_SHA1 == cryptotype) {
|
|
rval = !SHA1_Update(&csha1, buf, USM_LENGTH_KU_HASHBLOCK);
|
|
} else {
|
|
rval = !MD5_Update(&cmd5, buf, USM_LENGTH_KU_HASHBLOCK);
|
|
}
|
|
if (rval != 0) {
|
|
return SNMPERR_USM_ENCRYPTIONERROR;
|
|
}
|
|
#elif NETSNMP_USE_INTERNAL_MD5
|
|
if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) {
|
|
rval = SNMPERR_USM_ENCRYPTIONERROR;
|
|
goto md5_fin;
|
|
}
|
|
#endif /* NETSNMP_USE_OPENSSL */
|
|
nbytes -= USM_LENGTH_KU_HASHBLOCK;
|
|
}
|
|
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
{
|
|
unsigned int tmp_len;
|
|
|
|
tmp_len = *kulen;
|
|
EVP_DigestFinal(ctx, (unsigned char *) Ku, &tmp_len);
|
|
*kulen = tmp_len;
|
|
/*
|
|
* what about free()
|
|
*/
|
|
}
|
|
#elif NETSNMP_USE_INTERNAL_CRYPTO
|
|
if (TYPE_SHA1 == cryptotype) {
|
|
SHA1_Final(Ku, &csha1);
|
|
} else {
|
|
MD5_Final(Ku, &cmd5);
|
|
}
|
|
ret = sc_get_properlength(hashtype, hashtype_len);
|
|
if (ret == SNMPERR_GENERR)
|
|
return SNMPERR_GENERR;
|
|
*kulen = ret;
|
|
#elif NETSNMP_USE_INTERNAL_MD5
|
|
if (MDupdate(&MD, buf, 0)) {
|
|
rval = SNMPERR_USM_ENCRYPTIONERROR;
|
|
goto md5_fin;
|
|
}
|
|
ret = sc_get_properlength(hashtype, hashtype_len);
|
|
if (ret == SNMPERR_GENERR)
|
|
return SNMPERR_GENERR;
|
|
*kulen = ret;
|
|
MDget(&MD, Ku, *kulen);
|
|
md5_fin:
|
|
memset(&MD, 0, sizeof(MD));
|
|
#endif /* NETSNMP_USE_INTERNAL_MD5 */
|
|
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGMSGTL(("generate_Ku", "generating Ku (%s from %s): ",
|
|
usm_lookup_auth_str(auth_type), P));
|
|
for (i = 0; i < *kulen; i++)
|
|
DEBUGMSG(("generate_Ku", "%02x", Ku[i]));
|
|
DEBUGMSG(("generate_Ku", "\n"));
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
|
|
generate_Ku_quit:
|
|
memset(buf, 0, sizeof(buf));
|
|
#ifdef NETSNMP_USE_OPENSSL
|
|
if (ctx) {
|
|
#if defined(HAVE_EVP_MD_CTX_FREE)
|
|
EVP_MD_CTX_free(ctx);
|
|
#elif defined(HAVE_EVP_MD_CTX_DESTROY)
|
|
EVP_MD_CTX_destroy(ctx);
|
|
#else
|
|
EVP_MD_CTX_cleanup(ctx);
|
|
free(ctx);
|
|
#endif
|
|
}
|
|
#endif
|
|
return rval;
|
|
|
|
} /* end generate_Ku() */
|
|
#elif NETSNMP_USE_PKCS11
|
|
{
|
|
int rval = SNMPERR_SUCCESS, auth_type;;
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)) {
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
if (pplen < USM_LENGTH_P_MIN) {
|
|
snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
|
|
"requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
|
|
snmp_set_detail("The supplied password length is too short.");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
auth_type = sc_get_authtype(hashtype, hashtype_len);
|
|
if (SNMPERR_GENERR == auth_type) {
|
|
snmp_log(LOG_ERR, "Error: unknown authtype");
|
|
snmp_set_detail("unknown authtype");
|
|
QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
|
|
}
|
|
|
|
|
|
/*
|
|
* Setup for the transform type.
|
|
*/
|
|
|
|
#ifndef NETSNMP_DISABLE_MD5
|
|
if (NETSNMP_USMAUTH_HMACMD5 == auth_type)
|
|
return pkcs_generate_Ku(CKM_MD5, P, pplen, Ku, kulen);
|
|
else
|
|
#endif
|
|
if (NETSNMP_USMAUTH_HMACSHA1 == auth_type)
|
|
return pkcs_generate_Ku(CKM_SHA_1, P, pplen, Ku, kulen);
|
|
else {
|
|
return (SNMPERR_GENERR);
|
|
}
|
|
|
|
generate_Ku_quit:
|
|
|
|
return rval;
|
|
} /* end generate_Ku() */
|
|
#else
|
|
_KEYTOOLS_NOT_AVAILABLE
|
|
#endif /* internal or openssl */
|
|
/*******************************************************************-o-******
|
|
* generate_kul
|
|
*
|
|
* Parameters:
|
|
* *hashtype
|
|
* hashtype_len
|
|
* *engineID
|
|
* engineID_len
|
|
* *Ku Master key for a given user.
|
|
* ku_len Length of Ku in bytes.
|
|
* *Kul Localized key for a given user at engineID.
|
|
* *kul_len Length of Kul buffer (IN); Length of Kul key (OUT).
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*
|
|
*
|
|
* Ku MUST be the proper length (currently fixed) for the given hashtype.
|
|
*
|
|
* Upon successful return, Kul contains the localized form of Ku at
|
|
* engineID, and the length of the key is stored in kul_len.
|
|
*
|
|
* The localized key method is defined in RFC2274, Sections 2.6 and A.2, and
|
|
* originally documented in:
|
|
* U. Blumenthal, N. C. Hien, B. Wijnen,
|
|
* "Key Derivation for Network Management Applications",
|
|
* IEEE Network Magazine, April/May issue, 1997.
|
|
*
|
|
*
|
|
* ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku).
|
|
*
|
|
* NOTE Localized keys for privacy transforms are generated via
|
|
* the authentication transform held by the same usmUser.
|
|
*
|
|
* XXX An engineID of any length is accepted, even if larger than
|
|
* what is spec'ed for the textual convention.
|
|
*/
|
|
int
|
|
generate_kul(const oid * hashtype, u_int hashtype_len,
|
|
const u_char * engineID, size_t engineID_len,
|
|
const u_char * Ku, size_t ku_len,
|
|
u_char * Kul, size_t * kul_len)
|
|
#if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
|
|
{
|
|
int rval = SNMPERR_SUCCESS, auth_type;
|
|
u_int nbytes = 0;
|
|
size_t properlength;
|
|
int iproperlength;
|
|
|
|
u_char buf[SNMP_MAXBUF];
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
int i;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if (!hashtype || !engineID || !Ku || !Kul || !kul_len
|
|
|| (engineID_len <= 0) || (ku_len <= 0) || (*kul_len <= 0)) {
|
|
QUITFUN(SNMPERR_GENERR, generate_kul_quit);
|
|
}
|
|
|
|
auth_type = sc_get_authtype(hashtype, hashtype_len);
|
|
if (SNMPERR_GENERR == auth_type)
|
|
QUITFUN(SNMPERR_GENERR, generate_kul_quit);
|
|
|
|
iproperlength = sc_get_proper_auth_length_bytype(auth_type);
|
|
if (iproperlength == SNMPERR_GENERR)
|
|
QUITFUN(SNMPERR_GENERR, generate_kul_quit);
|
|
|
|
properlength = (size_t) iproperlength;
|
|
|
|
if ((*kul_len < properlength) || (ku_len < properlength)) {
|
|
QUITFUN(SNMPERR_GENERR, generate_kul_quit);
|
|
}
|
|
|
|
/*
|
|
* Concatenate Ku and engineID properly, then hash the result.
|
|
* Store it in Kul.
|
|
*/
|
|
nbytes = 0;
|
|
memcpy(buf, Ku, properlength);
|
|
nbytes += properlength;
|
|
memcpy(buf + nbytes, engineID, engineID_len);
|
|
nbytes += engineID_len;
|
|
memcpy(buf + nbytes, Ku, properlength);
|
|
nbytes += properlength;
|
|
|
|
rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len);
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGMSGTL(("generate_kul", "generating Kul (%s from Ku): ",
|
|
usm_lookup_auth_str(auth_type) ));
|
|
for (i = 0; i < *kul_len; i++)
|
|
DEBUGMSG(("generate_kul", "%02x", Kul[i]));
|
|
DEBUGMSG(("generate_kul", "keytools\n"));
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
QUITFUN(rval, generate_kul_quit);
|
|
|
|
|
|
generate_kul_quit:
|
|
return rval;
|
|
|
|
} /* end generate_kul() */
|
|
|
|
#else
|
|
_KEYTOOLS_NOT_AVAILABLE
|
|
#endif /* internal or openssl */
|
|
|
|
/*******************************************************************-o-******
|
|
* _kul_extend_blumenthal
|
|
*
|
|
* Parameters:
|
|
* *hashoid MIB OID for the hash transform type.
|
|
* hashoid_len Length of the MIB OID hash transform type.
|
|
* *origKul original kul (localized; IN/OUT)
|
|
* *origKulLen Length of original kul in bytes (IN/OUT)
|
|
* origKulSize Size of original kul buffer
|
|
* needKeyLen Size needed for key
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*/
|
|
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
|
|
static int
|
|
_kul_extend_blumenthal(int needKeyLen, oid *hashoid, u_int hashoid_len,
|
|
u_char *origKul, size_t *origKulLen, int origKulSize)
|
|
{
|
|
u_char newKul[USM_LENGTH_KU_HASHBLOCK];
|
|
size_t newKulLen;
|
|
int count, hashBits, hashBytes, authtype, i, saveLen;
|
|
|
|
DEBUGMSGTL(("usm:extend_kul", " blumenthal called\n"));
|
|
|
|
if (NULL == hashoid || NULL == origKul || NULL == origKulLen ||
|
|
needKeyLen > origKulSize) {
|
|
DEBUGMSGTL(("usm:extend_kul", "bad parameters\n"));
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
authtype = sc_get_authtype(hashoid, hashoid_len);
|
|
if (authtype < 0 ) {
|
|
DEBUGMSGTL(("usm:extend_kul", "unknown authtype\n"));
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
saveLen = *origKulLen;
|
|
needKeyLen -= *origKulLen; /* subtract bytes we already have */
|
|
|
|
/*
|
|
* 3.1.2.1:
|
|
* 1)Let Hnnn() the hash function of the authentication protocol for
|
|
* the user U on the SNMP authoritative engine E. nnn being the size
|
|
* of the output of the hash function (e.g. nnn=128 bits for MD5, or
|
|
* nnn=160 bits for SHA1).
|
|
*/
|
|
hashBytes = sc_get_proper_auth_length_bytype(authtype);
|
|
hashBits = 8 * hashBytes;
|
|
|
|
/* 3.1.2.1:
|
|
* 2)Set c = ceil ( 256 / nnn )
|
|
*/
|
|
count = ceil( (double)256 / (double)hashBits );
|
|
DEBUGMSGTL(("9:usm:extend_kul:blumenthal", "count ceiling %d\n", count));
|
|
|
|
/* 3.1.2.1:
|
|
* 3)For i = 1, 2, ..., c
|
|
* a.Set Kul = Kul || Hnnn(Kul); Where Hnnn() is the hash
|
|
* function of the authentication protocol defined for that user
|
|
*/
|
|
for(i = 0; i < count; ++i) {
|
|
int copyLen, rc;
|
|
|
|
newKulLen = sizeof(newKul);
|
|
rc = sc_hash_type( authtype, origKul, *origKulLen, newKul, &newKulLen);
|
|
if (SNMPERR_SUCCESS != rc) {
|
|
DEBUGMSGTL(("usm:extend_kul", "error from sc_hash_type\n"));
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
copyLen = SNMP_MIN(needKeyLen, newKulLen);
|
|
memcpy(origKul + *origKulLen, newKul, copyLen);
|
|
needKeyLen -= copyLen;
|
|
*origKulLen += copyLen;
|
|
|
|
/** not part of the draft, but stop if we already have enought bits */
|
|
if (needKeyLen <= 0)
|
|
break;
|
|
}
|
|
|
|
DEBUGMSGTL(("usm:extend_kul:blumenthal",
|
|
"orig len %d, new len %" NETSNMP_PRIz "d\n",
|
|
saveLen, *origKulLen));
|
|
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
#endif /* NETSNMP_DRAFT_BLUMENTHAL_AES_04 */
|
|
|
|
/*******************************************************************-o-******
|
|
* _kul_extend_reeder
|
|
*
|
|
* Parameters:
|
|
* *hashoid MIB OID for the hash transform type.
|
|
* hashoid_len Length of the MIB OID hash transform type.
|
|
* *engineID engineID
|
|
* engineIDLen Length of engineID
|
|
* *origKul original kul (localized; IN/OUT)
|
|
* *origKulLen Length of original kul in bytes (IN/OUT)
|
|
* origKulSize Size of original kul buffer
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*/
|
|
#if defined(NETSNMP_DRAFT_REEDER_3DES) || defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04)
|
|
static int
|
|
_kul_extend_reeder(int needKeyLen, oid *hashoid, u_int hashoid_len,
|
|
u_char *engineID, int engineIDLen,
|
|
u_char *origKul, size_t *origKulLen, int origKulSize)
|
|
{
|
|
u_char newKu[USM_LENGTH_KU_HASHBLOCK], newKul[USM_LENGTH_KU_HASHBLOCK];
|
|
size_t newKuLen, newKulLen, saveLen;
|
|
int authType, copylen;
|
|
|
|
DEBUGMSGTL(("usm:extend_kul", " reeder called\n"));
|
|
|
|
if (NULL == hashoid || NULL == engineID || NULL == origKul ||
|
|
NULL == origKulLen || needKeyLen > origKulSize)
|
|
return SNMPERR_GENERR;
|
|
|
|
authType = sc_get_authtype(hashoid, hashoid_len);
|
|
if (SNMPERR_GENERR == authType)
|
|
return SNMPERR_GENERR;
|
|
newKulLen = sc_get_proper_auth_length_bytype(authType);
|
|
saveLen = *origKulLen;
|
|
needKeyLen -= *origKulLen; /* subtract bytes we already have */
|
|
while (needKeyLen > 0) {
|
|
|
|
newKuLen = sizeof(newKu);
|
|
/*
|
|
* hash the existing key using the passphrase to Ku algorithm.
|
|
* [ Ku' = Ku(kul) ]
|
|
*/
|
|
if (generate_Ku(hashoid, hashoid_len, origKul, *origKulLen,
|
|
newKu, &newKuLen) != SNMPERR_SUCCESS)
|
|
return SNMPERR_GENERR;
|
|
|
|
/*
|
|
* localize the new key generated from current localized key
|
|
* and append it to the current localized key.
|
|
* [ kul' = kul || kul(Ku') ]
|
|
*/
|
|
newKulLen = sizeof(newKul);
|
|
if(generate_kul(hashoid, hashoid_len, engineID, engineIDLen,
|
|
newKu, newKuLen, newKul,
|
|
&newKulLen) != SNMPERR_SUCCESS)
|
|
return SNMPERR_GENERR;
|
|
|
|
copylen = SNMP_MIN(needKeyLen, newKulLen);
|
|
memcpy(origKul + *origKulLen, newKul, copylen);
|
|
needKeyLen -= copylen;
|
|
*origKulLen += copylen;
|
|
}
|
|
|
|
DEBUGMSGTL(("usm:extend_kul:reeder",
|
|
"orig len %" NETSNMP_PRIz "d, new len %" NETSNMP_PRIz "d\n",
|
|
saveLen, *origKulLen));
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
#endif /* NETSNMP_DRAFT_REEDER_3DES || NETSNMP_DRAFT_BLUMENTHAL_AES_04 */
|
|
|
|
/*******************************************************************-o-******
|
|
* netsnmp_extend_key
|
|
*
|
|
* Extend a kul buffer to the needed key size. if the passed kulBuf
|
|
* is not large enough, a new one will be allocated and the old one
|
|
* will be freed.
|
|
*
|
|
* Parameters:
|
|
* neededKeyLen The neede key length
|
|
* *hashoid MIB OID for the hash transform type.
|
|
* hashoid_len Length of the MIB OID hash transform type.
|
|
* privType Privay algorithm type
|
|
* engineID engineID
|
|
* engineIDLen Length of engineID
|
|
* **kulBuf Pointer to a buffer pointer
|
|
* *kulBufLen Length of current kul buffer
|
|
* kulBufSize Allocated size of current kul buffer
|
|
*
|
|
* OUT:
|
|
* **kulBuf New kulBuf pointer (if it needed to be expanded)
|
|
* *kulBufLen Length of new kul buffer
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*/
|
|
int
|
|
netsnmp_extend_kul(u_int needKeyLen, oid *hashoid, u_int hashoid_len,
|
|
int privType, u_char *engineID, u_int engineIDLen,
|
|
u_char **kulBuf, size_t *kulBufLen, u_int kulBufSize)
|
|
{
|
|
int ret;
|
|
u_char *newKul;
|
|
size_t newKulLen;
|
|
|
|
DEBUGMSGTL(("9:usm:extend_kul", " called\n"));
|
|
|
|
if (*kulBufLen >= needKeyLen) {
|
|
DEBUGMSGTL(("usm:extend_kul", " key already big enough\n"));
|
|
return SNMPERR_SUCCESS; /* already have enough key material */
|
|
}
|
|
|
|
switch (privType & (USM_PRIV_MASK_ALG | USM_PRIV_MASK_VARIANT)) {
|
|
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
|
|
case USM_CREATE_USER_PRIV_AES192:
|
|
case USM_CREATE_USER_PRIV_AES256:
|
|
break;
|
|
#endif
|
|
#if defined(NETSNMP_DRAFT_REEDER_3DES) || defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04)
|
|
case USM_CREATE_USER_PRIV_3DES:
|
|
break;
|
|
#endif
|
|
default:
|
|
DEBUGMSGTL(("usm:extend_kul",
|
|
"no extension method defined for priv type 0x%x\n",
|
|
privType));
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
|
|
DEBUGMSGTL(("usm:extend_kul", " have %" NETSNMP_PRIz "d bytes; need %d\n",
|
|
*kulBufLen, needKeyLen));
|
|
|
|
if (kulBufSize < needKeyLen) {
|
|
newKul = calloc(1, needKeyLen);
|
|
if (NULL == newKul)
|
|
return SNMPERR_GENERR;
|
|
memcpy(newKul, *kulBuf, *kulBufLen);
|
|
newKulLen = *kulBufLen;
|
|
kulBufSize = needKeyLen;
|
|
}
|
|
else {
|
|
newKul = *kulBuf;
|
|
newKulLen = *kulBufLen;
|
|
}
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("9:usm:extend_kul") {
|
|
int i;
|
|
DEBUGMSG(("9:usm:extend_kul",
|
|
"key: key=0x"));
|
|
for (i = 0; i < newKulLen; i++)
|
|
DEBUGMSG(("9:usm:extend_kul", "%02x", newKul[i] & 0xff));
|
|
DEBUGMSG(("9:usm:extend_kul", " (%ld)\n", newKulLen));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
ret = SNMPERR_SUCCESS; /* most privTypes don't need extended kul */
|
|
switch (privType & (USM_PRIV_MASK_ALG | USM_PRIV_MASK_VARIANT)) {
|
|
#ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04
|
|
case USM_CREATE_USER_PRIV_AES192:
|
|
case USM_CREATE_USER_PRIV_AES256:
|
|
{
|
|
int reeder = privType & USM_AES_REEDER_FLAG;
|
|
if (reeder)
|
|
ret = _kul_extend_reeder(needKeyLen, hashoid, hashoid_len,
|
|
engineID, engineIDLen,
|
|
newKul, &newKulLen, kulBufSize);
|
|
else
|
|
ret = _kul_extend_blumenthal(needKeyLen, hashoid, hashoid_len,
|
|
newKul, &newKulLen, kulBufSize);
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(NETSNMP_DRAFT_REEDER_3DES)
|
|
case USM_CREATE_USER_PRIV_3DES:
|
|
ret = _kul_extend_reeder(needKeyLen, hashoid, hashoid_len,
|
|
engineID, engineIDLen,
|
|
newKul, &newKulLen, kulBufSize);
|
|
break;
|
|
#endif
|
|
default:
|
|
DEBUGMSGTL(("usm:extend_kul",
|
|
"unknown priv type 0x%x\n", privType));
|
|
ret = SNMPERR_GENERR;
|
|
}
|
|
|
|
if (SNMPERR_SUCCESS == ret) {
|
|
*kulBufLen = newKulLen;
|
|
if (newKul != *kulBuf) {
|
|
free(*kulBuf);
|
|
*kulBuf = newKul;
|
|
}
|
|
}
|
|
else {
|
|
if (newKul != *kulBuf)
|
|
free(newKul);
|
|
}
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("9:usm:extend_kul") {
|
|
int i;
|
|
DEBUGMSG(("usm:extend_kul",
|
|
"key: key=0x"));
|
|
for (i = 0; i < newKulLen; i++)
|
|
DEBUGMSG(("usm:extend_kul", "%02x", newKul[i] & 0xff));
|
|
DEBUGMSG(("usm:extend_kul", " (%ld)\n", newKulLen));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************-o-******
|
|
* encode_keychange
|
|
*
|
|
* Parameters:
|
|
* *hashtype MIB OID for the hash transform type.
|
|
* hashtype_len Length of the MIB OID hash transform type.
|
|
* *oldkey Old key that is used to encodes the new key.
|
|
* oldkey_len Length of oldkey in bytes.
|
|
* *newkey New key that is encoded using the old key.
|
|
* newkey_len Length of new key in bytes.
|
|
* *kcstring Buffer to contain the KeyChange TC string.
|
|
* *kcstring_len Length of kcstring buffer.
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*
|
|
*
|
|
* Uses oldkey and acquired random bytes to encode newkey into kcstring
|
|
* according to the rules of the KeyChange TC described in RFC 2274, Section 5.
|
|
*
|
|
* Upon successful return, *kcstring_len contains the length of the
|
|
* encoded string.
|
|
*
|
|
* ASSUMES Old and new key are always equal to each other, although
|
|
* this may be less than the transform type hash output
|
|
* output length (eg, using KeyChange for a DESPriv key when
|
|
* the user also uses SHA1Auth). This also implies that the
|
|
* hash placed in the second 1/2 of the key change string
|
|
* will be truncated before the XOR'ing when the hash output is
|
|
* larger than that 1/2 of the key change string.
|
|
*
|
|
* *kcstring_len will be returned as exactly twice that same
|
|
* length though the input buffer may be larger.
|
|
*
|
|
* XXX FIX: Does not handle varibable length keys.
|
|
* XXX FIX: Does not handle keys larger than the hash algorithm used.
|
|
*
|
|
KeyChange ::= TEXTUAL-CONVENTION
|
|
STATUS current
|
|
DESCRIPTION
|
|
"Every definition of an object with this syntax must identify
|
|
a protocol P, a secret key K, and a hash algorithm H
|
|
that produces output of L octets.
|
|
|
|
The object's value is a manager-generated, partially-random
|
|
value which, when modified, causes the value of the secret
|
|
key K, to be modified via a one-way function.
|
|
|
|
The value of an instance of this object is the concatenation
|
|
of two components: first a 'random' component and then a
|
|
'delta' component.
|
|
|
|
The lengths of the random and delta components
|
|
are given by the corresponding value of the protocol P;
|
|
if P requires K to be a fixed length, the length of both the
|
|
random and delta components is that fixed length; if P
|
|
allows the length of K to be variable up to a particular
|
|
maximum length, the length of the random component is that
|
|
maximum length and the length of the delta component is any
|
|
length less than or equal to that maximum length.
|
|
For example, usmHMACMD5AuthProtocol requires K to be a fixed
|
|
length of 16 octets and L - of 16 octets.
|
|
usmHMACSHAAuthProtocol requires K to be a fixed length of
|
|
20 octets and L - of 20 octets. Other protocols may define
|
|
other sizes, as deemed appropriate.
|
|
|
|
When a requester wants to change the old key K to a new
|
|
key keyNew on a remote entity, the 'random' component is
|
|
obtained from either a true random generator, or from a
|
|
pseudorandom generator, and the 'delta' component is
|
|
computed as follows:
|
|
|
|
- a temporary variable is initialized to the existing value
|
|
of K;
|
|
- if the length of the keyNew is greater than L octets,
|
|
then:
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
the hash algorithm H to produce a digest value, and
|
|
the temporary variable is set to this digest value;
|
|
- the value of the temporary variable is XOR-ed with
|
|
the first (next) L-octets (16 octets in case of MD5)
|
|
of the keyNew to produce the first (next) L-octets
|
|
(16 octets in case of MD5) of the 'delta' component.
|
|
- the above two steps are repeated until the unused
|
|
portion of the keyNew component is L octets or less,
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
hash algorithm H to produce a digest value;
|
|
- this digest value, truncated if necessary to be the same
|
|
length as the unused portion of the keyNew, is XOR-ed
|
|
with the unused portion of the keyNew to produce the
|
|
(final portion of the) 'delta' component.
|
|
|
|
For example, using MD5 as the hash algorithm H:
|
|
|
|
iterations = (lenOfDelta - 1)/16; -- integer division
|
|
temp = keyOld;
|
|
for (i = 0; i < iterations; i++) {
|
|
temp = MD5 (temp || random);
|
|
delta[i*16 .. (i*16)+15] =
|
|
temp XOR keyNew[i*16 .. (i*16)+15];
|
|
}
|
|
temp = MD5 (temp || random);
|
|
delta[i*16 .. lenOfDelta-1] =
|
|
temp XOR keyNew[i*16 .. lenOfDelta-1];
|
|
|
|
The 'random' and 'delta' components are then concatenated as
|
|
described above, and the resulting octet string is sent to
|
|
the recipient as the new value of an instance of this object.
|
|
|
|
At the receiver side, when an instance of this object is set
|
|
to a new value, then a new value of K is computed as follows:
|
|
|
|
- a temporary variable is initialized to the existing value
|
|
of K;
|
|
- if the length of the delta component is greater than L
|
|
octets, then:
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
hash algorithm H to produce a digest value, and the
|
|
temporary variable is set to this digest value;
|
|
- the value of the temporary variable is XOR-ed with
|
|
the first (next) L-octets (16 octets in case of MD5)
|
|
of the delta component to produce the first (next)
|
|
L-octets (16 octets in case of MD5) of the new value
|
|
of K.
|
|
- the above two steps are repeated until the unused
|
|
portion of the delta component is L octets or less,
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
hash algorithm H to produce a digest value;
|
|
- this digest value, truncated if necessary to be the same
|
|
length as the unused portion of the delta component, is
|
|
XOR-ed with the unused portion of the delta component to
|
|
produce the (final portion of the) new value of K.
|
|
|
|
For example, using MD5 as the hash algorithm H:
|
|
|
|
iterations = (lenOfDelta - 1)/16; -- integer division
|
|
temp = keyOld;
|
|
for (i = 0; i < iterations; i++) {
|
|
temp = MD5 (temp || random);
|
|
keyNew[i*16 .. (i*16)+15] =
|
|
temp XOR delta[i*16 .. (i*16)+15];
|
|
}
|
|
temp = MD5 (temp || random);
|
|
keyNew[i*16 .. lenOfDelta-1] =
|
|
temp XOR delta[i*16 .. lenOfDelta-1];
|
|
|
|
The value of an object with this syntax, whenever it is
|
|
retrieved by the management protocol, is always the zero
|
|
length string.
|
|
|
|
Note that the keyOld and keyNew are the localized keys.
|
|
|
|
Note that it is probably wise that when an SNMP entity sends
|
|
a SetRequest to change a key, that it keeps a copy of the old
|
|
key until it has confirmed that the key change actually
|
|
succeeded.
|
|
"
|
|
SYNTAX OCTET STRING
|
|
*
|
|
*/
|
|
int
|
|
encode_keychange(const oid * hashtype, u_int hashtype_len,
|
|
u_char * oldkey, size_t oldkey_len,
|
|
u_char * newkey, size_t newkey_len,
|
|
u_char * kcstring, size_t * kcstring_len)
|
|
#if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
|
|
{
|
|
u_char tmpbuf[SNMP_MAXBUF_SMALL], digest[SNMP_MAXBUF_SMALL];
|
|
u_char delta[SNMP_MAXBUF_SMALL];
|
|
u_char *tmpp = tmpbuf, *randp;
|
|
int rval = SNMPERR_SUCCESS, auth_type;
|
|
int iauth_length, tmp_len;
|
|
size_t auth_length, rand_len, digest_len, delta_len = 0;
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if (!hashtype || !oldkey || !newkey || !kcstring || !kcstring_len
|
|
|| (oldkey_len != newkey_len ) || (newkey_len == 0)
|
|
|| (*kcstring_len < (2 * newkey_len))) {
|
|
QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
|
|
}
|
|
|
|
/*
|
|
* Setup for the transform type.
|
|
*/
|
|
auth_type = sc_get_authtype(hashtype, hashtype_len);
|
|
iauth_length = sc_get_proper_auth_length_bytype(auth_type);
|
|
if (iauth_length == SNMPERR_GENERR)
|
|
QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
|
|
|
|
auth_length = SNMP_MIN(oldkey_len, (size_t)iauth_length);
|
|
|
|
DEBUGMSGTL(("encode_keychange",
|
|
"oldkey_len %" NETSNMP_PRIz "d, newkey_len %" NETSNMP_PRIz "d, auth_length %" NETSNMP_PRIz "d\n",
|
|
oldkey_len, newkey_len, auth_length));
|
|
|
|
/*
|
|
* Use the old key and some random bytes to encode the new key
|
|
* in the KeyChange TC format:
|
|
* . Get random bytes (store in first half of kcstring),
|
|
* . Hash (oldkey | random_bytes) (into second half of kcstring),
|
|
* . XOR hash and newkey (into second half of kcstring).
|
|
*
|
|
* Getting the wrong number of random bytes is considered an error.
|
|
*/
|
|
randp = kcstring;
|
|
rand_len = oldkey_len;
|
|
|
|
memset(randp, 0x0, rand_len);
|
|
/*
|
|
* KeyChange ::= TEXTUAL-CONVENTION
|
|
* STATUS current
|
|
* DESCRIPTION
|
|
* [...]
|
|
* When a requester wants to change the old key K to a new
|
|
* key keyNew on a remote entity, the 'random' component is
|
|
* obtained from either a true random generator, or from a
|
|
* pseudorandom generator, ...
|
|
*/
|
|
#if defined(NETSNMP_ENABLE_TESTING_CODE) && defined(RANDOMZEROS)
|
|
memset(randp, 0, rand_len);
|
|
DEBUGMSG(("encode_keychange",
|
|
"** Using all zero bits for \"random\" delta of )"
|
|
"the keychange string! **\n"));
|
|
#else /* !NETSNMP_ENABLE_TESTING_CODE */
|
|
rval = sc_random(randp, &rand_len);
|
|
QUITFUN(rval, encode_keychange_quit);
|
|
if (rand_len != oldkey_len) {
|
|
QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
|
|
}
|
|
#endif /* !NETSNMP_ENABLE_TESTING_CODE */
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("encode_keychange") {
|
|
int i;
|
|
DEBUGMSG(("encode_keychange",
|
|
"rand: key=0x"));
|
|
for (i = 0; i < rand_len; i++)
|
|
DEBUGMSG(("encode_keychange", "%02x", randp[i] & 0xff));
|
|
DEBUGMSG(("encode_keychange", " (%ld)\n", rand_len));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
/*
|
|
* ... and the 'delta' component is
|
|
* computed as follows:
|
|
*
|
|
* - a temporary variable is initialized to the existing value
|
|
* of K;
|
|
*/
|
|
memcpy(tmpbuf, oldkey, oldkey_len);
|
|
tmp_len = oldkey_len;
|
|
tmpp = tmpbuf + tmp_len;
|
|
|
|
delta_len = 0;
|
|
while (delta_len < newkey_len) {
|
|
DEBUGMSGTL(("encode_keychange", "%" NETSNMP_PRIz "d < %" NETSNMP_PRIz "d\n", delta_len,
|
|
newkey_len));
|
|
|
|
/*
|
|
* - the random component is appended to the value of the
|
|
* temporary variable,
|
|
*/
|
|
memcpy(tmpp, randp, rand_len);
|
|
tmp_len += rand_len;
|
|
|
|
/*
|
|
* and the result is input to the
|
|
* the hash algorithm H to produce a digest value, and
|
|
* the temporary variable is set to this digest value;
|
|
*/
|
|
digest_len = sizeof(digest);
|
|
rval = sc_hash(hashtype, hashtype_len, tmpbuf, tmp_len,
|
|
digest, &digest_len);
|
|
QUITFUN(rval, encode_keychange_quit);
|
|
DEBUGMSGTL(("encode_keychange", "digest_len %" NETSNMP_PRIz "d\n", digest_len));
|
|
|
|
memcpy(tmpbuf, digest, digest_len);
|
|
tmp_len = digest_len;
|
|
tmpp = tmpbuf;
|
|
/*
|
|
* - the value of the temporary variable is XOR-ed with
|
|
* the first (next) L-octets (16 octets in case of MD5)
|
|
* of the keyNew to produce the first (next) L-octets
|
|
* (16 octets in case of MD5) of the 'delta' component.
|
|
* - the above two steps are repeated until the unused
|
|
* portion of the keyNew component is L octets or less,
|
|
*/
|
|
while ((delta_len < newkey_len) && (digest_len-- > 0)) {
|
|
delta[delta_len] = *tmpp ^ newkey[delta_len];
|
|
DEBUGMSGTL(("encode_keychange",
|
|
"d[%" NETSNMP_PRIz "d] 0x%0x = 0x%0x ^ 0x%0x\n",
|
|
delta_len, delta[delta_len], *tmpp, newkey[delta_len]));
|
|
++tmpp;
|
|
++delta_len;
|
|
}
|
|
DEBUGMSGTL(("encode_keychange", "delta_len %" NETSNMP_PRIz "d\n", delta_len));
|
|
}
|
|
|
|
/** copy results */
|
|
memcpy(kcstring + rand_len, delta, delta_len);
|
|
*kcstring_len = rand_len + delta_len;
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("encode_keychange") {
|
|
int i;
|
|
DEBUGMSG(("encode_keychange",
|
|
"oldkey: key=0x"));
|
|
for (i = 0; i < oldkey_len; i++)
|
|
DEBUGMSG(("encode_keychange", "%02x", oldkey[i] & 0xff));
|
|
DEBUGMSG(("encode_keychange", " (%ld)\n", oldkey_len));
|
|
|
|
DEBUGMSG(("encode_keychange",
|
|
"newkey: key=0x"));
|
|
for (i = 0; i < newkey_len; i++)
|
|
DEBUGMSG(("encode_keychange", "%02x", newkey[i] & 0xff));
|
|
DEBUGMSG(("encode_keychange", " (%ld)\n", newkey_len));
|
|
|
|
DEBUGMSG(("encode_keychange",
|
|
"kcstring: key=0x"));
|
|
for (i = 0; i < *kcstring_len; i++)
|
|
DEBUGMSG(("encode_keychange", "%02x", kcstring[i] & 0xff));
|
|
DEBUGMSG(("encode_keychange", " (%ld)\n", *kcstring_len));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
encode_keychange_quit:
|
|
if (kcstring && rval != SNMPERR_SUCCESS)
|
|
memset(kcstring, 0, *kcstring_len);
|
|
memset(tmpbuf, 0, sizeof(tmpbuf));
|
|
memset(digest, 0, sizeof(digest));
|
|
memset(delta, 0, sizeof(delta));
|
|
|
|
return rval;
|
|
|
|
} /* end encode_keychange() */
|
|
|
|
#else
|
|
_KEYTOOLS_NOT_AVAILABLE
|
|
#endif /* internal or openssl */
|
|
/*******************************************************************-o-******
|
|
* decode_keychange
|
|
*
|
|
* Parameters:
|
|
* *hashtype MIB OID of the hash transform to use.
|
|
* hashtype_len Length of the hash transform MIB OID.
|
|
* *oldkey Old key that is used to encode the new key.
|
|
* oldkey_len Length of oldkey in bytes.
|
|
* *kcstring Encoded KeyString buffer containing the new key.
|
|
* kcstring_len Length of kcstring in bytes.
|
|
* *newkey Buffer to hold the extracted new key.
|
|
* *newkey_len Length of newkey in bytes.
|
|
*
|
|
* Returns:
|
|
* SNMPERR_SUCCESS Success.
|
|
* SNMPERR_GENERR All errors.
|
|
*
|
|
*
|
|
* Decodes a string of bits encoded according to the KeyChange TC described
|
|
* in RFC 2274, Section 5. The new key is extracted from *kcstring with
|
|
* the aid of the old key.
|
|
*
|
|
* Upon successful return, *newkey_len contains the length of the new key.
|
|
*
|
|
*
|
|
* ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer,
|
|
* although this length may not be equal to the hash transform
|
|
* output. Thus the new key length will be equal to the old
|
|
* key length.
|
|
*
|
|
KeyChange ::= TEXTUAL-CONVENTION
|
|
STATUS current
|
|
DESCRIPTION
|
|
"Every definition of an object with this syntax must identify
|
|
a protocol P, a secret key K, and a hash algorithm H
|
|
that produces output of L octets.
|
|
|
|
The object's value is a manager-generated, partially-random
|
|
value which, when modified, causes the value of the secret
|
|
key K, to be modified via a one-way function.
|
|
|
|
The value of an instance of this object is the concatenation
|
|
of two components: first a 'random' component and then a
|
|
'delta' component.
|
|
|
|
The lengths of the random and delta components
|
|
are given by the corresponding value of the protocol P;
|
|
if P requires K to be a fixed length, the length of both the
|
|
random and delta components is that fixed length; if P
|
|
allows the length of K to be variable up to a particular
|
|
maximum length, the length of the random component is that
|
|
maximum length and the length of the delta component is any
|
|
length less than or equal to that maximum length.
|
|
For example, usmHMACMD5AuthProtocol requires K to be a fixed
|
|
length of 16 octets and L - of 16 octets.
|
|
usmHMACSHAAuthProtocol requires K to be a fixed length of
|
|
20 octets and L - of 20 octets. Other protocols may define
|
|
other sizes, as deemed appropriate.
|
|
|
|
When a requester wants to change the old key K to a new
|
|
[... see encode_keychange above ...]
|
|
|
|
At the receiver side, when an instance of this object is set
|
|
to a new value, then a new value of K is computed as follows:
|
|
|
|
- a temporary variable is initialized to the existing value
|
|
of K;
|
|
- if the length of the delta component is greater than L
|
|
octets, then:
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
hash algorithm H to produce a digest value, and the
|
|
temporary variable is set to this digest value;
|
|
- the value of the temporary variable is XOR-ed with
|
|
the first (next) L-octets (16 octets in case of MD5)
|
|
of the delta component to produce the first (next)
|
|
L-octets (16 octets in case of MD5) of the new value
|
|
of K.
|
|
- the above two steps are repeated until the unused
|
|
portion of the delta component is L octets or less,
|
|
- the random component is appended to the value of the
|
|
temporary variable, and the result is input to the
|
|
hash algorithm H to produce a digest value;
|
|
- this digest value, truncated if necessary to be the same
|
|
length as the unused portion of the delta component, is
|
|
XOR-ed with the unused portion of the delta component to
|
|
produce the (final portion of the) new value of K.
|
|
|
|
For example, using MD5 as the hash algorithm H:
|
|
|
|
iterations = (lenOfDelta - 1)/16; -- integer division
|
|
temp = keyOld;
|
|
for (i = 0; i < iterations; i++) {
|
|
temp = MD5 (temp || random);
|
|
keyNew[i*16 .. (i*16)+15] =
|
|
temp XOR delta[i*16 .. (i*16)+15];
|
|
}
|
|
temp = MD5 (temp || random);
|
|
keyNew[i*16 .. lenOfDelta-1] =
|
|
temp XOR delta[i*16 .. lenOfDelta-1];
|
|
|
|
The value of an object with this syntax, whenever it is
|
|
retrieved by the management protocol, is always the zero
|
|
length string.
|
|
|
|
Note that the keyOld and keyNew are the localized keys.
|
|
|
|
Note that it is probably wise that when an SNMP entity sends
|
|
a SetRequest to change a key, that it keeps a copy of the old
|
|
key until it has confirmed that the key change actually
|
|
succeeded.
|
|
"
|
|
SYNTAX OCTET STRING
|
|
*/
|
|
/*
|
|
* XXX: if the newkey is not long enough, it should be freed and remalloced
|
|
*/
|
|
int
|
|
decode_keychange(const oid * hashtype, u_int hashtype_len,
|
|
u_char * oldkey, size_t oldkey_len,
|
|
u_char * kcstring, size_t kcstring_len,
|
|
u_char * newkey, size_t * newkey_len)
|
|
#if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
|
|
{
|
|
int rval = SNMPERR_SUCCESS, auth_type;
|
|
size_t hash_len = 0, key_len = 0;
|
|
int ihash_len = 0;
|
|
u_int nbytes = 0;
|
|
|
|
u_char *deltap, hash[SNMP_MAXBUF];
|
|
size_t delta_len, tmpbuf_len;
|
|
u_char *tmpbuf = NULL;
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
u_char *newkey_save = newkey;
|
|
#endif
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if (!hashtype || !oldkey || !kcstring || !newkey || !newkey_len
|
|
|| (oldkey_len == 0) || (kcstring_len == 0) || (*newkey_len == 0)) {
|
|
DEBUGMSGTL(("decode_keychange", "bad args\n"));
|
|
QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
|
|
}
|
|
|
|
/*
|
|
* Setup for the transform type.
|
|
*/
|
|
auth_type = sc_get_authtype(hashtype, hashtype_len);
|
|
ihash_len = sc_get_proper_auth_length_bytype(auth_type);
|
|
if (ihash_len == SNMPERR_GENERR) {
|
|
DEBUGMSGTL(("decode_keychange", "proper length err\n"));
|
|
QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
|
|
}
|
|
hash_len = (size_t) ihash_len;
|
|
DEBUGMSGTL(("decode_keychange",
|
|
"oldkey_len %" NETSNMP_PRIz "d, newkey_len %" NETSNMP_PRIz "d, kcstring_len %" NETSNMP_PRIz "d, hash_len %" NETSNMP_PRIz "d\n",
|
|
oldkey_len, *newkey_len, kcstring_len, hash_len));
|
|
|
|
if (((oldkey_len * 2) != kcstring_len) || (*newkey_len < oldkey_len)) {
|
|
DEBUGMSGTL(("decode_keychange", "keylen error\n"));
|
|
QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
|
|
}
|
|
|
|
/*********** handle hash len > keylen ******************/
|
|
|
|
key_len = oldkey_len;
|
|
*newkey_len = oldkey_len;
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("decode_keychange") {
|
|
int i;
|
|
DEBUGMSG(("decode_keychange",
|
|
"oldkey: key=0x"));
|
|
for (i = 0; i < oldkey_len; i++)
|
|
DEBUGMSG(("decode_keychange", "%02x", oldkey[i] & 0xff));
|
|
DEBUGMSG(("decode_keychange", " (%ld)\n", oldkey_len));
|
|
|
|
DEBUGMSG(("decode_keychange",
|
|
"kcstring: key=0x"));
|
|
for (i = 0; i < kcstring_len; i++)
|
|
DEBUGMSG(("decode_keychange", "%02x", kcstring[i] & 0xff));
|
|
DEBUGMSG(("decode_keychange", " (%ld)\n", kcstring_len));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
/*
|
|
* KeyChange ::= TEXTUAL-CONVENTION
|
|
* STATUS current
|
|
* DESCRIPTION
|
|
* [...]
|
|
* At the receiver side, when an instance of this object is set
|
|
* to a new value, then a new value of K is computed as follows:
|
|
*
|
|
* - a temporary variable is initialized to the existing value
|
|
* of K;
|
|
*/
|
|
tmpbuf = (u_char *) malloc(key_len * 2);
|
|
if (NULL == tmpbuf) {
|
|
DEBUGMSGTL(("decode_keychange", "malloc failed\n"));
|
|
QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
|
|
}
|
|
memcpy(tmpbuf, oldkey, key_len);
|
|
tmpbuf_len = key_len;
|
|
|
|
/*
|
|
* key=0xe27c077c47d4cb4e4473aeac969ba9fa622486e0|d7440406892e0941175cb5ee
|
|
* key=0xe27c077c47d4cb4e4473aeac969ba9fa622486e0|4fa8e081a6cb2f089f40949c
|
|
*/
|
|
delta_len = 0;
|
|
deltap = kcstring + key_len;
|
|
while (delta_len < key_len) {
|
|
|
|
/*
|
|
* - if the length of the delta component is greater than L
|
|
* octets, then:
|
|
* - the random component is appended to the value of the
|
|
* temporary variable, ...
|
|
*/
|
|
DEBUGMSGTL(("decode_keychange",
|
|
"append random tmpbuf_len %" NETSNMP_PRIz "d key_len %" NETSNMP_PRIz "d\n",
|
|
tmpbuf_len, key_len));
|
|
memcpy(tmpbuf + tmpbuf_len, kcstring, key_len);
|
|
tmpbuf_len += key_len;
|
|
|
|
/*
|
|
* ... and the result is input to the
|
|
* hash algorithm H to produce a digest value, ...
|
|
*/
|
|
hash_len = sizeof(hash);
|
|
DEBUGMSGTL(("decode_keychange", "get hash\n"));
|
|
rval = sc_hash(hashtype, hashtype_len, tmpbuf, tmpbuf_len,
|
|
hash, &hash_len);
|
|
QUITFUN(rval, decode_keychange_quit);
|
|
if (hash_len > key_len) {
|
|
DEBUGMSGTL(("decode_keychange",
|
|
"truncating hash to key_len\n"));
|
|
hash_len = key_len;
|
|
}
|
|
|
|
/*
|
|
* ... and the
|
|
* temporary variable is set to this digest value;
|
|
*/
|
|
DEBUGMSGTL(("decode_keychange", "copy %" NETSNMP_PRIz "d hash bytes to tmp\n",
|
|
hash_len));
|
|
memcpy(tmpbuf, hash, hash_len);
|
|
tmpbuf_len = hash_len;
|
|
|
|
/*
|
|
* - the value of the temporary variable is XOR-ed with
|
|
* the first (next) L-octets (16 octets in case of MD5)
|
|
* of the delta component to produce the first (next)
|
|
* L-octets (16 octets in case of MD5) of the new value
|
|
* of K.
|
|
*/
|
|
DEBUGMSGTL(("decode_keychange",
|
|
"xor to get new key; hash_len %" NETSNMP_PRIz "d delta_len %" NETSNMP_PRIz "d\n",
|
|
hash_len, delta_len));
|
|
nbytes = 0;
|
|
while ((nbytes < hash_len) && (delta_len < key_len)) {
|
|
newkey[delta_len] = tmpbuf[nbytes++] ^ deltap[delta_len];
|
|
++delta_len;
|
|
}
|
|
}
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
DEBUGIF("decode_keychange") {
|
|
int i;
|
|
DEBUGMSG(("decode_keychange",
|
|
"newkey: key=0x"));
|
|
for (i = 0; i < *newkey_len; i++)
|
|
DEBUGMSG(("decode_keychange", "%02x", newkey_save[i] & 0xff));
|
|
DEBUGMSG(("decode_keychange", " (%ld)\n", *newkey_len));
|
|
}
|
|
#endif /* NETSNMP_ENABLE_TESTING_CODE */
|
|
|
|
decode_keychange_quit:
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
DEBUGMSGTL(("decode_keychange", "error %d\n", rval));
|
|
if (newkey)
|
|
memset(newkey, 0, key_len);
|
|
}
|
|
memset(hash, 0, SNMP_MAXBUF);
|
|
SNMP_FREE(tmpbuf);
|
|
|
|
return rval;
|
|
|
|
} /* end decode_keychange() */
|
|
|
|
#else
|
|
_KEYTOOLS_NOT_AVAILABLE
|
|
#endif /* internal or openssl */
|
|
#endif /* NETSNMP_FEATURE_REMOVE_USM_KEYTOOLS */
|