diff -up ./doc/ipmitool.1.kg1 ./doc/ipmitool.1 --- ./doc/ipmitool.1.kg1 2014-03-05 09:04:19.334742088 +0100 +++ ./doc/ipmitool.1 2014-03-05 09:02:53.011524309 +0100 @@ -371,6 +371,20 @@ Configure user access information on the 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 .RE .TP diff -up ./include/ipmitool/helper.h.kg1 ./include/ipmitool/helper.h --- ./include/ipmitool/helper.h.kg1 2014-03-05 09:42:21.546499783 +0100 +++ ./include/ipmitool/helper.h 2014-03-05 09:43:28.095667676 +0100 @@ -58,6 +58,8 @@ # define IPMI_UID_MAX 63 #endif +#define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ + struct ipmi_intf; struct valstr { @@ -100,6 +102,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 -up ./include/ipmitool/ipmi_channel.h.kg1 ./include/ipmitool/ipmi_channel.h --- ./include/ipmitool/ipmi_channel.h.kg1 2014-03-05 09:04:41.707798532 +0100 +++ ./include/ipmitool/ipmi_channel.h 2014-03-05 09:10:00.770603481 +0100 @@ -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 @@ -246,6 +249,48 @@ struct set_user_access_data { #endif } ATTRIBUTE_PACKING; #ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +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_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +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_PACKING; +#ifdef HAVE_PRAGMA_PACK #pragma pack(0) #endif diff -up ./include/ipmitool/ipmi_intf.h.kg1 ./include/ipmitool/ipmi_intf.h --- ./include/ipmitool/ipmi_intf.h.kg1 2014-03-05 09:10:45.363715984 +0100 +++ ./include/ipmitool/ipmi_intf.h 2014-03-05 09:11:27.483822244 +0100 @@ -60,7 +60,7 @@ 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 */ +/* XXX: remove or modify # d e fine IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ struct ipmi_session { uint8_t hostname[64]; diff -up ./lib/helper.c.kg1 ./lib/helper.c --- ./lib/helper.c.kg1 2014-03-05 09:11:41.417857400 +0100 +++ ./lib/helper.c 2014-03-05 09:15:11.318386949 +0100 @@ -667,6 +667,68 @@ ipmi_start_daemon(struct ipmi_intf *intf 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; +} + /* is_fru_id - wrapper for str-2-int FRU ID conversion. Message is printed * on error. * FRU ID range: <0..255> diff -up ./lib/ipmi_channel.c.kg1 ./lib/ipmi_channel.c --- ./lib/ipmi_channel.c.kg1 2014-03-05 09:15:28.655430688 +0100 +++ ./lib/ipmi_channel.c 2014-03-05 09:37:19.367737427 +0100 @@ -786,6 +786,90 @@ ipmi_current_channel_medium(struct ipmi_ 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() { @@ -795,6 +879,7 @@ printf_channel_usage() " [callin=on|off] [ipmi=on|off] [link=on|off] [privilege=level]"); lprintf(LOG_NOTICE, " info [channel number]"); lprintf(LOG_NOTICE, " getciphers [channel]\n"); + lprintf(LOG_NOTICE, " setkg hex|plain [channel]\n"); lprintf(LOG_NOTICE, "Possible privilege levels are:"); lprintf(LOG_NOTICE, " 1 Callback level"); lprintf(LOG_NOTICE, " 2 User level"); @@ -892,6 +977,21 @@ ipmi_channel_main(struct ipmi_intf * int 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 -up ./lib/ipmi_main.c.kg1 ./lib/ipmi_main.c --- ./lib/ipmi_main.c.kg1 2014-03-05 09:50:30.905734365 +0100 +++ ./lib/ipmi_main.c 2014-03-05 10:47:04.857296819 +0100 @@ -275,69 +275,6 @@ void ipmi_catch_sigint() exit(-1); } -/* 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); - out = NULL; - 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 @@ -521,11 +458,12 @@ ipmi_main(int argc, char ** argv, free(kgkey); kgkey = NULL; } - kgkey = strdup(optarg); + kgkey = calloc(IPMI_KG_BUFFER_SIZE, 1); if (kgkey == NULL) { lprintf(LOG_ERR, "%s: malloc failure", progname); goto out_free; } + strncpy(kgkey, optarg, IPMI_KG_BUFFER_SIZE); break; case 'K': if ((tmp_env = getenv("IPMI_KGKEY"))) { @@ -533,11 +471,12 @@ ipmi_main(int argc, char ** argv, free(kgkey); kgkey = NULL; } - kgkey = strdup(tmp_env); + kgkey = calloc(IPMI_KG_BUFFER_SIZE, 1); if (kgkey == NULL) { lprintf(LOG_ERR, "%s: malloc failure", progname); goto out_free; } + strncpy(kgkey, tmp_env, IPMI_KG_BUFFER_SIZE); } else { lprintf(LOG_WARN, "Unable to read kgkey from environment"); } @@ -564,11 +503,14 @@ ipmi_main(int argc, char ** argv, kgkey = NULL; } kgkey = strdup(tmp_pass); - tmp_pass = NULL; + kgkey = calloc(IPMI_KG_BUFFER_SIZE, 1); if (kgkey == NULL) { lprintf(LOG_ERR, "%s: malloc failure", progname); + tmp_pass = NULL; goto out_free; } + strncpy(kgkey, tmp_pass, IPMI_KG_BUFFER_SIZE); + tmp_pass = NULL; } break; case 'U':