diff options
author | Vernon Mauery <vernon.mauery@intel.com> | 2018-04-09 12:28:57 -0700 |
---|---|---|
committer | Alexander Amelkin <mocbuhtig@amelkin.msk.ru> | 2018-11-01 19:29:45 +0300 |
commit | 7772254b62826b894ca629df8c597030a98f4f72 (patch) | |
tree | c60b3a9724a8888a6ce220f09c6ad4bf40082b1b /lib | |
parent | a8862d7508fb138b1c286eea958700cca63c9476 (diff) | |
download | ipmitool-7772254b62826b894ca629df8c597030a98f4f72.tar.gz |
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>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ipmi_channel.c | 242 | ||||
-rw-r--r-- | lib/ipmi_main.c | 9 |
2 files changed, 123 insertions, 128 deletions
diff --git a/lib/ipmi_channel.c b/lib/ipmi_channel.c index 57aebef..fcc384e 100644 --- a/lib/ipmi_channel.c +++ b/lib/ipmi_channel.c @@ -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)); - 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; + *count = parse_channel_cipher_suite_data(cipher_suite_data, offset, suites, + nr_suites); + return 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; - } +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); - cipher_suite_id = cipher_suite_data[offset++]; + rc = ipmi_get_channel_cipher_suites(intf, payload_type, channel, + suites, &nr_suites); - /* 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; - } + if (rc < 0) + return rc; - /* - * 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; - } - } + if (! csv_output) { + printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n"); + } + 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 { diff --git a/lib/ipmi_main.c b/lib/ipmi_main.c index 166995c..43ac5f7 100644 --- a/lib/ipmi_main.c +++ b/lib/ipmi_main.c @@ -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; |