From a9ee361f27e0439530387765924574e5358c8a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 10 Sep 2022 15:44:41 +0200 Subject: New upstream version 1.8.19 --- lib/ipmi_channel.c | 464 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 272 insertions(+), 192 deletions(-) (limited to 'lib/ipmi_channel.c') diff --git a/lib/ipmi_channel.c b/lib/ipmi_channel.c index fab2e54..bb7e60a 100644 --- a/lib/ipmi_channel.c +++ b/lib/ipmi_channel.c @@ -75,7 +75,7 @@ _ipmi_get_channel_access(struct ipmi_intf *intf, struct ipmi_rq req = {0}; uint8_t data[2]; - if (channel_access == NULL) { + if (!channel_access) { return (-3); } data[0] = channel_access->channel & 0x0F; @@ -87,9 +87,9 @@ _ipmi_get_channel_access(struct ipmi_intf *intf, req.msg.data_len = 2; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { return (-1); - } else if (rsp->ccode != 0) { + } else if (rsp->ccode) { return rsp->ccode; } else if (rsp->data_len != 2) { return (-2); @@ -118,7 +118,7 @@ _ipmi_get_channel_info(struct ipmi_intf *intf, struct ipmi_rq req = {0}; uint8_t data[1]; - if (channel_info == NULL) { + if (!channel_info) { return (-3); } data[0] = channel_info->channel & 0x0F; @@ -128,9 +128,9 @@ _ipmi_get_channel_info(struct ipmi_intf *intf, req.msg.data_len = 1; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { return (-1); - } else if (rsp->ccode != 0) { + } else if (rsp->ccode) { return rsp->ccode; } else if (rsp->data_len != 9) { return (-2); @@ -202,7 +202,7 @@ _ipmi_set_channel_access(struct ipmi_intf *intf, req.msg.data_len = 3; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { return (-1); } return rsp->ccode; @@ -225,7 +225,7 @@ iana_string(uint32_t iana) * ipmi_1_5_authtypes * * Create a string describing the supported authentication types as - * specificed by the parameter n + * specified by the parameter n */ static const char * ipmi_1_5_authtypes(uint8_t n) @@ -244,10 +244,28 @@ ipmi_1_5_authtypes(uint8_t n) return supportedTypes; } -uint8_t -ipmi_current_channel_medium(struct ipmi_intf *intf) +void +ipmi_current_channel_info(struct ipmi_intf *intf, + struct channel_info_t *chinfo) { - return ipmi_get_channel_medium(intf, 0xE); + int ccode = 0; + + chinfo->channel = CH_CURRENT; + ccode = _ipmi_get_channel_info(intf, chinfo); + if (ccode) { + if (ccode != IPMI_CC_INV_DATA_FIELD_IN_REQ) { + if (ccode > 0) { + lprintf(LOG_ERR, "Get Channel Info command failed: %s", + val2str(ccode, completion_code_vals)); + } + else { + eval_ccode(ccode); + } + } + chinfo->channel = CH_UNKNOWN; + chinfo->medium = IPMI_CHANNEL_MEDIUM_RESERVED; + } + return; } /** @@ -276,7 +294,7 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv) rsp = intf->sendrecv(intf, &req); - if ((rsp == NULL) || (rsp->ccode > 0)) { + if (!rsp || rsp->ccode) { /* * It's very possible that this failed because we asked for IPMI v2 data * Ask again, without requesting IPMI v2 data @@ -284,11 +302,11 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv) msg_data[0] &= 0x7F; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to Get Channel Authentication Capabilities"); return (-1); } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Channel Authentication Capabilities failed: %s", val2str(rsp->ccode, completion_code_vals)); return (-1); @@ -342,86 +360,152 @@ ipmi_get_channel_auth_cap(struct ipmi_intf *intf, uint8_t channel, uint8_t priv) return 0; } -static int -ipmi_get_channel_cipher_suites(struct ipmi_intf *intf, const char *payload_type, - uint8_t channel) +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) +{ + size_t count = 0; + size_t offset = 0; + + /* Default everything to zeroes */ + memset(suites, 0, sizeof(*suites) * nr_suites); + + while (offset < data_len && count < nr_suites) { + size_t suite_size; + + /* 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; + + /* 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); + + if (!suite_size) { + lprintf(LOG_INFO, + "Failed to parse cipher suite data at offset %d", + offset); + break; + } + + 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) { 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 == NULL) { - lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); - return -1; - } - if (rsp->ccode > 0) { - 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; + rqdata[1] = strcmp(payload_type, "ipmi") ? 1 : 0; + do { + /* Always ask for cipher suite format */ + rqdata[2] = LIST_ALGORITHMS_BY_CIPHER_SUITE | list_index; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Unable to Get Channel Cipher Suites"); return -1; } - if (rsp->ccode > 0) { + if (rsp->ccode + || rsp->data_len < 1 + || rsp->data_len > sizeof(uint8_t) + MAX_CIPHER_SUITE_DATA_LEN) + { 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 +513,49 @@ 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); + const char *header_str = +"ID IANA Auth Alg Integrity Alg Confidentiality Alg"; - 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("%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"), - cipher_suite_id, - iana_string(iana), - val2str(auth_alg, ipmi_auth_algorithms), - val2str(integrity_alg, ipmi_integrity_algorithms), - val2str(crypt_alg, ipmi_encryption_algorithms)); + 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), + val2str(suites[i].integrity_alg, ipmi_integrity_algorithms), + val2str(suites[i].crypt_alg, ipmi_encryption_algorithms)); } return 0; } @@ -648,8 +693,9 @@ ipmi_get_channel_info(struct ipmi_intf *intf, uint8_t channel) * * @channel - IPMI Channel * - * returns - IPMI Channel Medium, IPMI_CHANNEL_MEDIUM_RESERVED if ccode > 0, - * 0 on error. + * @returns IPMI Channel Medium + * @retval IPMI_CHANNEL_MEDIUM_RESERVED if ccode was not IPMI_CC_OK + * @retval 0 on error */ uint8_t ipmi_get_channel_medium(struct ipmi_intf *intf, uint8_t channel) @@ -659,13 +705,16 @@ ipmi_get_channel_medium(struct ipmi_intf *intf, uint8_t channel) channel_info.channel = channel; ccode = _ipmi_get_channel_info(intf, &channel_info); - if (ccode == 0xCC) { - return IPMI_CHANNEL_MEDIUM_RESERVED; - } else if (ccode < 0 && eval_ccode(ccode) != 0) { - return 0; - } else if (ccode > 0) { - lprintf(LOG_ERR, "Get Channel Info command failed: %s", - val2str(ccode, completion_code_vals)); + if (ccode) { + if (ccode != IPMI_CC_INV_DATA_FIELD_IN_REQ) { + if (ccode > 0) { + lprintf(LOG_ERR, "Get Channel Info command failed: %s", + val2str(ccode, completion_code_vals)); + } + else { + eval_ccode(ccode); + } + } return IPMI_CHANNEL_MEDIUM_RESERVED; } lprintf(LOG_DEBUG, "Channel type: %s", @@ -754,9 +803,27 @@ ipmi_set_user_access(struct ipmi_intf *intf, int argc, char **argv) int ccode = 0; int i = 0; uint8_t channel = 0; - uint8_t priv = 0; uint8_t user_id = 0; - if (argc > 0 && strncmp(argv[0], "help", 4) == 0) { + struct { + const char *option; + enum { + UA_INTEGER, /* direct integer value */ + UA_BOOLEAN, /* off/disable = false, on/enable = true */ + UA_BOOLEAN_INVERSE /* off/disable = true, on/enable = false */ + } type; + uint8_t *val; + uint8_t min; /* minimum value for UA_INTEGER options */ + uint8_t max; /* maximum value for UA_INTEGER options */ + } options[] = { + { "callin=", UA_BOOLEAN_INVERSE, &user_access.callin_callback, 0, 0}, + { "link=", UA_BOOLEAN, &user_access.link_auth, 0, 0}, + { "ipmi=", UA_BOOLEAN, &user_access.ipmi_messaging, 0, 0}, + { "privilege=", UA_INTEGER, &user_access.privilege_limit + , IPMI_SESSION_PRIV_CALLBACK + , IPMI_SESSION_PRIV_NOACCESS }, + }; + + if (argc > 0 && !strcmp(argv[0], "help")) { printf_channel_usage(); return 0; } else if (argc < 3) { @@ -778,33 +845,46 @@ ipmi_set_user_access(struct ipmi_intf *intf, int argc, char **argv) return (-1); } for (i = 2; i < argc; i ++) { - if (strncmp(argv[i], "callin=", 7) == 0) { - if (strncmp(argv[i] + 7, "off", 3) == 0) { - user_access.callin_callback = 1; - } else { - user_access.callin_callback = 0; - } - } else if (strncmp(argv[i], "link=", 5) == 0) { - if (strncmp(argv[i] + 5, "off", 3) == 0) { - user_access.link_auth = 0; - } else { - user_access.link_auth = 1; - } - } else if (strncmp(argv[i], "ipmi=", 5) == 0) { - if (strncmp(argv[i] + 5, "off", 3) == 0) { - user_access.ipmi_messaging = 0; - } else { - user_access.ipmi_messaging = 1; - } - } else if (strncmp(argv[i], "privilege=", 10) == 0) { - if (str2uchar(argv[i] + 10, &priv) != 0) { - lprintf(LOG_ERR, - "Numeric value expected, but '%s' given.", - argv[i] + 10); - return (-1); + size_t j; + for (j = 0; j < ARRAY_SIZE(options); ++j) { + const char *opt = argv[i]; + const int optlen = strlen(options[j].option); + if (!strncmp(opt, options[j].option, optlen)) { + const char *optval = opt + optlen; + uint16_t val; + + if (UA_INTEGER != options[j].type) { + bool boolval = (UA_BOOLEAN_INVERSE == options[j].type) + ? false + : true; + *options[j].val = boolval; + if (!strcmp(optval, "off") + || !strcmp(optval, "disable") + || !strcmp(optval, "no")) + { + boolval = !boolval; + } + } else if (UINT8_MAX + != (val = str2val(optval, ipmi_privlvl_vals))) + { + *options[j].val = (uint8_t)val; + } else if (str2uchar(optval, options[j].val)) { + lprintf(LOG_ERR + , "Numeric [%hhu-%hhu] value expected, " + "but '%s' given." + , options[j].min + , options[j].max + , optval); + return (-1); + } + lprintf(LOG_DEBUG + , "Option %s=%hhu" + , options[j].option + , *options[j].val); + break; } - user_access.privilege_limit = priv; - } else { + } + if (ARRAY_SIZE(options) == j) { lprintf(LOG_ERR, "Invalid option: %s\n", argv[i]); return (-1); } @@ -831,10 +911,10 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) lprintf(LOG_ERR, "Not enough parameters given."); printf_channel_usage(); return (-1); - } else if (strncmp(argv[0], "help", 4) == 0) { + } else if (!strcmp(argv[0], "help")) { printf_channel_usage(); return 0; - } else if (strncmp(argv[0], "authcap", 7) == 0) { + } else if (!strcmp(argv[0], "authcap")) { if (argc != 3) { printf_channel_usage(); return (-1); @@ -844,7 +924,7 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) return (-1); } retval = ipmi_get_channel_auth_cap(intf, channel, priv); - } else if (strncmp(argv[0], "getaccess", 10) == 0) { + } else if (!strcmp(argv[0], "getaccess")) { uint8_t user_id = 0; if ((argc < 2) || (argc > 3)) { lprintf(LOG_ERR, "Not enough parameters given."); @@ -860,9 +940,9 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) } } retval = ipmi_get_user_access(intf, channel, user_id); - } else if (strncmp(argv[0], "setaccess", 9) == 0) { + } else if (!strcmp(argv[0], "setaccess")) { return ipmi_set_user_access(intf, (argc - 1), &(argv[1])); - } else if (strncmp(argv[0], "info", 4) == 0) { + } else if (!strcmp(argv[0], "info")) { channel = 0xE; if (argc > 2) { printf_channel_usage(); @@ -874,11 +954,11 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) } } retval = ipmi_get_channel_info(intf, channel); - } else if (strncmp(argv[0], "getciphers", 10) == 0) { + } else if (!strcmp(argv[0], "getciphers")) { /* channel getciphers [channel] */ channel = 0xE; if ((argc < 2) || (argc > 3) || - (strncmp(argv[1], "ipmi", 4) && strncmp(argv[1], "sol", 3))) { + (strcmp(argv[1], "ipmi") && strcmp(argv[1], "sol"))) { printf_channel_usage(); return (-1); } @@ -887,9 +967,9 @@ ipmi_channel_main(struct ipmi_intf *intf, int argc, char **argv) return (-1); } } - retval = ipmi_get_channel_cipher_suites(intf, - argv[1], /* ipmi | sol */ - channel); + retval = ipmi_print_channel_cipher_suites(intf, + argv[1], /* ipmi | sol */ + channel); } else { lprintf(LOG_ERR, "Invalid CHANNEL command: %s\n", argv[0]); printf_channel_usage(); -- cgit v1.2.3