/*
* 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[IPMI_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 */
assert((session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1)
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_MD5)
#ifdef HAVE_CRYPTO_SHA256
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA256)
#endif /* HAVE_CRYPTO_SHA256 */
);
bufferLength =
4 + /* SIDm */
4 + /* SIDc */
16 + /* Rm */
16 + /* Rc */
16 + /* GUIDc */
1 + /* ROLEm */
1 + /* ULENGTHm */
strlen((const char *)intf->ssn_params.username); /* optional */
buffer = malloc(bufferLength);
if (buffer == NULL) {
lprintf(LOG_ERR, "ipmitool: 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;
if (ipmi_oem_active(intf, "i82571spt")) {
/*
* The HMAC calculation code in the Intel 82571 GbE
* skips this bit! Looks like a GbE bug, but we need
* to work around it here anyway...
*/
buffer[56] &= ~0x10;
}
/* ULENGTHm */
buffer[57] = strlen((const char *)intf->ssn_params.username);
/* UserName [optional] */
for (i = 0; i < buffer[57]; ++i)
buffer[58 + i] = intf->ssn_params.username[i];
if (verbose > 2)
{
printbuf((const uint8_t *)buffer, bufferLength, ">> rakp2 mac input buffer");
printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key");
}
/*
* 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);
buffer = NULL;
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[IPMI_MAX_MD_SIZE];
uint32_t macLength;
uint32_t cmpLength;
uint32_t SIDc_lsbf;
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;
/* We don't yet support other algorithms */
assert((session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96)
|| (session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_MD5_128));
} else {
if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
return 1;
/* We don't yet support other algorithms */
assert((session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1)
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_MD5)
#ifdef HAVE_CRYPTO_SHA256
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA256)
#endif /* HAVE_CRYPTO_SHA256 */
);
}
bufferLength =
16 + /* Rm */
4 + /* SIDc */
16; /* GUIDc */
buffer = (uint8_t *)malloc(bufferLength);
if (buffer == NULL) {
lprintf(LOG_ERR, "ipmitool: 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.
*/
lanplus_HMAC((ipmi_oem_active(intf, "intelplus"))
? session->v2_data.integrity_alg
: session->v2_data.auth_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:
assert(macLength == IPMI_SHA_DIGEST_LENGTH);
cmpLength = IPMI_SHA1_AUTHCODE_SIZE;
break;
case IPMI_INTEGRITY_HMAC_MD5_128:
assert(macLength == IPMI_MD5_DIGEST_LENGTH);
cmpLength = IPMI_HMAC_MD5_AUTHCODE_SIZE;
break;
default:
assert(0);
break;
}
} else {
/* We don't yet support other algorithms */
switch(session->v2_data.auth_alg) {
case IPMI_AUTH_RAKP_HMAC_SHA1:
assert(macLength == IPMI_SHA_DIGEST_LENGTH);
cmpLength = IPMI_SHA1_AUTHCODE_SIZE;
break;
case IPMI_AUTH_RAKP_HMAC_MD5:
assert(macLength == IPMI_MD5_DIGEST_LENGTH);
cmpLength = IPMI_HMAC_MD5_AUTHCODE_SIZE;
break;
#ifdef HAVE_CRYPTO_SHA256
case IPMI_AUTH_RAKP_HMAC_SHA256:
assert(macLength == IPMI_SHA256_DIGEST_LENGTH);
cmpLength = IPMI_HMAC_SHA256_AUTHCODE_SIZE;
break;
#endif /* HAVE_CRYPTO_SHA256 */
default:
assert(0);
break;
}
}
free(buffer);
buffer = NULL;
assert(macLength >= cmpLength);
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 */
assert((session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1)
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_MD5)
#ifdef HAVE_CRYPTO_SHA256
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA256)
#endif /* HAVE_CRYPTO_SHA256 */
);
input_buffer_length =
16 + /* Rc */
4 + /* SIDm */
1 + /* ROLEm */
1 + /* ULENGTHm */
strlen((const char *)intf->ssn_params.username);
input_buffer = malloc(input_buffer_length);
if (input_buffer == NULL) {
lprintf(LOG_ERR, "ipmitool: 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") || ipmi_oem_active(intf, "i82571spt"))
input_buffer[20] = intf->ssn_params.privlvl;
else
input_buffer[20] = session->v2_data.requested_role;
/* ULENGTHm */
input_buffer[21] = strlen((const char *)intf->ssn_params.username);
/* USERNAME */
for (i = 0; i < input_buffer[21]; ++i)
input_buffer[22 + i] = intf->ssn_params.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);
input_buffer = NULL;
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, struct ipmi_intf * intf)
{
uint8_t * input_buffer;
int input_buffer_length, i;
uint8_t * input_key;
uint32_t mac_length;
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 */
assert((session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1)
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_MD5)
#ifdef HAVE_CRYPTO_SHA256
|| (session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA256)
#endif /* HAVE_CRYPTO_SHA256 */
);
input_buffer_length =
16 + /* Rm */
16 + /* Rc */
1 + /* ROLEm */
1 + /* ULENGTHm */
strlen((const char *)intf->ssn_params.username);
input_buffer = malloc(input_buffer_length);
if (input_buffer == NULL) {
lprintf(LOG_ERR, "ipmitool: 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;
if (ipmi_oem_active(intf, "i82571spt")) {
/*
* The HMAC calculation code in the Intel 82571 GbE
* skips this bit! Looks like a GbE bug, but we need
* to work around it here anyway...
*/
input_buffer[32] &= ~0x10;
}
/* ULENGTHm */
input_buffer[33] = strlen((const char *)intf->ssn_params.username);
/* USERNAME */
for (i = 0; i < input_buffer[33]; ++i)
input_buffer[34 + i] = intf->ssn_params.username[i];
if (intf->ssn_params.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 = intf->ssn_params.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);
input_buffer = NULL;
switch (session->v2_data.auth_alg) {
case IPMI_AUTH_RAKP_HMAC_SHA1:
assert(mac_length == IPMI_SHA_DIGEST_LENGTH);
break;
case IPMI_AUTH_RAKP_HMAC_MD5:
assert(mac_length == IPMI_MD5_DIGEST_LENGTH);
break;
#ifdef HAVE_CRYPTO_SHA256
case IPMI_AUTH_RAKP_HMAC_SHA256:
assert(mac_length == IPMI_SHA256_DIGEST_LENGTH);
break;
#endif /* HAVE_CRYPTO_SHA256 */
default:
assert(0);
break;
}
session->v2_data.sik_len = 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;
uint8_t CONST_1[] =
{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,
20,
session->v2_data.k1,
&mac_length);
switch (session->v2_data.auth_alg) {
case IPMI_AUTH_RAKP_HMAC_SHA1:
assert(mac_length == IPMI_SHA_DIGEST_LENGTH);
break;
case IPMI_AUTH_RAKP_HMAC_MD5:
assert(mac_length == IPMI_MD5_DIGEST_LENGTH);
break;
#ifdef HAVE_CRYPTO_SHA256
case IPMI_AUTH_RAKP_HMAC_SHA256:
assert(mac_length == IPMI_SHA256_DIGEST_LENGTH);
break;
#endif /* HAVE_CRYPTO_SHA256 */
default:
assert(0);
break;
}
session->v2_data.k1_len = 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;
uint8_t CONST_2[] =
{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,
20,
session->v2_data.k2,
&mac_length);
switch (session->v2_data.auth_alg) {
case IPMI_AUTH_RAKP_HMAC_SHA1:
assert(mac_length == IPMI_SHA_DIGEST_LENGTH);
break;
case IPMI_AUTH_RAKP_HMAC_MD5:
assert(mac_length == IPMI_MD5_DIGEST_LENGTH);
break;
#ifdef HAVE_CRYPTO_SHA256
case IPMI_AUTH_RAKP_HMAC_SHA256:
assert(mac_length == IPMI_SHA256_DIGEST_LENGTH);
break;
#endif /* HAVE_CRYPTO_SHA256 */
default:
assert(0);
break;
}
session->v2_data.k2_len = 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 = input_length;
return 0;
}
/* Currently, we only support AES */
assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
assert(input_length <= IPMI_MAX_PAYLOAD_SIZE);
/*
* 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, "ipmitool: 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");
if (padded_input != NULL) {
free(padded_input);
padded_input = NULL;
}
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);
padded_input = NULL;
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[IPMI_MAX_MAC_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;
#ifdef HAVE_CRYPTO_SHA256
case IPMI_INTEGRITY_HMAC_SHA256_128:
authcode_length = IPMI_HMAC_SHA256_AUTHCODE_SIZE;
break;
#endif /* HAVE_CRYPTO_SHA256 */
/* Unsupported */
default:
assert(0);
break;
}
/*
* For SHA1-96, the authcode will be the last 12 bytes in the packet
* For SHA256-128 or MD5-128, the authcode will be the last 16 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, generated_authcode_length, "Generated authcode");
printbuf(bmc_authcode, authcode_length, "Expected authcode");
}
assert(generated_authcode_length >= authcode_length);
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 = input_length;
memmove(output, input, input_length);
return 0;
}
/* We only support AES */
assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
decrypted_payload = (uint8_t*)malloc(input_length);
if (decrypted_payload == NULL) {
lprintf(LOG_ERR, "ipmitool: malloc failure");
return 1;
}
lanplus_decrypt_aes_cbc_128(input, /* IV */
key, /* Key */
input +
IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Data to decrypt */
input_length -
IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Input length */
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 + 1))
{
lprintf(LOG_ERR, "Malformed payload padding");
assert(0);
}
}
}
else
{
lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes");
assert(0);
}
free(decrypted_payload);
decrypted_payload = NULL;
return (bytes_decrypted == 0);
}