lanplus: Auto-select 'best' cipher suite available

Based on current crypto alogrithms, one could rank cipher suites along
these lines:

17 > 3 >> all the rest

17 and 3 are the only cipher suites that implement any sort of
confidentiality alogorithm that is secure. In addition, any hmac-md5 or
md5 integrity algorithm used in integrity is weak at best and dangerous
for authentication.

This could possibly be enabled in a simpler mechanism by simply checking
for 17 and then choosing it before falling back to 3, but the way this
is implemented, it makes it easy to change the list of acceptable
algorithms from two to three or more items.

Resolves ipmitool/ipmitool#29

Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
This commit is contained in:
Vernon Mauery 2018-04-09 12:28:57 -07:00 committed by Alexander Amelkin
parent a8862d7508
commit 7772254b62
6 changed files with 297 additions and 161 deletions

View File

@ -37,6 +37,7 @@
# include <config.h> # include <config.h>
#endif #endif
#include <ipmitool/ipmi.h> #include <ipmitool/ipmi.h>
#include <ipmitool/ipmi_intf.h>
#define IPMI_GET_CHANNEL_AUTH_CAP 0x38 #define IPMI_GET_CHANNEL_AUTH_CAP 0x38
@ -77,6 +78,50 @@ struct channel_access_t {
uint8_t user_level_auth; uint8_t user_level_auth;
}; };
/*
* The Cipher Suite Record Format from table 22-18 of the IPMI v2.0 spec
*/
enum cipher_suite_format_tag {
STANDARD_CIPHER_SUITE = 0xc0,
OEM_CIPHER_SUITE = 0xc1,
};
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
struct std_cipher_suite_record_t {
uint8_t start_of_record;
uint8_t cipher_suite_id;
uint8_t auth_alg;
uint8_t integrity_alg;
uint8_t crypt_alg;
} ATTRIBUTE_PACKING;
struct oem_cipher_suite_record_t {
uint8_t start_of_record;
uint8_t cipher_suite_id;
uint8_t iana[3];
uint8_t auth_alg;
uint8_t integrity_alg;
uint8_t crypt_alg;
} ATTRIBUTE_PACKING;
#ifdef HAVE_PRAGMA_PACK
#pragma pack(0)
#endif
#define CIPHER_ALG_MASK 0x3f
#define MAX_CIPHER_SUITE_RECORD_OFFSET 0x40
#define MAX_CIPHER_SUITE_DATA_LEN 0x10
#define LIST_ALGORITHMS_BY_CIPHER_SUITE 0x80
/* Below is the theoretical maximum number of cipher suites that could be
* reported by a BMC. That is with the Get Channel Cipher Suites Command, at 16
* bytes at a time and 0x40 requests, it can report 1024 bytes, which is about
* 204 standard records or 128 OEM records. Really, we probably don't need more
* than about 20, which is the full set of standard records plus a few OEM
* records.
*/
#define MAX_CIPHER_SUITE_COUNT (MAX_CIPHER_SUITE_RECORD_OFFSET * \
MAX_CIPHER_SUITE_DATA_LEN / \
sizeof(struct std_cipher_suite_record_t))
/* /*
* The Get Authentication Capabilities response structure * The Get Authentication Capabilities response structure
* From table 22-15 of the IPMI v2.0 spec * From table 22-15 of the IPMI v2.0 spec
@ -131,6 +176,8 @@ struct get_channel_auth_cap_rsp {
int _ipmi_get_channel_access(struct ipmi_intf *intf, int _ipmi_get_channel_access(struct ipmi_intf *intf,
struct channel_access_t *channel_access, struct channel_access_t *channel_access,
uint8_t get_volatile_settings); uint8_t get_volatile_settings);
int ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
uint8_t channel, struct cipher_suite_info *suites, size_t *count);
int _ipmi_get_channel_info(struct ipmi_intf *intf, int _ipmi_get_channel_info(struct ipmi_intf *intf,
struct channel_info_t *channel_info); struct channel_info_t *channel_info);
int _ipmi_set_channel_access(struct ipmi_intf *intf, int _ipmi_set_channel_access(struct ipmi_intf *intf,

View File

@ -62,13 +62,45 @@ enum LANPLUS_SESSION_STATE {
#define IPMI_SIK_BUFFER_SIZE IPMI_MAX_MD_SIZE #define IPMI_SIK_BUFFER_SIZE IPMI_MAX_MD_SIZE
#define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ #define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */
enum cipher_suite_ids {
IPMI_LANPLUS_CIPHER_SUITE_0 = 0,
IPMI_LANPLUS_CIPHER_SUITE_1 = 1,
IPMI_LANPLUS_CIPHER_SUITE_2 = 2,
IPMI_LANPLUS_CIPHER_SUITE_3 = 3,
IPMI_LANPLUS_CIPHER_SUITE_4 = 4,
IPMI_LANPLUS_CIPHER_SUITE_5 = 5,
IPMI_LANPLUS_CIPHER_SUITE_6 = 6,
IPMI_LANPLUS_CIPHER_SUITE_7 = 7,
IPMI_LANPLUS_CIPHER_SUITE_8 = 8,
IPMI_LANPLUS_CIPHER_SUITE_9 = 9,
IPMI_LANPLUS_CIPHER_SUITE_10 = 10,
IPMI_LANPLUS_CIPHER_SUITE_11 = 11,
IPMI_LANPLUS_CIPHER_SUITE_12 = 12,
IPMI_LANPLUS_CIPHER_SUITE_13 = 13,
IPMI_LANPLUS_CIPHER_SUITE_14 = 14,
#ifdef HAVE_CRYPTO_SHA256
IPMI_LANPLUS_CIPHER_SUITE_15 = 15,
IPMI_LANPLUS_CIPHER_SUITE_16 = 16,
IPMI_LANPLUS_CIPHER_SUITE_17 = 17,
#endif /* HAVE_CRYPTO_SHA256 */
IPMI_LANPLUS_CIPHER_SUITE_RESERVED = 0xff,
};
struct cipher_suite_info {
enum cipher_suite_ids cipher_suite_id;
uint8_t auth_alg;
uint8_t integrity_alg;
uint8_t crypt_alg;
uint32_t iana;
};
struct ipmi_session_params { struct ipmi_session_params {
char * hostname; char * hostname;
uint8_t username[17]; uint8_t username[17];
uint8_t authcode_set[IPMI_AUTHCODE_BUFFER_SIZE + 1]; uint8_t authcode_set[IPMI_AUTHCODE_BUFFER_SIZE + 1];
uint8_t authtype_set; uint8_t authtype_set;
uint8_t privlvl; uint8_t privlvl;
uint8_t cipher_suite_id; enum cipher_suite_ids cipher_suite_id;
char sol_escape_char; char sol_escape_char;
int password; int password;
int port; int port;
@ -217,7 +249,10 @@ void ipmi_intf_session_set_username(struct ipmi_intf * intf, char * username);
void ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password); void ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password);
void ipmi_intf_session_set_privlvl(struct ipmi_intf * intf, uint8_t privlvl); void ipmi_intf_session_set_privlvl(struct ipmi_intf * intf, uint8_t privlvl);
void ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit); void ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit);
void ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, uint8_t cipher_suite_id); #ifdef IPMI_INTF_LANPLUS
void ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf,
enum cipher_suite_ids cipher_suite_id);
#endif /* IPMI_INTF_LANPLUS */
void ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char); void ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char);
void ipmi_intf_session_set_kgkey(struct ipmi_intf *intf, const uint8_t *kgkey); void ipmi_intf_session_set_kgkey(struct ipmi_intf *intf, const uint8_t *kgkey);
void ipmi_intf_session_set_port(struct ipmi_intf * intf, int port); void ipmi_intf_session_set_port(struct ipmi_intf * intf, int port);

View File

@ -342,58 +342,116 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv)
return 0; return 0;
} }
static int static size_t
parse_channel_cipher_suite_data(uint8_t *cipher_suite_data, size_t data_len,
struct cipher_suite_info* suites, size_t nr_suites)
{
size_t count = 0;
size_t offset = 0;
uint32_t iana;
uint8_t auth_alg, integrity_alg, crypt_alg;
uint8_t cipher_suite_id;
memset(suites, 0, sizeof(*suites) * nr_suites);
while (offset < data_len && count < nr_suites) {
auth_alg = IPMI_AUTH_RAKP_NONE;
integrity_alg = IPMI_INTEGRITY_NONE;
crypt_alg = IPMI_CRYPT_NONE;
if (cipher_suite_data[offset] == STANDARD_CIPHER_SUITE) {
struct std_cipher_suite_record_t *record =
(struct std_cipher_suite_record_t*)(&cipher_suite_data[offset]);
/* standard type */
iana = 0;
/* Verify that we have at least a full record left; id + 3 algs */
if ((data_len - offset) < sizeof(*record)) {
lprintf(LOG_INFO, "Incomplete data record in cipher suite data");
break;
}
cipher_suite_id = record->cipher_suite_id;
auth_alg = CIPHER_ALG_MASK & record->auth_alg;
integrity_alg = CIPHER_ALG_MASK & record->integrity_alg;
crypt_alg = CIPHER_ALG_MASK & record->crypt_alg;
offset += sizeof(*record);
} else if (cipher_suite_data[offset] == OEM_CIPHER_SUITE) {
/* OEM record type */
struct oem_cipher_suite_record_t *record =
(struct oem_cipher_suite_record_t*)(&cipher_suite_data[offset]);
/* Verify that we have at least a full record left
* id + iana + 3 algs
*/
if ((data_len - offset) < sizeof(*record)) {
lprintf(LOG_INFO, "Incomplete data record in cipher suite data");
break;
}
cipher_suite_id = record->cipher_suite_id;
/* Grab the IANA */
iana = ipmi24toh(record->iana);
auth_alg = CIPHER_ALG_MASK & record->auth_alg;
integrity_alg = CIPHER_ALG_MASK & record->integrity_alg;
crypt_alg = CIPHER_ALG_MASK & record->crypt_alg;
offset += sizeof(*record);
} else {
lprintf(LOG_INFO, "Bad start of record byte in cipher suite data (offset %d, value %x)", offset, cipher_suite_data[offset]);
break;
}
suites[count].cipher_suite_id = cipher_suite_id;
suites[count].iana = iana;
suites[count].auth_alg = auth_alg;
suites[count].integrity_alg = integrity_alg;
suites[count].crypt_alg = crypt_alg;
count++;
}
return count;
}
int
ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
uint8_t channel) uint8_t channel, struct cipher_suite_info *suites, size_t *count)
{ {
struct ipmi_rs *rsp; struct ipmi_rs *rsp;
struct ipmi_rq req; struct ipmi_rq req;
uint8_t rqdata[3]; uint8_t rqdata[3];
uint32_t iana;
uint8_t auth_alg, integrity_alg, crypt_alg;
uint8_t cipher_suite_id;
uint8_t list_index = 0; uint8_t list_index = 0;
/* 0x40 sets * 16 bytes per set */ /* 0x40 sets * 16 bytes per set */
uint8_t cipher_suite_data[1024]; uint8_t cipher_suite_data[MAX_CIPHER_SUITE_RECORD_OFFSET *
uint16_t offset = 0; MAX_CIPHER_SUITE_DATA_LEN];
/* how much was returned, total */ size_t offset = 0;
uint16_t cipher_suite_data_length = 0; size_t nr_suites = 0;
if (!suites || !count || !*count)
return -1;
nr_suites = *count;
*count = 0;
memset(cipher_suite_data, 0, sizeof(cipher_suite_data)); memset(cipher_suite_data, 0, sizeof(cipher_suite_data));
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_APP; req.msg.netfn = IPMI_NETFN_APP;
req.msg.cmd = IPMI_GET_CHANNEL_CIPHER_SUITES; req.msg.cmd = IPMI_GET_CHANNEL_CIPHER_SUITES;
req.msg.data = rqdata; req.msg.data = rqdata;
req.msg.data_len = 3; req.msg.data_len = sizeof(rqdata);
rqdata[0] = channel; rqdata[0] = channel;
rqdata[1] = ((strncmp(payload_type, "ipmi", 4) == 0)? 0: 1); rqdata[1] = ((strncmp(payload_type, "ipmi", 4) == 0)? 0: 1);
/* Always ask for cipher suite format */
rqdata[2] = 0x80;
do {
/* Always ask for cipher suite format */
rqdata[2] = LIST_ALGORITHMS_BY_CIPHER_SUITE | list_index;
rsp = intf->sendrecv(intf, &req); rsp = intf->sendrecv(intf, &req);
if (!rsp) { if (!rsp) {
lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites");
return -1; return -1;
} }
if (rsp->ccode) { if (rsp->ccode || rsp->data_len < 1) {
lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s", lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s",
val2str(rsp->ccode, completion_code_vals)); val2str(rsp->ccode, completion_code_vals));
return -1; return -1;
} }
/*
* Grab the returned channel number once. We assume it's the same
* in future calls.
*/
if (rsp->data_len >= 1) {
channel = rsp->data[0];
}
while ((rsp->data_len > 1) && (rsp->data_len == 17) && (list_index < 0x3F)) {
/* /*
* We got back cipher suite data -- store it. * We got back cipher suite data -- store it.
* printf("copying data to offset %d\n", offset); * printf("copying data to offset %d\n", offset);
@ -406,111 +464,41 @@ ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
* Increment our list for the next call * Increment our list for the next call
*/ */
++list_index; ++list_index;
rqdata[2] = (rqdata[2] & 0x80) + list_index; } while ((rsp->data_len == (sizeof(uint8_t) + MAX_CIPHER_SUITE_DATA_LEN)) &&
(list_index < MAX_CIPHER_SUITE_RECORD_OFFSET));
rsp = intf->sendrecv(intf, &req); *count = parse_channel_cipher_suite_data(cipher_suite_data, offset, suites,
if (!rsp) { nr_suites);
lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); return 0;
return -1; }
}
if (rsp->ccode) {
lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s",
val2str(rsp->ccode, completion_code_vals));
return -1;
}
}
/* Copy last chunk */ static int
if(rsp->data_len > 1) { ipmi_print_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
/* uint8_t channel)
* We got back cipher suite data -- store it. {
* printf("copying data to offset %d\n", offset); int rc;
* printbuf(rsp->data + 1, rsp->data_len - 1, "this is the data"); size_t i = 0;
*/ struct cipher_suite_info suites[MAX_CIPHER_SUITE_COUNT];
memcpy(cipher_suite_data + offset, rsp->data + 1, rsp->data_len - 1); size_t nr_suites = sizeof(*suites);
offset += rsp->data_len - 1;
}
/* We can chomp on all our data now. */ rc = ipmi_get_channel_cipher_suites(intf, payload_type, channel,
cipher_suite_data_length = offset; suites, &nr_suites);
offset = 0;
if (rc < 0)
return rc;
if (! csv_output) { if (! csv_output) {
printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n"); printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n");
} }
while (offset < cipher_suite_data_length) { for (i = 0; i < nr_suites; i++) {
if (cipher_suite_data[offset++] == 0xC0) {
/* standard type */
iana = 0;
/* Verify that we have at least a full record left; id + 3 algs */
if ((cipher_suite_data_length - offset) < 4) {
lprintf(LOG_ERR, "Incomplete data record in cipher suite data");
return -1;
}
cipher_suite_id = cipher_suite_data[offset++];
} else if (cipher_suite_data[offset++] == 0xC1) {
/* OEM record type */
/* Verify that we have at least a full record left
* id + iana + 3 algs
*/
if ((cipher_suite_data_length - offset) < 4) {
lprintf(LOG_ERR, "Incomplete data record in cipher suite data");
return -1;
}
cipher_suite_id = cipher_suite_data[offset++];
/* Grab the IANA */
iana =
cipher_suite_data[offset] |
(cipher_suite_data[offset + 1] << 8) |
(cipher_suite_data[offset + 2] << 16);
offset += 3;
} else {
lprintf(LOG_ERR, "Bad start of record byte in cipher suite data");
return -1;
}
/*
* Grab the algorithms for this cipher suite. I guess we can't be
* sure of what order they'll come in. Also, I suppose we default
* to the NONE algorithm if one were absent. This part of the spec is
* poorly written -- I have read the errata document. For now, I'm only
* allowing one algorithm per type (auth, integrity, crypt) because I
* don't I understand how it could be otherwise.
*/
auth_alg = IPMI_AUTH_RAKP_NONE;
integrity_alg = IPMI_INTEGRITY_NONE;
crypt_alg = IPMI_CRYPT_NONE;
while (((cipher_suite_data[offset] & 0xC0) != 0xC0) &&
((cipher_suite_data_length - offset) > 0))
{
switch (cipher_suite_data[offset] & 0xC0)
{
case 0x00:
/* Authentication algorithm specifier */
auth_alg = cipher_suite_data[offset++] & 0x3F;
break;
case 0x40:
/* Interity algorithm specifier */
integrity_alg = cipher_suite_data[offset++] & 0x3F;
break;
case 0x80:
/* Confidentiality algorithm specifier */
crypt_alg = cipher_suite_data[offset++] & 0x3F;
break;
}
}
/* We have everything we need to spit out a cipher suite record */ /* We have everything we need to spit out a cipher suite record */
printf((csv_output? "%d,%s,%s,%s,%s\n" : printf((csv_output? "%d,%s,%s,%s,%s\n" :
"%-4d %-7s %-15s %-15s %-15s\n"), "%-4d %-7s %-15s %-15s %-15s\n"),
cipher_suite_id, suites[i].cipher_suite_id,
iana_string(iana), iana_string(suites[i].iana),
val2str(auth_alg, ipmi_auth_algorithms), val2str(suites[i].auth_alg, ipmi_auth_algorithms),
val2str(integrity_alg, ipmi_integrity_algorithms), val2str(suites[i].integrity_alg, ipmi_integrity_algorithms),
val2str(crypt_alg, ipmi_encryption_algorithms)); val2str(suites[i].crypt_alg, ipmi_encryption_algorithms));
} }
return 0; return 0;
} }
@ -888,7 +876,7 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv)
return (-1); return (-1);
} }
} }
retval = ipmi_get_channel_cipher_suites(intf, retval = ipmi_print_channel_cipher_suites(intf,
argv[1], /* ipmi | sol */ argv[1], /* ipmi | sol */
channel); channel);
} else { } else {

View File

@ -342,7 +342,10 @@ ipmi_main(int argc, char ** argv,
char * seloem = NULL; char * seloem = NULL;
int port = 0; int port = 0;
int devnum = 0; int devnum = 0;
int cipher_suite_id = 3; /* See table 22-19 of the IPMIv2 spec */ #ifdef IPMI_INTF_LANPLUS
/* lookup best cipher suite available */
enum cipher_suite_ids cipher_suite_id = IPMI_LANPLUS_CIPHER_SUITE_RESERVED;
#endif /* IPMI_INTF_LANPLUS */
int argflag, i, found; int argflag, i, found;
int rc = -1; int rc = -1;
int ai_family = AF_UNSPEC; int ai_family = AF_UNSPEC;
@ -420,6 +423,7 @@ ipmi_main(int argc, char ** argv,
goto out_free; goto out_free;
} }
break; break;
#ifdef IPMI_INTF_LANPLUS
case 'C': case 'C':
if (str2int(optarg, &cipher_suite_id) != 0) { if (str2int(optarg, &cipher_suite_id) != 0) {
lprintf(LOG_ERR, "Invalid parameter given or out of range for '-C'."); lprintf(LOG_ERR, "Invalid parameter given or out of range for '-C'.");
@ -433,6 +437,7 @@ ipmi_main(int argc, char ** argv,
goto out_free; goto out_free;
} }
break; break;
#endif /* IPMI_INTF_LANPLUS */
case 'v': case 'v':
verbose++; verbose++;
break; break;
@ -868,7 +873,9 @@ ipmi_main(int argc, char ** argv,
ipmi_intf_session_set_lookupbit(ipmi_main_intf, lookupbit); ipmi_intf_session_set_lookupbit(ipmi_main_intf, lookupbit);
ipmi_intf_session_set_sol_escape_char(ipmi_main_intf, sol_escape_char); ipmi_intf_session_set_sol_escape_char(ipmi_main_intf, sol_escape_char);
#ifdef IPMI_INTF_LANPLUS
ipmi_intf_session_set_cipher_suite_id(ipmi_main_intf, cipher_suite_id); ipmi_intf_session_set_cipher_suite_id(ipmi_main_intf, cipher_suite_id);
#endif /* IPMI_INTF_LANPLUS */
ipmi_main_intf->devnum = devnum; ipmi_main_intf->devnum = devnum;

View File

@ -249,11 +249,14 @@ ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit)
intf->ssn_params.lookupbit = lookupbit; intf->ssn_params.lookupbit = lookupbit;
} }
#ifdef IPMI_INTF_LANPLUS
void void
ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, uint8_t cipher_suite_id) ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf,
enum cipher_suite_ids cipher_suite_id)
{ {
intf->ssn_params.cipher_suite_id = cipher_suite_id; intf->ssn_params.cipher_suite_id = cipher_suite_id;
} }
#endif /* IPMI_INTF_LANPLUS */
void void
ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char) ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char)

View File

@ -163,114 +163,109 @@ extern int verbose;
* returns 0 on success * returns 0 on success
* 1 on failure * 1 on failure
*/ */
int lanplus_get_requested_ciphers(int cipher_suite_id, int lanplus_get_requested_ciphers(enum cipher_suite_ids cipher_suite_id,
uint8_t * auth_alg, uint8_t * auth_alg,
uint8_t * integrity_alg, uint8_t * integrity_alg,
uint8_t * crypt_alg) uint8_t * crypt_alg)
{ {
#ifdef HAVE_CRYPTO_SHA256
if ((cipher_suite_id < 0) || (cipher_suite_id > 17)) {
return 1;
}
#else
if ((cipher_suite_id < 0) || (cipher_suite_id > 14))
return 1;
#endif /* HAVE_CRYPTO_SHA256 */
/* See table 22-19 for the source of the statement */ /* See table 22-19 for the source of the statement */
switch (cipher_suite_id) switch (cipher_suite_id)
{ {
case 0: case IPMI_LANPLUS_CIPHER_SUITE_0:
*auth_alg = IPMI_AUTH_RAKP_NONE; *auth_alg = IPMI_AUTH_RAKP_NONE;
*integrity_alg = IPMI_INTEGRITY_NONE; *integrity_alg = IPMI_INTEGRITY_NONE;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 1: case IPMI_LANPLUS_CIPHER_SUITE_1:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
*integrity_alg = IPMI_INTEGRITY_NONE; *integrity_alg = IPMI_INTEGRITY_NONE;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 2: case IPMI_LANPLUS_CIPHER_SUITE_2:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 3: case IPMI_LANPLUS_CIPHER_SUITE_3:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
*crypt_alg = IPMI_CRYPT_AES_CBC_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128;
break; break;
case 4: case IPMI_LANPLUS_CIPHER_SUITE_4:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
*crypt_alg = IPMI_CRYPT_XRC4_128; *crypt_alg = IPMI_CRYPT_XRC4_128;
break; break;
case 5: case IPMI_LANPLUS_CIPHER_SUITE_5:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
*crypt_alg = IPMI_CRYPT_XRC4_40; *crypt_alg = IPMI_CRYPT_XRC4_40;
break; break;
case 6: case IPMI_LANPLUS_CIPHER_SUITE_6:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_NONE; *integrity_alg = IPMI_INTEGRITY_NONE;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 7: case IPMI_LANPLUS_CIPHER_SUITE_7:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 8: case IPMI_LANPLUS_CIPHER_SUITE_8:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
*crypt_alg = IPMI_CRYPT_AES_CBC_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128;
break; break;
case 9: case IPMI_LANPLUS_CIPHER_SUITE_9:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
*crypt_alg = IPMI_CRYPT_XRC4_128; *crypt_alg = IPMI_CRYPT_XRC4_128;
break; break;
case 10: case IPMI_LANPLUS_CIPHER_SUITE_10:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
*crypt_alg = IPMI_CRYPT_XRC4_40; *crypt_alg = IPMI_CRYPT_XRC4_40;
break; break;
case 11: case IPMI_LANPLUS_CIPHER_SUITE_11:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_MD5_128; *integrity_alg = IPMI_INTEGRITY_MD5_128;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 12: case IPMI_LANPLUS_CIPHER_SUITE_12:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_MD5_128; *integrity_alg = IPMI_INTEGRITY_MD5_128;
*crypt_alg = IPMI_CRYPT_AES_CBC_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128;
break; break;
case 13: case IPMI_LANPLUS_CIPHER_SUITE_13:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_MD5_128; *integrity_alg = IPMI_INTEGRITY_MD5_128;
*crypt_alg = IPMI_CRYPT_XRC4_128; *crypt_alg = IPMI_CRYPT_XRC4_128;
break; break;
case 14: case IPMI_LANPLUS_CIPHER_SUITE_14:
*auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
*integrity_alg = IPMI_INTEGRITY_MD5_128; *integrity_alg = IPMI_INTEGRITY_MD5_128;
*crypt_alg = IPMI_CRYPT_XRC4_40; *crypt_alg = IPMI_CRYPT_XRC4_40;
break; break;
#ifdef HAVE_CRYPTO_SHA256 #ifdef HAVE_CRYPTO_SHA256
case 15: case IPMI_LANPLUS_CIPHER_SUITE_15:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256;
*integrity_alg = IPMI_INTEGRITY_NONE; *integrity_alg = IPMI_INTEGRITY_NONE;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 16: case IPMI_LANPLUS_CIPHER_SUITE_16:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128;
*crypt_alg = IPMI_CRYPT_NONE; *crypt_alg = IPMI_CRYPT_NONE;
break; break;
case 17: case IPMI_LANPLUS_CIPHER_SUITE_17:
*auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256;
*integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128;
*crypt_alg = IPMI_CRYPT_AES_CBC_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128;
break; break;
#endif /* HAVE_CRYPTO_SHA256 */ #endif /* HAVE_CRYPTO_SHA256 */
case IPMI_LANPLUS_CIPHER_SUITE_RESERVED:
default:
return 1;
} }
return 0; return 0;
@ -3381,6 +3376,57 @@ ipmi_set_session_privlvl_cmd(struct ipmi_intf * intf)
return 0; return 0;
} }
static uint8_t
ipmi_find_best_cipher_suite(struct ipmi_intf *intf)
{
enum cipher_suite_ids best_suite = IPMI_LANPLUS_CIPHER_SUITE_RESERVED;
#ifdef HAVE_CRYPTO_SHA256
struct cipher_suite_info suites[MAX_CIPHER_SUITE_COUNT];
size_t nr_suites = ARRAY_SIZE(suites);
/* cipher suite best order is chosen with this criteria:
* HMAC-MD5 and MD5 are BAD; xRC4 is bad; AES128 is required
* HMAC-SHA256 > HMAC-SHA1
* secure authentication > encrypted content
*
* With xRC4 out, all cipher suites with MD5 out, and cipher suite 3 being
* required by the spec, the only better defined standard cipher suite is
* 17. So if SHA256 is available, we should try to use that, otherwise,
* fall back to 3.
*/
const enum cipher_suite_ids cipher_order_preferred[] = {
IPMI_LANPLUS_CIPHER_SUITE_17,
IPMI_LANPLUS_CIPHER_SUITE_3,
};
const size_t nr_preferred = ARRAY_SIZE(cipher_order_preferred);
size_t ipref, i;
if (ipmi_get_channel_cipher_suites(intf, "ipmi", IPMI_LAN_CHANNEL_E,
suites, &nr_suites) < 0)
{
/* default legacy behavior - cipher suite 3 if none is requested */
return IPMI_LANPLUS_CIPHER_SUITE_3;
}
for (ipref = 0; ipref < nr_preferred &&
IPMI_LANPLUS_CIPHER_SUITE_RESERVED == best_suite; ipref++)
{
for (i = 0; i < nr_suites; i++) {
if (cipher_order_preferred[ipref] == suites[i].cipher_suite_id) {
best_suite = cipher_order_preferred[ipref];
break;
}
}
}
#endif /* HAVE_CRYPTO_SHA256 */
if (IPMI_LANPLUS_CIPHER_SUITE_RESERVED == best_suite) {
/* IPMI 2.0 spec requires that cipher suite 3 is implemented
* so we should always be able to fall back to that if better
* options are not available. */
best_suite = IPMI_LANPLUS_CIPHER_SUITE_3;
}
lprintf(LOG_INFO, "Using best available cipher suite %d\n", best_suite);
return best_suite;
}
/** /**
* ipmi_lanplus_open * ipmi_lanplus_open
*/ */
@ -3454,6 +3500,16 @@ ipmi_lanplus_open(struct ipmi_intf * intf)
lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+"); lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+");
goto fail; goto fail;
} }
/*
* If no cipher suite was provided, query the channel cipher suite list and
* pick the best one available
*/
if (IPMI_LANPLUS_CIPHER_SUITE_RESERVED ==
intf->ssn_params.cipher_suite_id)
{
ipmi_intf_session_set_cipher_suite_id(intf,
ipmi_find_best_cipher_suite(intf));
}
/* /*
* If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence * If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence
@ -3666,7 +3722,7 @@ static int ipmi_lanplus_setup(struct ipmi_intf * intf)
static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size)
{ {
if (intf->ssn_params.cipher_suite_id == 3) { if (intf->ssn_params.cipher_suite_id == IPMI_LANPLUS_CIPHER_SUITE_3) {
/* /*
* encrypted payload can only be multiple of 16 bytes * encrypted payload can only be multiple of 16 bytes
*/ */
@ -3684,7 +3740,7 @@ static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t siz
static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size)
{ {
if (intf->ssn_params.cipher_suite_id == 3) { if (intf->ssn_params.cipher_suite_id == IPMI_LANPLUS_CIPHER_SUITE_3) {
/* /*
* encrypted payload can only be multiple of 16 bytes * encrypted payload can only be multiple of 16 bytes
*/ */