diff --git a/include/ipmitool/ipmi_channel.h b/include/ipmitool/ipmi_channel.h index 6dfca60..7ff723c 100644 --- a/include/ipmitool/ipmi_channel.h +++ b/include/ipmitool/ipmi_channel.h @@ -119,8 +119,8 @@ struct oem_cipher_suite_record_t { * records. */ #define MAX_CIPHER_SUITE_COUNT (MAX_CIPHER_SUITE_RECORD_OFFSET * \ - MAX_CIPHER_SUITE_DATA_LEN / \ - sizeof(struct std_cipher_suite_record_t)) + MAX_CIPHER_SUITE_DATA_LEN / \ + sizeof(struct std_cipher_suite_record_t)) /* * The Get Authentication Capabilities response structure @@ -174,20 +174,25 @@ struct get_channel_auth_cap_rsp { #endif int _ipmi_get_channel_access(struct ipmi_intf *intf, - struct channel_access_t *channel_access, - 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); + struct channel_access_t *channel_access, + 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, - struct channel_info_t *channel_info); + struct channel_info_t *channel_info); int _ipmi_set_channel_access(struct ipmi_intf *intf, - struct channel_access_t channel_access, uint8_t access_option, - uint8_t privilege_option); + struct channel_access_t channel_access, + uint8_t access_option, + uint8_t privilege_option); uint8_t ipmi_get_channel_medium(struct ipmi_intf * intf, uint8_t channel); uint8_t ipmi_current_channel_medium(struct ipmi_intf * intf); int ipmi_channel_main(struct ipmi_intf * intf, int argc, char ** argv); -int ipmi_get_channel_auth_cap(struct ipmi_intf * intf, uint8_t channel, uint8_t priv); +int ipmi_get_channel_auth_cap(struct ipmi_intf * intf, + uint8_t channel, uint8_t priv); int ipmi_get_channel_info(struct ipmi_intf * intf, uint8_t channel); #endif /*IPMI_CHANNEL_H*/ diff --git a/include/ipmitool/ipmi_intf.h b/include/ipmitool/ipmi_intf.h index 7f48a04..2ca409f 100644 --- a/include/ipmitool/ipmi_intf.h +++ b/include/ipmitool/ipmi_intf.h @@ -251,7 +251,7 @@ 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); #ifdef IPMI_INTF_LANPLUS void ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, - enum cipher_suite_ids cipher_suite_id); + 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_kgkey(struct ipmi_intf *intf, const uint8_t *kgkey); diff --git a/lib/ipmi_channel.c b/lib/ipmi_channel.c index fcc384e..65da32a 100644 --- a/lib/ipmi_channel.c +++ b/lib/ipmi_channel.c @@ -342,75 +342,108 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv) return 0; } +static inline size_t parse_cipher_suite(uint8_t *cipher_suite_data, + size_t data_len, + uint32_t *iana, + uint8_t *auth_alg, + uint8_t *integrity_alg, + uint8_t *crypt_alg, + enum cipher_suite_ids *cipher_suite_id) +{ + size_t size = 0; + const char *incomplete = "Incomplete data record in cipher suite data"; + + if (*cipher_suite_data == STANDARD_CIPHER_SUITE) { + struct std_cipher_suite_record_t *record = + (struct std_cipher_suite_record_t*)cipher_suite_data; + + /* Verify that we have at least a full record left; id + 3 algs */ + if (data_len < sizeof(*record)) { + lprintf(LOG_INFO, "%s", incomplete); + goto out; + } + + /* IANA code remains default (0) */ + *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; + size = sizeof(*record); + } else if (*cipher_suite_data == OEM_CIPHER_SUITE) { + /* OEM record type */ + struct oem_cipher_suite_record_t *record = + (struct oem_cipher_suite_record_t*)cipher_suite_data; + /* Verify that we have at least a full record left + * id + iana + 3 algs + */ + if (data_len < sizeof(*record)) { + lprintf(LOG_INFO, "%s", incomplete); + goto out; + } + + /* Grab the IANA */ + *iana = ipmi24toh(record->iana); + *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; + size = sizeof(*record); + } else { + lprintf(LOG_INFO, "Bad start of record byte in cipher suite data " + "(value %x)", *cipher_suite_data); + } + +out: + return size; +} + 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) + 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; + /* Default everything to zeroes */ 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; + size_t suite_size; - /* 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; - } + /* Set non-zero defaults */ + suites[count].auth_alg = IPMI_AUTH_RAKP_NONE; + suites[count].integrity_alg = IPMI_INTEGRITY_NONE; + suites[count].crypt_alg = IPMI_CRYPT_NONE; - cipher_suite_id = record->cipher_suite_id; + /* Update fields from cipher suite data */ + suite_size = parse_cipher_suite(cipher_suite_data + offset, + data_len - offset, + &suites[count].iana, + &suites[count].auth_alg, + &suites[count].integrity_alg, + &suites[count].crypt_alg, + &suites[count].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]); + if (!suite_size) { + lprintf(LOG_INFO, + "Failed to parse cipher suite data at offset %d", + 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; + + offset += suite_size; count++; } return count; } 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) +ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, + const char *payload_type, + uint8_t channel, + struct cipher_suite_info *suites, + size_t *count) { struct ipmi_rs *rsp; struct ipmi_rq req; @@ -419,7 +452,7 @@ ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, uint8_t list_index = 0; /* 0x40 sets * 16 bytes per set */ uint8_t cipher_suite_data[MAX_CIPHER_SUITE_RECORD_OFFSET * - MAX_CIPHER_SUITE_DATA_LEN]; + MAX_CIPHER_SUITE_DATA_LEN]; size_t offset = 0; size_t nr_suites = 0; @@ -464,8 +497,8 @@ ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, * 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)); + } 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); @@ -473,27 +506,30 @@ ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, } static int -ipmi_print_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, - uint8_t channel) +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); + const char *header_str = +"ID IANA Auth Alg Integrity Alg Confidentiality Alg"; rc = ipmi_get_channel_cipher_suites(intf, payload_type, channel, - suites, &nr_suites); + suites, &nr_suites); if (rc < 0) return rc; - if (! csv_output) { - printf("ID IANA Auth Alg Integrity Alg Confidentiality Alg\n"); + if (!csv_output) { + printf("%s\n", header_str); } 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"), + printf(csv_output ? "%d,%s,%s,%s,%s\n" + : "%-4d %-7s %-15s %-15s %-15s\n", suites[i].cipher_suite_id, iana_string(suites[i].iana), val2str(suites[i].auth_alg, ipmi_auth_algorithms), @@ -877,8 +913,8 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) } } retval = ipmi_print_channel_cipher_suites(intf, - argv[1], /* ipmi | sol */ - channel); + argv[1], /* ipmi | sol */ + channel); } else { lprintf(LOG_ERR, "Invalid CHANNEL command: %s\n", argv[0]); printf_channel_usage(); diff --git a/src/plugins/ipmi_intf.c b/src/plugins/ipmi_intf.c index 4288e6b..eebd69d 100644 --- a/src/plugins/ipmi_intf.c +++ b/src/plugins/ipmi_intf.c @@ -252,7 +252,7 @@ ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit) #ifdef IPMI_INTF_LANPLUS void ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, - enum cipher_suite_ids cipher_suite_id) + enum cipher_suite_ids cipher_suite_id) { intf->ssn_params.cipher_suite_id = cipher_suite_id; } diff --git a/src/plugins/lanplus/lanplus.c b/src/plugins/lanplus/lanplus.c index 7b7c227..b7dfc3d 100644 --- a/src/plugins/lanplus/lanplus.c +++ b/src/plugins/lanplus/lanplus.c @@ -163,10 +163,11 @@ extern int verbose; * returns 0 on success * 1 on failure */ -int lanplus_get_requested_ciphers(enum cipher_suite_ids cipher_suite_id, - uint8_t * auth_alg, - uint8_t * integrity_alg, - uint8_t * crypt_alg) +int +lanplus_get_requested_ciphers(enum cipher_suite_ids cipher_suite_id, + uint8_t *auth_alg, + uint8_t *integrity_alg, + uint8_t *crypt_alg) { /* See table 22-19 for the source of the statement */ switch (cipher_suite_id) @@ -3383,15 +3384,16 @@ ipmi_find_best_cipher_suite(struct ipmi_intf *intf) #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. + * 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, @@ -3403,14 +3405,18 @@ ipmi_find_best_cipher_suite(struct ipmi_intf *intf) 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 */ + /* default legacy behavior - fall back to cipher suite 3 */ return IPMI_LANPLUS_CIPHER_SUITE_3; } - for (ipref = 0; ipref < nr_preferred && - IPMI_LANPLUS_CIPHER_SUITE_RESERVED == best_suite; ipref++) + 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) { + if (cipher_order_preferred[ipref] + == suites[i].cipher_suite_id) + { best_suite = cipher_order_preferred[ipref]; break; } @@ -3477,7 +3483,9 @@ ipmi_lanplus_open(struct ipmi_intf * intf) /* Setup our lanplus session state */ memset(session, 0, sizeof(struct ipmi_session)); session->timeout = params->timeout; - memcpy(&session->authcode, ¶ms->authcode_set, sizeof(session->authcode)); + memcpy(&session->authcode, + ¶ms->authcode_set, + sizeof(session->authcode)); session->v2_data.auth_alg = IPMI_AUTH_RAKP_NONE; session->v2_data.crypt_alg = IPMI_CRYPT_NONE; session->sol_data.sequence_number = 1; @@ -3496,25 +3504,30 @@ ipmi_lanplus_open(struct ipmi_intf * intf) goto fail; } - if (!ipmi_oem_active(intf, "i82571spt") && ! auth_cap.v20_data_available) { + if (!ipmi_oem_active(intf, "i82571spt") && + !auth_cap.v20_data_available) + { lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+"); goto fail; } + /* - * If no cipher suite was provided, query the channel cipher suite list and - * pick the best one available + * 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)); + 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 - * needs to restart. The individual messages are not individually retryable, - * as the session state is advancing. + * If the open/rakp1/rakp3 sequence encounters a timeout, the whole + * sequence needs to restart. The individual messages are not + * individually retryable, as the session state is advancing. */ for (retry = 0; retry < IPMI_LAN_RETRY; retry++) { session->v2_data.session_state = LANPLUS_STATE_PRESESSION;