diff options
Diffstat (limited to 'lib/lanplus/lanplus_crypt.c')
-rw-r--r-- | lib/lanplus/lanplus_crypt.c | 1038 |
1 files changed, 1038 insertions, 0 deletions
diff --git a/lib/lanplus/lanplus_crypt.c b/lib/lanplus/lanplus_crypt.c new file mode 100644 index 0000000..7f3095e --- /dev/null +++ b/lib/lanplus/lanplus_crypt.c @@ -0,0 +1,1038 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <assert.h> +#include <string.h> +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif +#include <ipmitool/bswap.h> +#include <ipmitool/log.h> +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_crypt_impl.h" + +/* + * lanplus_rakp2_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 2 message + * + * The HMAC was generated [per RFC2404] from : + * + * SIDm - Remote console session ID + * SIDc - BMC session ID + * Rm - Remote console random number + * Rc - BMC random number + * GUIDc - BMC guid + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <UNAMEm> - Username (absent for null user names) + * + * generated by using Kuid. I am aware that the subscripts on the values + * look backwards, but that's the way they are written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 0 on success (the authcode matches) + * 1 on failure (the authcode does not match) + */ +int lanplus_rakp2_hmac_matches(const struct ipmi_session * session, + const uint8_t * bmc_mac, + struct ipmi_intf * intf) +{ + uint8_t * buffer; + int bufferLength, i; + uint8_t mac[EVP_MAX_MD_SIZE]; + uint32_t macLength; + + uint32_t SIDm_lsbf, SIDc_lsbf; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other algorithms (was assert) */ + if ((session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA1) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_MD5) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA256)) { + printf("Error, unsupported rakp2 auth alg %d\n", + session->v2_data.auth_alg); + return 1; + } + + bufferLength = + 4 + /* SIDm */ + 4 + /* SIDc */ + 16 + /* Rm */ + 16 + /* Rc */ + 16 + /* GUIDc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + (int)strlen((const char *)session->username); /* optional */ + + buffer = malloc(bufferLength); + if (buffer == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + + memcpy(buffer, &SIDm_lsbf, 4); + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 4, &SIDc_lsbf, 4); + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[i]; + #endif + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[i]; + #endif + + /* ROLEm */ + buffer[56] = session->v2_data.requested_role; + + /* ULENGTHm */ + buffer[57] = (uint8_t)strlen((const char *)session->username); + + /* UserName [optional] */ + for (i = 0; i < buffer[57]; ++i) + buffer[58 + i] = session->username[i]; + + if (verbose > 2) + { + lprintf(LOG_DEBUG,"rakp2 mac input buffer (%d bytes)", bufferLength); + } + + /* + * The buffer is complete. Let's hash. + */ + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + IPMI_AUTHCODE_BUFFER_SIZE, + buffer, + bufferLength, + mac, + &macLength); + + free(buffer); + + + if (verbose > 2) + { + printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console"); + } + + return (memcmp(bmc_mac, mac, macLength) == 0); +} + + + +/* + * lanplus_rakp4_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 4 message + * + * The HMAC was generated [per RFC2404] from : + * + * Rm - Remote console random number + * SIDc - BMC session ID + * GUIDc - BMC guid + * + * generated by using SIK (the session integrity key). I am aware that the + * subscripts on the values look backwards, but that's the way they are + * written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 1 on success (the authcode matches) + * 0 on failure (the authcode does not match) + * + */ +int lanplus_rakp4_hmac_matches(const struct ipmi_session * session, + const uint8_t * bmc_mac, + struct ipmi_intf * intf) +{ + uint8_t * buffer; + int bufferLength, i; + uint8_t mac[EVP_MAX_MD_SIZE]; + uint32_t macLength; + uint32_t cmpLength; + uint32_t SIDc_lsbf; + int unsupported = 0; + uint8_t alg; + + if (ipmi_oem_active(intf, "intelplus")){ + /* Intel BMC responds with the integrity Algorithm in RAKP4 */ + if (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE) + return 1; + + /* (Old) Intel BMC doesn't support other algorithms */ + if ((session->v2_data.integrity_alg != IPMI_INTEGRITY_HMAC_SHA1_96) && + (session->v2_data.integrity_alg != IPMI_INTEGRITY_HMAC_MD5_128)) { + printf("Error, unsupported rakp4 integrity_alg %d\n", + session->v2_data.integrity_alg); + return 1; + } + } else { + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other algorithms (was assert) */ + if ((session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA1) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_MD5) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA256)) { + printf("Error, unsupported rakp4 auth alg %d\n", + session->v2_data.auth_alg); + return 1; + } + } + + bufferLength = + 16 + /* Rm */ + 4 + /* SIDc */ + 16; /* GUIDc */ + + buffer = (uint8_t *)malloc(bufferLength); + if (buffer == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF + * representation of the multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 16, &SIDc_lsbf, 4); + + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[i]; + #endif + + + if (verbose > 2) + { + printbuf((const uint8_t *)buffer, bufferLength, ">> rakp4 mac input buffer"); + printbuf(session->v2_data.sik, session->v2_data.sik_len, ">> rakp4 mac key (sik)"); + } + + + /* + * The buffer is complete. Let's hash. + */ + alg = ( (ipmi_oem_active(intf, "intelplus")) + ? session->v2_data.integrity_alg + : session->v2_data.auth_alg ); + lanplus_HMAC(alg, + session->v2_data.sik, + session->v2_data.sik_len, + buffer, + bufferLength, + mac, + &macLength); + + if (verbose > 2) + { + printbuf(bmc_mac, macLength, ">> rakp4 mac as computed by the BMC"); + printbuf(mac, macLength, ">> rakp4 mac as computed by the remote console"); + } + + if (ipmi_oem_active(intf, "intelplus")){ + /* Intel BMC responds with the integrity Algorithm in RAKP4 */ + switch(session->v2_data.integrity_alg) + { + case IPMI_INTEGRITY_HMAC_SHA1_96: + if (macLength != SHA_DIGEST_LENGTH) unsupported = 1; + cmpLength = IPMI_SHA1_AUTHCODE_SIZE; + break; + case IPMI_INTEGRITY_HMAC_MD5_128: + if (macLength != MD5_DIGEST_LENGTH) unsupported = 1; + cmpLength = IPMI_HMAC_MD5_AUTHCODE_SIZE; + break; + default: + unsupported = 1; + break; + } + + } else { + + /* We don't yet support other algorithms (was assert) */ + switch(session->v2_data.auth_alg) + { + case IPMI_AUTH_RAKP_HMAC_SHA1: + if (macLength != SHA_DIGEST_LENGTH) unsupported = 1; + cmpLength = IPMI_SHA1_AUTHCODE_SIZE; + break; + case IPMI_AUTH_RAKP_HMAC_MD5 : + if (macLength != MD5_DIGEST_LENGTH) unsupported = 1; + cmpLength = IPMI_HMAC_MD5_AUTHCODE_SIZE; + break; + case IPMI_AUTH_RAKP_HMAC_SHA256: + if (macLength != SHA256_DIGEST_LENGTH) unsupported = 1; + cmpLength = IPMI_HMAC_SHA256_AUTHCODE_SIZE; + break; + default: + unsupported = 1; + break; + } + } + if (unsupported) { + printf("Unsupported rakp4 macLength %d for auth %d\n", + macLength, session->v2_data.auth_alg); + return 1; + } + + free(buffer); + return (memcmp(bmc_mac, mac, cmpLength) == 0); +} + + + +/* + * lanplus_generate_rakp3_auth_code + * + * This auth code is an HMAC generated with : + * + * Rc - BMC random number + * SIDm - Console session ID + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <USERNAME> - Usename (absent for null usernames) + * + * The key used to generated the MAC is Kuid + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param output_buffer [out] will hold the generated MAC + * param session [in] holds all the state data we need to generate the auth code + * param mac_length [out] will be set to the length of the auth code + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_rakp3_authcode(uint8_t * output_buffer, + const struct ipmi_session * session, + uint32_t * mac_length, + struct ipmi_intf * intf) +{ + int ret = 0; + int input_buffer_length, i; + uint8_t * input_buffer; + uint32_t SIDm_lsbf; + + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + { + *mac_length = 0; + return 0; + } + + /* We don't yet support other algorithms (was assert) */ + if ((session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA1) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_MD5) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA256)) { + printf("Error, unsupported rakp3 auth alg %d\n", + session->v2_data.auth_alg); + return 1; + } + + input_buffer_length = + 16 + /* Rc */ + 4 + /* SIDm */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + (int)strlen((const char *)session->username); + + input_buffer = malloc(input_buffer_length); + if (input_buffer == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[i]; + #endif + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + memcpy(input_buffer + 16, &SIDm_lsbf, 4); + + /* ROLEm */ + if (ipmi_oem_active(intf, "intelplus")) + input_buffer[20] = session->privlvl; + else + input_buffer[20] = session->v2_data.requested_role; + + /* ULENGTHm */ + input_buffer[21] = (uint8_t)strlen((const char *)session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[21]; ++i) + input_buffer[22 + i] = session->username[i]; + + if (verbose > 2) + { + printbuf((const uint8_t *)input_buffer, input_buffer_length, ">> rakp3 mac input buffer"); + printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp3 mac key"); + } + + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + IPMI_AUTHCODE_BUFFER_SIZE, + input_buffer, + input_buffer_length, + output_buffer, + mac_length); + + if (verbose > 2) + printbuf((const uint8_t *)output_buffer, *mac_length, "generated rakp3 mac"); + + + free(input_buffer); + + return ret; +} + + + +/* + * lanplus_generate_sik + * + * Generate the session integrity key (SIK) used for integrity checking + * during the IPMI v2 / RMCP+ session + * + * This session integrity key is a HMAC generated with : + * + * Rm - Console generated random number + * Rc - BMC generated random number + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <USERNAME> - Usename (absent for null usernames) + * + * The key used to generated the SIK is Kg if Kg is not null (two-key logins are + * enabled). Otherwise Kuid (the user authcode) is used as the key to genereate + * the SIK. + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param session [in/out] contains our input and output fields. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_sik(struct ipmi_session * session) +{ + uint8_t * input_buffer; + int input_buffer_length, i; + uint8_t * input_key; + uint32_t mac_length; + int unsupported = 0; + + memset(session->v2_data.sik, 0, sizeof(session->v2_data.sik)); + session->v2_data.sik_len = 0; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 0; + + /* We don't yet support other algorithms (was assert) */ + if ((session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA1) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_MD5) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_HMAC_SHA256)) { + printf("Error, unsupported sik auth alg %d\n", + session->v2_data.auth_alg); + return 1; + } + + input_buffer_length = + 16 + /* Rm */ + 16 + /* Rc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + (int)strlen((const char *)session->username); + + input_buffer = malloc(input_buffer_length); + if (input_buffer == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* ROLEm */ + input_buffer[32] = session->v2_data.requested_role; + + /* ULENGTHm */ + input_buffer[33] = (uint8_t)strlen((const char *)session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[33]; ++i) + input_buffer[34 + i] = session->username[i]; + + if (session->v2_data.kg[0]) + { + /* We will be hashing with Kg */ + /* + * Section 13.31 of the IPMI v2 spec describes the SIK creation + * using Kg. It specifies that Kg should not be truncated. + * Kg is set in ipmi_intf. + */ + input_key = session->v2_data.kg; + } + else + { + /* We will be hashing with Kuid */ + input_key = session->authcode; + } + + + if (verbose >= 2) + printbuf((const uint8_t *)input_buffer, input_buffer_length, "session integrity key input"); + + lanplus_HMAC(session->v2_data.auth_alg, + input_key, + IPMI_AUTHCODE_BUFFER_SIZE, + input_buffer, + input_buffer_length, + session->v2_data.sik, + &mac_length); + + free(input_buffer); + switch(session->v2_data.auth_alg) + { + case IPMI_AUTH_RAKP_HMAC_SHA1 : + if (mac_length != SHA_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_MD5 : + if (mac_length != MD5_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_SHA256: + if (mac_length != SHA256_DIGEST_LENGTH) unsupported = 1; break; + default : unsupported = 1; break; + } + if (unsupported) { /*was assert*/ + printf("Unsupported sik macLength %d for auth %d\n", + mac_length, session->v2_data.auth_alg); + return 1; + } + + session->v2_data.sik_len = (uint8_t)mac_length; + + /* + * The key MAC generated is 20 bytes, but we will only be using the first + * 12 for SHA1 96 + */ + if (verbose >= 2) + printbuf(session->v2_data.sik, session->v2_data.sik_len, "Generated session integrity key"); + + return 0; +} + + + +/* + * lanplus_generate_k1 + * + * Generate K1, the key presumably used to generate integrity authcodes + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_k1(struct ipmi_session * session) +{ + uint32_t mac_length; + int unsupported = 0; + uint8_t CONST_1[36] = /*EVP_MAX_MD_SIZE = 36*/ + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k1, CONST_1, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + session->v2_data.sik_len,/* SIK length */ + CONST_1, + IPMI_AUTHCODE_BUFFER_SIZE, /*=20*/ + session->v2_data.k1, + &mac_length); + switch(session->v2_data.auth_alg) + { + case IPMI_AUTH_RAKP_HMAC_SHA1 : + if (mac_length != SHA_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_MD5 : + if (mac_length != MD5_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_SHA256: + if (mac_length != SHA256_DIGEST_LENGTH) unsupported = 1; break; + default : unsupported = 1; break; + } + if (unsupported) { /*was assert*/ + printf("Unsupported k1 macLength %d for auth %d\n", + mac_length, session->v2_data.auth_alg); + return 1; + } + session->v2_data.k1_len = (uint8_t)mac_length; + } + + if (verbose >= 2) + printbuf(session->v2_data.k1, session->v2_data.k1_len, "Generated K1"); + + return 0; +} + + + +/* + * lanplus_generate_k2 + * + * Generate K2, the key used for RMCP+ AES encryption. + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_k2(struct ipmi_session * session) +{ + uint32_t mac_length; + int unsupported = 0; + uint8_t CONST_2[36] = /*EVP_MAX_MD_SIZE = 36*/ + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k2, CONST_2, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + session->v2_data.sik_len,/* SIK length */ + CONST_2, + IPMI_AUTHCODE_BUFFER_SIZE, /*=20*/ + session->v2_data.k2, + &mac_length); + switch(session->v2_data.auth_alg) + { + case IPMI_AUTH_RAKP_HMAC_SHA1 : + if (mac_length != SHA_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_MD5 : + if (mac_length != MD5_DIGEST_LENGTH) unsupported = 1; break; + case IPMI_AUTH_RAKP_HMAC_SHA256: + if (mac_length != SHA256_DIGEST_LENGTH) unsupported = 1; break; + default : unsupported = 1; break; + } + if (unsupported) { /*was assert*/ + printf("Unsupported k2 macLength %d for auth %d\n", + mac_length, session->v2_data.auth_alg); + return 1; + } + session->v2_data.k2_len = (uint8_t)mac_length; + } + + if (verbose >= 2) + printbuf(session->v2_data.k2, session->v2_data.k2_len, "Generated K2"); + + return 0; +} + + + +/* + * lanplus_encrypt_payload + * + * Perform the appropriate encryption on the input data. Output the encrypted + * data to output, including the required confidentiality header and trailer. + * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and + * set bytes_written to input_length. + * + * param crypt_alg specifies the encryption algorithm (from table 13-19 of the + * IPMI v2 spec) + * param key is the used as input to the encryption algorithmf + * param input is the input data to be encrypted + * param input_length is the length of the input data to be encrypted + * param output is the cipher text generated by the encryption process + * param bytes_written is the number of bytes written during the encryption + * process + * + * returns 0 on success + * 1 on failure + */ +int lanplus_encrypt_payload(uint8_t crypt_alg, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint16_t * bytes_written) +{ + uint8_t * padded_input; + uint32_t mod, i, bytes_encrypted; + uint8_t pad_length = 0; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + /* Just copy the input to the output */ + *bytes_written = (uint16_t)input_length; + return 0; + } + + /* Currently, we only support AES (was assert) */ + if ((crypt_alg != IPMI_CRYPT_AES_CBC_128) || + (input_length > IPMI_MAX_PAYLOAD_SIZE)) { + lprintf(LOG_ERR,"lanplus crypt: unsupported alg %d or len %d\n", + crypt_alg,input_length); + return 1; + } + + /* + * The input to the AES encryption algorithm has to be a multiple of the + * block size (16 bytes). The extra byte we are adding is the pad length + * byte. + */ + mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE; + if (mod) + pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod; + + padded_input = (uint8_t*)malloc(input_length + pad_length + 1); + if (padded_input == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + memcpy(padded_input, input, input_length); + + /* add the pad */ + for (i = 0; i < pad_length; ++i) + padded_input[input_length + i] = i + 1; + + /* add the pad length */ + padded_input[input_length + pad_length] = pad_length; + + /* Generate an initialization vector, IV, for the encryption process */ + if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE)) + { + lprintf(LOG_ERR, "lanplus_encrypt_payload: Error generating IV"); + free(padded_input); + return 1; + } + + if (verbose > 2) + printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector"); + + + + lanplus_encrypt_aes_cbc_128(output, /* IV */ + key, /* K2 */ + padded_input, /* Data to encrypt */ + input_length + pad_length + 1, /* Input length */ + output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output */ + &bytes_encrypted); /* bytes written */ + + *bytes_written = + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */ + bytes_encrypted; + + free(padded_input); + + return 0; +} + + + +/* + * lanplus_has_valid_auth_code + * + * Determine whether the packets authcode field is valid for packet. + * + * We always return success if any of the following are true. + * - this is not an IPMIv2 packet + * - the session is not yet active + * - the packet specifies that it is not authenticated + * - the integrity algorithm agreed upon during session creation is "none" + * + * The authcode is computed using the specified integrity algorithm starting + * with the AuthType / Format field, and ending with the field immediately + * preceeding the authcode itself. + * + * The key key used to generate the authcode MAC is K1. + * + * param rs holds the response structure. + * param session holds our session state, including our chosen algorithm, key, etc. + * + * returns 1 on success (authcode is valid) + * 0 on failure (autchode integrity check failed) + */ +int lanplus_has_valid_auth_code(struct ipmi_rs * rs, + struct ipmi_session * session) +{ + uint8_t * bmc_authcode; + uint8_t generated_authcode[EVP_MAX_MD_SIZE]; + uint32_t generated_authcode_length; + uint32_t authcode_length; + + + if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) || + (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) || + (! rs->session.bAuthenticated) || + (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)) + return 1; + + switch(session->v2_data.integrity_alg) + { + case IPMI_INTEGRITY_HMAC_SHA1_96 : authcode_length = IPMI_SHA1_AUTHCODE_SIZE; break; + case IPMI_INTEGRITY_HMAC_MD5_128 : authcode_length = IPMI_HMAC_MD5_AUTHCODE_SIZE; break; + case IPMI_INTEGRITY_HMAC_SHA256_128: authcode_length = IPMI_HMAC_SHA256_AUTHCODE_SIZE; break; + /* Unsupported */ + default: printf("Unsupported lanplus auth_code %d\n", + session->v2_data.auth_alg); + return 1; break; + } + + /* + * For SHA1-96, the authcode will be the last 12 bytes in the packet + */ + bmc_authcode = rs->data + (rs->data_len - authcode_length); + + lanplus_HMAC(session->v2_data.integrity_alg, + session->v2_data.k1, + session->v2_data.k1_len, + rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE, + rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - authcode_length, + generated_authcode, + &generated_authcode_length); + + if (verbose > 3) + { + lprintf(LOG_DEBUG+2, "Validating authcode"); + printbuf(session->v2_data.k1, session->v2_data.k1_len, "K1"); + printbuf(rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE, + rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - authcode_length, + "Authcode Input Data"); + printbuf(generated_authcode, authcode_length, "Generated authcode"); + printbuf(bmc_authcode, authcode_length, "Expected authcode"); + } + +// assert(generated_authcode_length == 20); + return (memcmp(bmc_authcode, generated_authcode, authcode_length) == 0); +} + + + +/* + * lanplus_decrypt_payload + * + * + * param input points to the beginning of the payload (which will be the IV if + * we are using AES) + * param payload_size [out] will be set to the size of the payload EXCLUDING + * padding + * + * returns 0 on success (we were able to successfully decrypt the packet) + * 1 on failure (we were unable to successfully decrypt the packet) + */ +int lanplus_decrypt_payload(uint8_t crypt_alg, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint16_t * payload_size) +{ + uint8_t * decrypted_payload; + uint32_t bytes_decrypted; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + /* We are not encrypted. The paylaod size is is everything. */ + *payload_size = (uint16_t)input_length; + memmove(output, input, input_length); + return 0; + } + + /* We only support AES (was assert) */ + if (crypt_alg != IPMI_CRYPT_AES_CBC_128) { + lprintf(LOG_ERR,"lanplus decrypt: unsupported alg %d\n", + crypt_alg); + return 1; + } + + decrypted_payload = (uint8_t*)malloc(input_length); + if (decrypted_payload == NULL) { + lprintf(LOG_ERR, "lanplus: malloc failure"); + return 1; + } + + lanplus_decrypt_aes_cbc_128(input, /* IV */ + key, /* Key */ + input + /* Data to decrypt */ + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, + input_length - /* Input length */ + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, + decrypted_payload, /* output */ + &bytes_decrypted); /* bytes written */ + + if (bytes_decrypted != 0) + { + /* Success */ + uint8_t conf_pad_length; + int i; + + memmove(output, + decrypted_payload, + bytes_decrypted); + + /* + * We have to determine the payload size, by substracting the padding, etc. + * The last byte of the decrypted payload is the confidentiality pad length. + */ + conf_pad_length = decrypted_payload[bytes_decrypted - 1]; + *payload_size = bytes_decrypted - conf_pad_length - 1; + + /* + * Extra test to make sure that the padding looks like it should (should start + * with 0x01, 0x02, 0x03, etc... + */ + for (i = 0; i < conf_pad_length; ++i) + { + if (decrypted_payload[*payload_size + i] == i) + { + lprintf(LOG_ERR, "Malformed payload padding"); + return 1; /* assert(0); */ + } + } + } + else + { + lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes"); + return 1; /* assert(0); */ + } + + free(decrypted_payload); + return (bytes_decrypted == 0); +} |