mirror of
https://github.com/ipmitool/ipmitool.git
synced 2026-06-29 16:51:35 +00:00
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:
committed by
Alexander Amelkin
parent
a8862d7508
commit
7772254b62
@@ -342,86 +342,116 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv)
|
||||
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,
|
||||
uint8_t channel)
|
||||
uint8_t channel, struct cipher_suite_info *suites, size_t *count)
|
||||
{
|
||||
struct ipmi_rs *rsp;
|
||||
struct ipmi_rq req;
|
||||
|
||||
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;
|
||||
/* 0x40 sets * 16 bytes per set */
|
||||
uint8_t cipher_suite_data[1024];
|
||||
uint16_t offset = 0;
|
||||
/* how much was returned, total */
|
||||
uint16_t cipher_suite_data_length = 0;
|
||||
uint8_t cipher_suite_data[MAX_CIPHER_SUITE_RECORD_OFFSET *
|
||||
MAX_CIPHER_SUITE_DATA_LEN];
|
||||
size_t offset = 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(&req, 0, sizeof(req));
|
||||
req.msg.netfn = IPMI_NETFN_APP;
|
||||
req.msg.cmd = IPMI_GET_CHANNEL_CIPHER_SUITES;
|
||||
req.msg.data = rqdata;
|
||||
req.msg.data_len = 3;
|
||||
req.msg.data_len = sizeof(rqdata);
|
||||
|
||||
rqdata[0] = channel;
|
||||
rqdata[1] = ((strncmp(payload_type, "ipmi", 4) == 0)? 0: 1);
|
||||
/* Always ask for cipher suite format */
|
||||
rqdata[2] = 0x80;
|
||||
|
||||
rsp = intf->sendrecv(intf, &req);
|
||||
if (!rsp) {
|
||||
lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites");
|
||||
return -1;
|
||||
}
|
||||
if (rsp->ccode) {
|
||||
lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s",
|
||||
val2str(rsp->ccode, completion_code_vals));
|
||||
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.
|
||||
* printf("copying data to offset %d\n", offset);
|
||||
* printbuf(rsp->data + 1, rsp->data_len - 1, "this is the data");
|
||||
*/
|
||||
memcpy(cipher_suite_data + offset, rsp->data + 1, rsp->data_len - 1);
|
||||
offset += rsp->data_len - 1;
|
||||
|
||||
/*
|
||||
* Increment our list for the next call
|
||||
*/
|
||||
++list_index;
|
||||
rqdata[2] = (rqdata[2] & 0x80) + list_index;
|
||||
|
||||
do {
|
||||
/* Always ask for cipher suite format */
|
||||
rqdata[2] = LIST_ALGORITHMS_BY_CIPHER_SUITE | list_index;
|
||||
rsp = intf->sendrecv(intf, &req);
|
||||
if (!rsp) {
|
||||
lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites");
|
||||
return -1;
|
||||
}
|
||||
if (rsp->ccode) {
|
||||
if (rsp->ccode || rsp->data_len < 1) {
|
||||
lprintf(LOG_ERR, "Get Channel Cipher Suites failed: %s",
|
||||
val2str(rsp->ccode, completion_code_vals));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy last chunk */
|
||||
if(rsp->data_len > 1) {
|
||||
/*
|
||||
* We got back cipher suite data -- store it.
|
||||
* printf("copying data to offset %d\n", offset);
|
||||
@@ -429,88 +459,46 @@ ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
|
||||
*/
|
||||
memcpy(cipher_suite_data + offset, rsp->data + 1, rsp->data_len - 1);
|
||||
offset += rsp->data_len - 1;
|
||||
}
|
||||
|
||||
/* We can chomp on all our data now. */
|
||||
cipher_suite_data_length = offset;
|
||||
offset = 0;
|
||||
/*
|
||||
* Increment our list for the next call
|
||||
*/
|
||||
++list_index;
|
||||
} while ((rsp->data_len == (sizeof(uint8_t) + MAX_CIPHER_SUITE_DATA_LEN)) &&
|
||||
(list_index < MAX_CIPHER_SUITE_RECORD_OFFSET));
|
||||
|
||||
*count = parse_channel_cipher_suite_data(cipher_suite_data, offset, suites,
|
||||
nr_suites);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_print_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type,
|
||||
uint8_t channel)
|
||||
{
|
||||
int rc;
|
||||
size_t i = 0;
|
||||
struct cipher_suite_info suites[MAX_CIPHER_SUITE_COUNT];
|
||||
size_t nr_suites = sizeof(*suites);
|
||||
|
||||
rc = ipmi_get_channel_cipher_suites(intf, payload_type, channel,
|
||||
suites, &nr_suites);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (! csv_output) {
|
||||
printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n");
|
||||
}
|
||||
while (offset < cipher_suite_data_length) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nr_suites; i++) {
|
||||
/* We have everything we need to spit out a cipher suite record */
|
||||
printf((csv_output? "%d,%s,%s,%s,%s\n" :
|
||||
"%-4d %-7s %-15s %-15s %-15s\n"),
|
||||
cipher_suite_id,
|
||||
iana_string(iana),
|
||||
val2str(auth_alg, ipmi_auth_algorithms),
|
||||
val2str(integrity_alg, ipmi_integrity_algorithms),
|
||||
val2str(crypt_alg, ipmi_encryption_algorithms));
|
||||
suites[i].cipher_suite_id,
|
||||
iana_string(suites[i].iana),
|
||||
val2str(suites[i].auth_alg, ipmi_auth_algorithms),
|
||||
val2str(suites[i].integrity_alg, ipmi_integrity_algorithms),
|
||||
val2str(suites[i].crypt_alg, ipmi_encryption_algorithms));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -888,7 +876,7 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv)
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
retval = ipmi_get_channel_cipher_suites(intf,
|
||||
retval = ipmi_print_channel_cipher_suites(intf,
|
||||
argv[1], /* ipmi | sol */
|
||||
channel);
|
||||
} else {
|
||||
|
||||
@@ -342,7 +342,10 @@ ipmi_main(int argc, char ** argv,
|
||||
char * seloem = NULL;
|
||||
int port = 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 rc = -1;
|
||||
int ai_family = AF_UNSPEC;
|
||||
@@ -420,6 +423,7 @@ ipmi_main(int argc, char ** argv,
|
||||
goto out_free;
|
||||
}
|
||||
break;
|
||||
#ifdef IPMI_INTF_LANPLUS
|
||||
case 'C':
|
||||
if (str2int(optarg, &cipher_suite_id) != 0) {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
#endif /* IPMI_INTF_LANPLUS */
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
@@ -868,7 +873,9 @@ ipmi_main(int argc, char ** argv,
|
||||
|
||||
ipmi_intf_session_set_lookupbit(ipmi_main_intf, lookupbit);
|
||||
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);
|
||||
#endif /* IPMI_INTF_LANPLUS */
|
||||
|
||||
ipmi_main_intf->devnum = devnum;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user