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

Reply via email to