Following patch implements "Set Channel Security Keys" command. It allows users to set K_g key. Other variants of the command, like set K_r keys or read both K_g and K_r keys, were not implemented (it won't be difficult though).
Usage: # ipmitool channel setkg plain opensesame # ipmitool channel setkg hex 00000000001 Tested on Dell PowerEdge 1950. As consequence, I moved ipmi_parse_hex() to lib/helper.c, now it's used by more modules. Signed-off-by: Jan Safranek <jsafr...@redhat.com> --- doc/ipmitool.1 | 14 +++++ include/ipmitool/helper.h | 4 ++ include/ipmitool/ipmi_channel.h | 37 ++++++++++++++ include/ipmitool/ipmi_intf.h | 1 lib/helper.c | 62 ++++++++++++++++++++++++ lib/ipmi_channel.c | 100 +++++++++++++++++++++++++++++++++++++++ lib/ipmi_main.c | 62 ------------------------ 7 files changed, 217 insertions(+), 63 deletions(-) diff --git a/doc/ipmitool.1 b/doc/ipmitool.1 index 13a2779..da18646 100644 --- a/doc/ipmitool.1 +++ b/doc/ipmitool.1 @@ -398,6 +398,20 @@ Configure user access information on the given channel for the given userid. Displays the list of cipher suites supported for the given application (ipmi or sol) on the given channel. +.TP +\fIsetkg\fP <\fIhex\fP|\fIplain\fP> <\fBkey\fP> [<\fBchannel\fR>] +.br + +Sets K_g key to given value. Use \fIplain\fP to specify \fBkey\fR as simple ASCII string. +Use \fIhex\fP to specify \fBkey\fR as sequence of hexadecimal codes of ASCII charactes. +I.e. following two examples are equivalent: + +.RS +ipmitool channel setkg plain PASSWORD + +ipmitool channel setkg hex 50415353574F5244 +.RE + .RE .TP \fIchassis\fP diff --git a/include/ipmitool/helper.h b/include/ipmitool/helper.h index 031da22..df20555 100644 --- a/include/ipmitool/helper.h +++ b/include/ipmitool/helper.h @@ -50,6 +50,8 @@ #define tboolean int #endif +#define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ + struct ipmi_intf; struct valstr { @@ -77,6 +79,8 @@ uint8_t ipmi_csum(uint8_t * d, int s); FILE * ipmi_open_file(const char * file, int rw); void ipmi_start_daemon(struct ipmi_intf *intf); +unsigned char *ipmi_parse_hex(const char *str); + #define ipmi_open_file_read(file) ipmi_open_file(file, 0) #define ipmi_open_file_write(file) ipmi_open_file(file, 1) diff --git a/include/ipmitool/ipmi_channel.h b/include/ipmitool/ipmi_channel.h index 00fda96..d05ad46 100644 --- a/include/ipmitool/ipmi_channel.h +++ b/include/ipmitool/ipmi_channel.h @@ -48,7 +48,10 @@ #define IPMI_GET_USER_NAME 0x46 #define IPMI_SET_USER_PASSWORD 0x47 #define IPMI_GET_CHANNEL_CIPHER_SUITES 0x54 +#define IPMI_SET_CHANNEL_SECURITY_KEYS 0x56 +#define IPMI_KG_KEY_ID 1 +#define IPMI_SET_CHANNEL_SECURITY_KEYS_OP_SET 1 /* * The Get Authentication Capabilities response structure @@ -220,6 +223,40 @@ struct set_user_access_data { #endif } __attribute__ ((packed)); +struct set_channel_security_keys_req { +#if WORDS_BIGENDIAN + uint8_t __reserved1 :4; + uint8_t channel :4; + + uint8_t __reserved2 :6; + uint8_t operation :2; + + uint8_t key_id; + unsigned char key_value[IPMI_KG_BUFFER_SIZE-1]; /* we don't want space for '\0' at the end */ +#else + uint8_t channel :4; + uint8_t __reserved1 :4; + + uint8_t operation :2; + uint8_t __reserved2 :6; + + uint8_t key_id; + unsigned char key_value[IPMI_KG_BUFFER_SIZE-1]; /* we don't want space for '\0' at the end */ +#endif +} __attribute__ ((packed)); + +struct set_channel_security_keys_rsp { +#if WORDS_BIGENDIAN + uint8_t __reserved1 :6; + uint8_t lock_status :2; + unsigned char key_value; /* just the first character, use &key_value to explore the rest */ +#else + uint8_t lock_status :2; + uint8_t __reserved1 :6; + unsigned char key_value; /* just the first character, use &key_value to explore the rest */ +#endif +} __attribute__ ((packed)); + 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); diff --git a/include/ipmitool/ipmi_intf.h b/include/ipmitool/ipmi_intf.h index 78caad7..395bf89 100644 --- a/include/ipmitool/ipmi_intf.h +++ b/include/ipmitool/ipmi_intf.h @@ -60,7 +60,6 @@ enum LANPLUS_SESSION_STATE { #define IPMI_AUTHCODE_BUFFER_SIZE 20 #define IPMI_SIK_BUFFER_SIZE 20 -#define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ struct ipmi_session { uint8_t hostname[64]; diff --git a/lib/helper.c b/lib/helper.c index 3109dfe..f6ee00a 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -438,3 +438,65 @@ ipmi_start_daemon(struct ipmi_intf *intf) dup(0); dup(0); } + +/* ipmi_parse_hex - convert hexadecimal numbers to ascii string + * Input string must be composed of two-characer hexadecimal numbers. + * There is no separator between the numbers. Each number results in one character + * of the converted string. + * + * Example: ipmi_parse_hex("50415353574F5244") returns 'PASSWORD' + * + * @param str: input string. It must contain only even number of '0'-'9','a'-'f' and 'A-F' characters. + * @returns converted ascii string + * @returns NULL on error + */ +unsigned char * +ipmi_parse_hex(const char *str) +{ + const char * p; + unsigned char * out, *q; + unsigned char b = 0; + int shift = 4; + + if (strlen(str) == 0) + return NULL; + + if (strlen(str) % 2 != 0) { + lprintf(LOG_ERR, "Number of hex_kg characters is not even"); + return NULL; + } + + if (strlen(str) > (IPMI_KG_BUFFER_SIZE-1)*2) { + lprintf(LOG_ERR, "Kg key is too long"); + return NULL; + } + + out = calloc(IPMI_KG_BUFFER_SIZE, sizeof(unsigned char)); + if (out == NULL) { + lprintf(LOG_ERR, "malloc failure"); + return NULL; + } + + for (p = str, q = out; *p; p++) { + if (!isxdigit(*p)) { + lprintf(LOG_ERR, "Kg_hex is not hexadecimal number"); + free(out); + return NULL; + } + + if (*p < 'A') /* it must be 0-9 */ + b = *p - '0'; + else /* it's A-F or a-f */ + b = (*p | 0x20) - 'a' + 10; /* convert to lowercase and to 10-15 */ + + *q = *q + b << shift; + if (shift) + shift = 0; + else { + shift = 4; + q++; + } + } + + return out; +} diff --git a/lib/ipmi_channel.c b/lib/ipmi_channel.c index 4acdae7..098e853 100644 --- a/lib/ipmi_channel.c +++ b/lib/ipmi_channel.c @@ -776,6 +776,90 @@ ipmi_current_channel_medium(struct ipmi_intf * intf) return ipmi_get_channel_medium(intf, 0xE); } +int +ipmi_set_channel_security_keys (struct ipmi_intf *intf, uint8_t channel, + const char *method, const char *key) +{ + unsigned char* decoded_key = NULL; + struct ipmi_rs *rsp; + struct ipmi_rq req; + struct set_channel_security_keys_req req_data; + + /* convert provided key to array of bytes */ + if (strcmp(method, "hex") == 0) { + if (strlen(key) > (IPMI_KG_BUFFER_SIZE-1)*2) { + lprintf(LOG_ERR, "Provided key is too long, max. length is 20 bytes"); + printf_channel_usage(); + return -1; + } + decoded_key = ipmi_parse_hex(key); + if (decoded_key == NULL) { + /* something went bad, ipmi_parse_hex already reported the error */ + return -1; + } + } else if (strcmp(method, "plain") == 0) { + if (strlen(key) > IPMI_KG_BUFFER_SIZE-1) { + lprintf(LOG_ERR, "Provided key is too long, max. length is 20 bytes"); + printf_channel_usage(); + return -1; + } + + decoded_key = calloc(IPMI_KG_BUFFER_SIZE, sizeof(unsigned char)); + if (decoded_key == NULL) { + lprintf(LOG_ERR, "Cannot allocate memory"); + return -1; + } + strncpy(decoded_key, key, IPMI_KG_BUFFER_SIZE-1); + } else { + printf_channel_usage(); + return -1; + } + + /* assemble and send request to set kg key */ + memset(&req_data, 0, sizeof(req_data)); + req_data.channel = channel; + req_data.operation = IPMI_SET_CHANNEL_SECURITY_KEYS_OP_SET; + req_data.key_id = IPMI_KG_KEY_ID; + memcpy(req_data.key_value, decoded_key, IPMI_KG_BUFFER_SIZE-1); + free(decoded_key); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = IPMI_SET_CHANNEL_SECURITY_KEYS; + req.msg.data = (uint8_t*) &req_data; + req.msg.data_len = sizeof(req_data); + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Channel Security Keys command failed"); + return -1; + } + if (rsp->ccode > 0) { + const char *error = NULL; + switch (rsp->ccode) { + case 0x80: + error = "Key is locked"; + break; + case 0x81: + error = "Insufficient key bytes"; + break; + case 0x82: + error = "Too many key bytes"; + break; + case 0x83: + error = "Key value does not meet criteria for K_g key"; + break; + default: + error = val2str(rsp->ccode, completion_code_vals); + } + lprintf(LOG_ERR, "Error setting security key: %X (%s)", rsp->ccode, error); + return -1; + } + + lprintf(LOG_NOTICE, "Set Channel Security Keys command succeeded"); + return 0; +} + void printf_channel_usage() { @@ -785,6 +869,7 @@ printf_channel_usage() "<user id> [callin=on|off] [ipmi=on|off] [link=on|off] [privilege=level]"); lprintf(LOG_NOTICE, " info [channel number]"); lprintf(LOG_NOTICE, " getciphers <ipmi | sol> [channel]\n"); + lprintf(LOG_NOTICE, " setkg hex|plain <key> [channel]\n"); lprintf(LOG_NOTICE, "Possible privilege levels are:"); lprintf(LOG_NOTICE, " 1 Callback level"); lprintf(LOG_NOTICE, " 2 User level"); @@ -857,6 +942,21 @@ ipmi_channel_main(struct ipmi_intf * intf, int argc, char ** argv) ch); } } + else if (strcmp(argv[0], "setkg") == 0) + { + if (argc < 3 || argc > 4) + printf_channel_usage(); + else { + uint8_t ch = 0xe; + char *method = argv[1]; + char *key = argv[2]; + if (argc == 4) { + ch = (uint8_t)strtol(argv[3], NULL, 0); + } + + retval = ipmi_set_channel_security_keys(intf, ch, method, key); + } + } else { printf("Invalid CHANNEL command: %s\n", argv[0]); diff --git a/lib/ipmi_main.c b/lib/ipmi_main.c index e904986..9980360 100644 --- a/lib/ipmi_main.c +++ b/lib/ipmi_main.c @@ -274,68 +274,6 @@ void ipmi_catch_sigint() } -/* ipmi_parse_hex - convert hexadecimal numbers to ascii string - * Input string must be composed of two-characer hexadecimal numbers. - * There is no separator between the numbers. Each number results in one character - * of the converted string. - * - * Example: ipmi_parse_hex("50415353574F5244") returns 'PASSWORD' - * - * @param str: input string. It must contain only even number of '0'-'9','a'-'f' and 'A-F' characters. - * @returns converted ascii string - * @returns NULL on error - */ -static unsigned char * -ipmi_parse_hex(const char *str) -{ - const char * p; - unsigned char * out, *q; - unsigned char b = 0; - int shift = 4; - - if (strlen(str) == 0) - return NULL; - - if (strlen(str) % 2 != 0) { - lprintf(LOG_ERR, "Number of hex_kg characters is not even"); - return NULL; - } - - if (strlen(str) > (IPMI_KG_BUFFER_SIZE-1)*2) { - lprintf(LOG_ERR, "Kg key is too long"); - return NULL; - } - - out = calloc(IPMI_KG_BUFFER_SIZE, sizeof(unsigned char)); - if (out == NULL) { - lprintf(LOG_ERR, "malloc failure"); - return NULL; - } - - for (p = str, q = out; *p; p++) { - if (!isxdigit(*p)) { - lprintf(LOG_ERR, "Kg_hex is not hexadecimal number"); - free(out); - return NULL; - } - - if (*p < 'A') /* it must be 0-9 */ - b = *p - '0'; - else /* it's A-F or a-f */ - b = (*p | 0x20) - 'a' + 10; /* convert to lowercase and to 10-15 */ - - *q = *q + b << shift; - if (shift) - shift = 0; - else { - shift = 4; - q++; - } - } - - return out; -} - /* ipmi_parse_options - helper function to handle parsing command line options * * @argc: count of options ------------------------------------------------------------------------------ OpenSolaris 2009.06 is a cutting edge operating system for enterprises looking to deploy the next generation of Solaris that includes the latest innovations from Sun and the OpenSource community. Download a copy and enjoy capabilities such as Networking, Storage and Virtualization. Go to: http://p.sf.net/sfu/opensolaris-get _______________________________________________ Ipmitool-devel mailing list Ipmitool-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ipmitool-devel