diff --git a/ipmitool/configure.in b/ipmitool/configure.in index 3cb7c3b..c7ef5c1 100644 --- a/ipmitool/configure.in +++ b/ipmitool/configure.in @@ -146,6 +146,42 @@ AC_SUBST(PLUGIN_INTF_LAN) AC_SUBST(STATIC_INTF_LAN) AC_SUBST(LDFLAGS_INTF_LAN) + + +PLUGIN_INTF_LANPLUS= +STATIC_INTF_LANPLUS= +LDFLAGS_INTF_LANPLUS= +AC_ARG_ENABLE([intf-lanplus], +[[ --enable-intf-lanplus enable IPMI-over-LAN RMCP+ interface ]] +[[ plugin : build dynamic]] +[[ static : link statically ]] +[[ --diable-intf-lanplus disable IPMI-over-LAN RMCP+ interface [default]]], + + [if test "x$enableval" = "xplugin" || test "x$enableval" = "xdynamic"; then + AC_SUBST(INTF_LANPLUS,lanplus) + PLUGIN_INTF_LANPLUS="libintf_lanplus.la" + LDFLAGS_INTF_LANPLUS="-rpath $pluginpath -avoid-version" + DYNAMIC_INTF_LIST="lanplus $DYNAMIC_INTF_LIST" + elif test "x$enableval" = "xstatic" || test "x$enableval" = "xyes"; then + AC_SUBST(INTF_LANPLUS,lanplus) + STATIC_INTF_LANPLUS="libintf_lanplus.la" + STATIC_INTF_LIST="lanplus $STATIC_INTF_LIST" + fi + + if test "x$enableval" != "xno"; then + AC_CHECK_LIB(ssl, EVP_aes_128_cbc, [], [ + echo "Error! The lanplus interface requires an SSL library with EVP_aes_128_cbc defined." + exit -1 + ]) + fi], + []) + +AC_SUBST(PLUGIN_INTF_LANPLUS) +AC_SUBST(STATIC_INTF_LANPLUS) +AC_SUBST(LDFLAGS_INTF_LANPLUS) + + + PLUGIN_INTF_OPEN= STATIC_INTF_OPEN= LDFLAGS_INTF_OPEN= @@ -248,6 +284,7 @@ AC_CONFIG_FILES([ipmitool.spec src/plugins/ipmi_intf_static.c src/plugins/Makefile src/plugins/lan/Makefile + src/plugins/lanplus/Makefile src/plugins/open/Makefile src/plugins/lipmi/Makefile]) diff --git a/ipmitool/include/ipmitool/helper.h b/ipmitool/include/ipmitool/helper.h index 0bc741f..7c17f1d 100644 --- a/ipmitool/include/ipmitool/helper.h +++ b/ipmitool/include/ipmitool/helper.h @@ -49,7 +49,7 @@ unsigned short str2val(const char * str, const struct valstr * vs); unsigned short buf2short(unsigned char * buf); uint32_t buf2long(unsigned char * buf); const char * buf2str(unsigned char * buf, int len); -void printbuf(unsigned char * buf, int len, char * desc); +void printbuf(const unsigned char * buf, int len, const char * desc); void signal_handler(int sig, void * handler); diff --git a/ipmitool/include/ipmitool/ipmi.h b/ipmitool/include/ipmitool/ipmi.h index 41616d1..d63632a 100644 --- a/ipmitool/include/ipmitool/ipmi.h +++ b/ipmitool/include/ipmitool/ipmi.h @@ -45,6 +45,17 @@ #define BUF_SIZE 256 +/* From table 13.16 of the IPMI v2 specification */ +#define IPMI_PAYLOAD_TYPE_IPMI 0x00 +#define IPMI_PAYLOAD_TYPE_SOL 0x01 +#define IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST 0x10 +#define IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE 0x11 +#define IPMI_PAYLOAD_TYPE_RAKP_1 0x12 +#define IPMI_PAYLOAD_TYPE_RAKP_2 0x13 +#define IPMI_PAYLOAD_TYPE_RAKP_3 0x14 +#define IPMI_PAYLOAD_TYPE_RAKP_4 0x15 + + extern int verbose; extern int csv_output; @@ -57,6 +68,56 @@ struct ipmi_rq { } msg; }; + +/* + * This is what the sendrcv_v2() function would take as an argument. The common case + * is for payload_type to be IPMI_PAYLOAD_TYPE_IPMI. + */ +struct ipmi_v2_payload { + unsigned short payload_length; + unsigned char payload_type; + + union { + + struct { + unsigned char rq_seq; + struct ipmi_rq * request; + } ipmi_request; + + struct { + unsigned char * message; + } sol_message; + + /* Only used internally by the lanplus interface */ + struct { + unsigned char * request; + } open_session_request; + + /* Only used internally by the lanplus interface */ + struct { + unsigned char * message; + } rakp_1_message; + + /* Only used internally by the lanplus interface */ + struct { + unsigned char * message; + } rakp_2_message; + + /* Only used internally by the lanplus interface */ + struct { + unsigned char * message; + } rakp_3_message; + + /* Only used internally by the lanplus interface */ + struct { + unsigned char * message; + } rakp_4_message; + + } payload; +}; + + + struct ipmi_rq_entry { struct ipmi_rq req; struct ipmi_intf * intf; @@ -66,34 +127,81 @@ struct ipmi_rq_entry { struct ipmi_rq_entry * next; }; + + + struct ipmi_rs { unsigned char ccode; unsigned char data[BUF_SIZE]; + + /* + * Looks like this is the length of the entire packet, including the RMCP + * stuff, then modified to be the length of the extra IPMI message data + */ int data_len; + struct { - unsigned char authtype; - uint32_t seq; - uint32_t id; - } session_hdr; - unsigned char msglen; - struct { - unsigned char rq_addr; - unsigned char netfn; - unsigned char rq_lun; - unsigned char rs_addr; - unsigned char rq_seq; - unsigned char rs_lun; - unsigned char cmd; - } header; + unsigned char authtype; + uint32_t seq; + uint32_t id; + unsigned char bEncrypted; /* IPMI v2 only */ + unsigned char bAuthenticated; /* IPMI v2 only */ + unsigned char payloadtype; /* IPMI v2 only */ + unsigned short msglen; /* This is the total length of the payload or + IPMI message. IPMI v2.0 requires this to + be 2 bytes. Not really used for much. */ + } session; + + + /* + * A union of the different possible payload meta-data + */ + union { + struct { + unsigned char rq_addr; + unsigned char netfn; + unsigned char rq_lun; + unsigned char rs_addr; + unsigned char rq_seq; + unsigned char rs_lun; + unsigned char cmd; + } ipmi_response; + struct { + unsigned char message_tag; + unsigned char rakp_return_code; + unsigned char max_priv_level; + unsigned int console_id; + unsigned int bmc_id; + unsigned char auth_alg; + unsigned char integrity_alg; + unsigned char crypt_alg; + } open_session_response; + struct { + unsigned char message_tag; + unsigned char rakp_return_code; + unsigned int console_id; + unsigned char bmc_rand[16]; /* Random number generated by the BMC */ + unsigned char bmc_guid[16]; + unsigned char key_exchange_auth_code[20]; + } rakp2_message; + struct { + unsigned char message_tag; + unsigned char rakp_return_code; + unsigned int console_id; + unsigned char integrity_check_value[20]; + } rakp4_message; + } payload; }; + + #define IPMI_NETFN_CHASSIS 0x0 #define IPMI_NETFN_BRIDGE 0x2 #define IPMI_NETFN_SE 0x4 #define IPMI_NETFN_APP 0x6 #define IPMI_NETFN_FIRMWARE 0x8 #define IPMI_NETFN_STORAGE 0xa -#define IPMI_NETFN_TRANSPORT 0xc +#define IPMI_NETFN_TRANSPORT 0xc #define IPMI_NETFN_SOL 0x34 #define IPMI_BMC_SLAVE_ADDR 0x20 diff --git a/ipmitool/include/ipmitool/ipmi_channel.h b/ipmitool/include/ipmitool/ipmi_channel.h index e041e55..4e69fc7 100644 --- a/ipmitool/include/ipmitool/ipmi_channel.h +++ b/ipmitool/include/ipmitool/ipmi_channel.h @@ -45,11 +45,6 @@ #define IPMI_GET_CHANNEL_AUTH_CAP 0x38 #define IPMI_GET_CHANNEL_ACCESS 0x41 #define IPMI_GET_CHANNEL_INFO 0x42 -#define IPMI_SET_USER_ACCESS 0x43 -#define IPMI_GET_USER_ACCESS 0x44 -#define IPMI_SET_USER_NAME 0x45 -#define IPMI_GET_USER_NAME 0x46 -#define IPMI_SET_USER_PASSWORD 0x47 #define IPMI_1_5_AUTH_TYPE_BIT_NONE 0x01 @@ -173,62 +168,6 @@ struct get_channel_access_rsp { } __attribute__ ((packed)); -struct get_user_access_rsp { -#if WORDS_BIGENDIAN - unsigned char __reserved1 : 2; - unsigned char max_user_ids : 6; - unsigned char __reserved2 : 2; - unsigned char enabled_user_ids : 6; - unsigned char __reserved3 : 2; - unsigned char fixed_user_ids : 6; - unsigned char __reserved4 : 1; - unsigned char callin_callback : 1; - unsigned char link_auth : 1; - unsigned char ipmi_messaging : 1; - unsigned char privilege_limit : 4; -#else - unsigned char max_user_ids : 6; - unsigned char __reserved1 : 2; - unsigned char enabled_user_ids : 6; - unsigned char __reserved2 : 2; - unsigned char fixed_user_ids : 6; - unsigned char __reserved3 : 2; - unsigned char privilege_limit : 4; - unsigned char ipmi_messaging : 1; - unsigned char link_auth : 1; - unsigned char callin_callback : 1; - unsigned char __reserved4 : 1; -#endif -} __attribute__ ((packed)); - -struct set_user_access_data { -#if WORDS_BIGENDIAN - unsigned char change_bits : 1; - unsigned char callin_callback : 1; - unsigned char link_auth : 1; - unsigned char ipmi_messaging : 1; - unsigned char channel : 4; - unsigned char __reserved1 : 2; - unsigned char user_id : 6; - unsigned char __reserved2 : 4; - unsigned char privilege_limit : 4; - unsigned char __reserved3 : 4; - unsigned char session_limit : 4; -#else - unsigned char channel : 4; - unsigned char ipmi_messaging : 1; - unsigned char link_auth : 1; - unsigned char callin_callback : 1; - unsigned char change_bits : 1; - unsigned char user_id : 6; - unsigned char __reserved1 : 2; - unsigned char privilege_limit : 4; - unsigned char __reserved2 : 4; - unsigned char session_limit : 4; - unsigned char __reserved3 : 4; -#endif -} __attribute__ ((packed)); - int ipmi_channel_main(struct ipmi_intf *, int, char **); void ipmi_get_channel_auth_cap(struct ipmi_intf * intf, unsigned char channel, unsigned char priv); diff --git a/ipmitool/include/ipmitool/ipmi_intf.h b/ipmitool/include/ipmitool/ipmi_intf.h index a815576..727b7dd 100644 --- a/ipmitool/include/ipmitool/ipmi_intf.h +++ b/ipmitool/include/ipmitool/ipmi_intf.h @@ -39,11 +39,12 @@ #include -#define IPMI_SESSION_AUTHTYPE_NONE 0x0 -#define IPMI_SESSION_AUTHTYPE_MD2 0x1 -#define IPMI_SESSION_AUTHTYPE_MD5 0x2 -#define IPMI_SESSION_AUTHTYPE_KEY 0x4 -#define IPMI_SESSION_AUTHTYPE_OEM 0x5 +#define IPMI_SESSION_AUTHTYPE_NONE 0x0 +#define IPMI_SESSION_AUTHTYPE_MD2 0x1 +#define IPMI_SESSION_AUTHTYPE_MD5 0x2 +#define IPMI_SESSION_AUTHTYPE_KEY 0x4 +#define IPMI_SESSION_AUTHTYPE_OEM 0x5 +#define IPMI_SESSION_AUTHTYPE_RMCP_PLUS 0x6 #define IPMI_SESSION_PRIV_CALLBACK 0x1 #define IPMI_SESSION_PRIV_USER 0x2 @@ -51,21 +52,71 @@ #define IPMI_SESSION_PRIV_ADMIN 0x4 #define IPMI_SESSION_PRIV_OEM 0x5 +/* + * An enumeration that describes every possible session state for + * an IPMIv2 / RMCP+ session. + */ +enum LANPLUS_SESSION_STATE { + LANPLUS_STATE_PRESESSION = 0, + LANPLUS_STATE_OPEN_SESSION_SENT, + LANPLUS_STATE_OPEN_SESSION_RECEIEVED, + LANPLUS_STATE_RAKP_1_SENT, + LANPLUS_STATE_RAKP_2_RECEIVED, + LANPLUS_STATE_RAKP_3_SENT, + LANPLUS_STATE_ACTIVE, + LANPLUS_STATE_CLOSE_SENT, +}; + + +#define IPMI_AUTHCODE_BUFFER_SIZE 16 +#define IPMI_SIK_BUFFER_SIZE 20 +#define IPMI_KG_BUFFER_SIZE 21 /* key plus null byte */ + struct ipmi_session { unsigned char hostname[64]; unsigned char username[16]; - unsigned char authcode[16]; + unsigned char authcode[IPMI_AUTHCODE_BUFFER_SIZE]; unsigned char challenge[16]; unsigned char authtype; unsigned char privlvl; int password; int port; int active; + uint32_t session_id; + uint32_t in_seq; uint32_t out_seq; + + /* + * This struct holds state data specific to IMPI v2 / RMCP+ sessions + */ + struct { + enum LANPLUS_SESSION_STATE session_state; + + /* These are the algorithms agreed upon for the session */ + unsigned char auth_alg; + unsigned char integrity_alg; + unsigned char crypt_alg; + unsigned char max_priv_level; + + uint32_t console_id; + uint32_t bmc_id; + + /* Values required for RAKP mesages */ + unsigned char console_rand[16]; /* Random number generated byt the console */ + unsigned char bmc_rand[16]; /* Random number generated by the BMC */ + unsigned char bmc_guid[16]; + unsigned char requested_role; /* As sent in the RAKP 1 message */ + unsigned char rakp2_return_code; + unsigned char sik[IPMI_SIK_BUFFER_SIZE]; /* Session integrity key */ + unsigned char kg[IPMI_KG_BUFFER_SIZE]; /* BMC key */ + unsigned char k1[20]; /* Used for Integrity checking? */ + unsigned char k2[20]; /* First 16 bytes used for AES */ + } v2_data; }; + struct ipmi_intf { int fd; int opened; @@ -74,6 +125,7 @@ struct ipmi_intf { int (*open)(struct ipmi_intf *); void (*close)(struct ipmi_intf *); struct ipmi_rs *(*sendrecv)(struct ipmi_intf *, struct ipmi_rq *); + struct ipmi_rs *(*sendrecv_v2)(struct ipmi_intf *, struct ipmi_v2_payload *); struct ipmi_session * session; }; diff --git a/ipmitool/include/ipmitool/md5.h b/ipmitool/include/ipmitool/md5.h new file mode 100644 index 0000000..5bdfd9a --- /dev/null +++ b/ipmitool/include/ipmitool/md5.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.1 2004/05/25 20:53:49 iceblink Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + + +/* + * multi-session authcode generation for md5 + * H(password + session_id + msg + session_seq + password) + */ +unsigned char * ipmi_auth_md5(unsigned char * data, int data_len); + + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/ipmitool/lib/helper.c b/ipmitool/lib/helper.c index be7c4ee..b2a25aa 100644 --- a/ipmitool/lib/helper.c +++ b/ipmitool/lib/helper.c @@ -69,7 +69,7 @@ const char * buf2str(unsigned char * buf, int len) return (const char *)str; } -void printbuf(unsigned char * buf, int len, char * desc) +void printbuf(const unsigned char * buf, int len, const char * desc) { int i; diff --git a/ipmitool/lib/ipmi_channel.c b/ipmitool/lib/ipmi_channel.c index f070e6e..8941872 100644 --- a/ipmitool/lib/ipmi_channel.c +++ b/ipmitool/lib/ipmi_channel.c @@ -51,7 +51,6 @@ #include #include -void printf_channel_usage (void); const struct valstr ipmi_authtype_vals[] = { { IPMI_1_5_AUTH_TYPE_BIT_NONE, "NONE" }, @@ -175,7 +174,7 @@ void ipmi_get_channel_auth_cap(struct ipmi_intf * intf, } memcpy(&auth_cap, rsp->data, sizeof(struct get_channel_auth_cap_rsp)); - + printbuf(rsp->data, sizeof(struct get_channel_auth_cap_rsp), "the authcap buffer per sizeof"); printf("Channel number : %d\n", auth_cap.channel_number); @@ -252,6 +251,10 @@ ipmi_get_channel_info(struct ipmi_intf * intf, unsigned char channel) } + printbuf(rsp->data, rsp->data_len, "the info buffer per data_len"); + printbuf(rsp->data, sizeof(struct get_channel_info_rsp), "the info buffer per sizeof"); + + memcpy(&channel_info, rsp->data, sizeof(struct get_channel_info_rsp)); @@ -378,177 +381,57 @@ ipmi_get_channel_info(struct ipmi_intf * intf, unsigned char channel) static int ipmi_get_user_access(struct ipmi_intf * intf, unsigned char channel, unsigned char userid) { - struct ipmi_rs * rsp; - struct ipmi_rq req1, req2; - unsigned char rqdata[2]; - struct get_user_access_rsp user_access; - int curr_uid, max_uid, init = 1; - - ipmi_intf_session_set_privlvl(intf, IPMI_SESSION_PRIV_ADMIN); - - curr_uid = userid ? userid : 1; - - memset(&req1, 0, sizeof(req1)); - req1.msg.netfn = IPMI_NETFN_APP; - req1.msg.cmd = IPMI_GET_USER_ACCESS; - req1.msg.data = rqdata; - req1.msg.data_len = 2; - - memset(&req2, 0, sizeof(req2)); - req2.msg.netfn = IPMI_NETFN_APP; - req2.msg.cmd = IPMI_GET_USER_NAME; - req2.msg.data = rqdata; - req2.msg.data_len = 1; - - do - { - rqdata[0] = channel & 0xf; - rqdata[1] = curr_uid & 0x3f; - - rsp = intf->sendrecv(intf, &req1); - if (!rsp || rsp->ccode) { - printf("Error:%x Get User Access Command (0x%x)\n", - rsp ? rsp->ccode : 0, channel); - return -1; - } - - memcpy(&user_access, rsp->data, sizeof(struct get_user_access_rsp)); - - - rqdata[0] = curr_uid & 0x3f; - - rsp = intf->sendrecv(intf, &req2); - if (!rsp || rsp->ccode) { - printf("Error:%x Get User Name Command (0x%x)\n", - rsp ? rsp->ccode : 0, channel); - return -1; - } - - if (init) - { - printf("Maximum User IDs : %d\n", user_access.max_user_ids); - printf("Enabled User IDs : %d\n", user_access.enabled_user_ids); - max_uid = user_access.max_user_ids; - init = 0; - } - - printf("\nUser ID : %d\n", curr_uid); - printf("User Name : %s\n", rsp->data); - printf("Fixed Name : %s\n", curr_uid <= user_access.fixed_user_ids ? "Yes" : "No"); - printf("Access Available : %s\n", user_access.callin_callback ? "callback" : "call-in / callback"); - printf("Link Authentication : %sabled\n", user_access.link_auth ? "en" : "dis"); - printf("IPMI Messaging : %sabled\n", user_access.ipmi_messaging ? "en" : "dis"); - printf("Privilege Level : %s\n", val2str(user_access.privilege_limit, ipmi_privlvl_vals)); - - curr_uid ++; - - } while (!userid && curr_uid <= max_uid); - - return 0; -} - -static void -ipmi_set_user_access(struct ipmi_intf * intf, int argc, char ** argv) -{ - unsigned char channel, userid; struct ipmi_rs * rsp; struct ipmi_rq req; unsigned char rqdata[2]; - struct get_user_access_rsp user_access; - struct set_user_access_data set_access; - int i; ipmi_intf_session_set_privlvl(intf, IPMI_SESSION_PRIV_ADMIN); - if (argc < 3 || !strncmp(argv[0], "help", 4)) { - printf_channel_usage(); - return; - } - - channel = (unsigned char)strtol(argv[0], NULL, 0); - userid = (unsigned char)strtol(argv[1], NULL, 0); - memset(&req, 0, sizeof(req)); - req.msg.netfn = IPMI_NETFN_APP; - req.msg.cmd = IPMI_GET_USER_ACCESS; - req.msg.data = rqdata; - req.msg.data_len = 2; - rqdata[0] = channel & 0xf; rqdata[1] = userid & 0x3f; + + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x44; + req.msg.data = rqdata; + req.msg.data_len = 2; rsp = intf->sendrecv(intf, &req); if (!rsp || rsp->ccode) { printf("Error:%x Get User Access Command (0x%x)\n", rsp ? rsp->ccode : 0, channel); - return; + return -1; } - memcpy(&user_access, rsp->data, sizeof(struct get_user_access_rsp)); + printf("Maximum User IDs : %d\n", rsp->data[0] & 0x3f); + printf("Enabled User IDs : %d\n", rsp->data[1] & 0x3f); + printf("Fixed Name User IDs : %d\n", rsp->data[2] & 0x3f); + printf("Access Available : %s\n", (rsp->data[3] & 0x40) ? "callback" : "call-in / callback"); + printf("Link Authentication : %sabled\n", (rsp->data[3] & 0x20) ? "en" : "dis"); + printf("IPMI Messaging : %sabled\n", (rsp->data[3] & 0x10) ? "en" : "dis"); + printf("Privilege Level : %s\n", val2str(rsp->data[3] & 0x0f, ipmi_privlvl_vals)); - set_access.change_bits = 1; - set_access.callin_callback = user_access.callin_callback; - set_access.link_auth = user_access.link_auth; - set_access.ipmi_messaging = user_access.ipmi_messaging; - set_access.channel = channel; - set_access.user_id = userid; - set_access.privilege_limit = user_access.privilege_limit; - set_access.session_limit = 0; - - for (i = 2; i < argc; i ++) - { - if (!strncmp(argv[i], "callin=", 7)) { - set_access.callin_callback = !strncmp (argv[i]+7, "off", 3); - } - else if (!strncmp(argv[i], "link=", 5)) { - set_access.link_auth = strncmp (argv[i]+5, "off", 3); - } - else if (!strncmp(argv[i], "ipmi=", 5)) { - set_access.ipmi_messaging = strncmp (argv[i]+5, "off", 3); - } - else if (!strncmp(argv[i], "privilege=", 10)) { - set_access.privilege_limit = strtol (argv[i]+10, NULL, 0); - } - else { - printf ("Invalid option: %s\n", argv [i]); - return; - } - } - - memset(&req, 0, sizeof(req)); - req.msg.netfn = IPMI_NETFN_APP; - req.msg.cmd = IPMI_SET_USER_ACCESS; - req.msg.data = (unsigned char *) &set_access; - req.msg.data_len = 4; - - rsp = intf->sendrecv(intf, &req); - if (!rsp || rsp->ccode) { - printf("Error:%x Set User Access Command (0x%x)\n", - rsp ? rsp->ccode : 0, channel); - return; - } - - return; + return 0; } + void printf_channel_usage() { - printf("Channel Commands: authcap \n"); - printf(" getaccess [user id]\n"); - printf(" setaccess [callin=on|off] [ipmi=on|off] [link=on|off] [privilege=level]\n"); - printf(" info [channel number]\n"); + printf("Channel Commands: authcap \n"); + printf(" user [user id]\n"); + printf(" info [channel number]\n"); printf("\n"); - printf("Possible privilege levels are:\n"); + printf("Possible privelige levels are:\n"); printf(" 1 Callback level\n"); printf(" 2 User level\n"); printf(" 3 Operator level\n"); printf(" 4 Administrator level\n"); printf(" 5 OEM Proprietary level\n"); - printf(" 15 No access\n"); } + int ipmi_channel_main(struct ipmi_intf * intf, int argc, char ** argv) { @@ -567,22 +450,18 @@ ipmi_channel_main(struct ipmi_intf * intf, int argc, char ** argv) (unsigned char)strtol(argv[1], NULL, 0), (unsigned char)strtol(argv[2], NULL, 0)); } - else if (!strncmp(argv[0], "getaccess", 10)) + else if (!strncmp(argv[0], "user", 4)) { if (argc < 2 || argc > 3) printf_channel_usage(); else { unsigned char ch = (unsigned char)strtol(argv[1], NULL, 0); - unsigned char id = 0; + unsigned char id = 1; if (argc == 3) id = (unsigned char)strtol(argv[2], NULL, 0); ipmi_get_user_access(intf, ch, id); } } - else if (!strncmp(argv[0], "setaccess", 9)) - { - ipmi_set_user_access(intf, argc-1, &(argv[1])); - } else if (!strncmp(argv[0], "info", 4)) { if (argc > 2) diff --git a/ipmitool/src/plugins/Makefile.am b/ipmitool/src/plugins/Makefile.am index 451edb6..72647d2 100644 --- a/ipmitool/src/plugins/Makefile.am +++ b/ipmitool/src/plugins/Makefile.am @@ -38,8 +38,8 @@ INCLUDES = -I$(top_srcdir)/include $(INCLTDL) MAINTAINERCLEANFILES = Makefile.in ipmi_intf_static.c EXTRA_DIST = ipmi_intf_static.c.in -SUBDIRS = @INTF_LAN@ @INTF_OPEN@ @INTF_LIPMI@ -DIST_SUBDIRS = lan open lipmi +SUBDIRS = @INTF_LAN@ @INTF_LANPLUS@ @INTF_OPEN@ @INTF_LIPMI@ +DIST_SUBDIRS = lan lanplus open lipmi noinst_LTLIBRARIES = libintf.la libintf_la_SOURCES = ipmi_intf.c ipmi_intf_static.c diff --git a/ipmitool/src/plugins/lan/lan.c b/ipmitool/src/plugins/lan/lan.c index 3184881..3fb9189 100644 --- a/ipmitool/src/plugins/lan/lan.c +++ b/ipmitool/src/plugins/lan/lan.c @@ -464,51 +464,53 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) } x = 4; - rsp->session_hdr.authtype = rsp->data[x++]; - memcpy(&rsp->session_hdr.seq, rsp->data+x, 4); + rsp->session.authtype = rsp->data[x++]; + memcpy(&rsp->session.seq, rsp->data+x, 4); x += 4; - memcpy(&rsp->session_hdr.id, rsp->data+x, 4); + memcpy(&rsp->session.id, rsp->data+x, 4); x += 4; if (intf->session->active && intf->session->authtype) x += 16; - rsp->msglen = rsp->data[x++]; - rsp->header.rq_addr = rsp->data[x++]; - rsp->header.netfn = rsp->data[x] >> 2; - rsp->header.rq_lun = rsp->data[x++] & 0x3; + rsp->session.msglen = rsp->data[x++]; + rsp->payload.ipmi_response.rq_addr = rsp->data[x++]; + rsp->payload.ipmi_response.netfn = rsp->data[x] >> 2; + rsp->payload.ipmi_response.rq_lun = rsp->data[x++] & 0x3; x++; /* checksum */ - rsp->header.rs_addr = rsp->data[x++]; - rsp->header.rq_seq = rsp->data[x] >> 2; - rsp->header.rs_lun = rsp->data[x++] & 0x3; - rsp->header.cmd = rsp->data[x++]; + rsp->payload.ipmi_response.rs_addr = rsp->data[x++]; + rsp->payload.ipmi_response.rq_seq = rsp->data[x] >> 2; + rsp->payload.ipmi_response.rs_lun = rsp->data[x++] & 0x3; + rsp->payload.ipmi_response.cmd = rsp->data[x++]; rsp->ccode = rsp->data[x++]; if (verbose > 2) { - printbuf(rsp->data, x, "ipmi message header"); + printbuf(rsp->data, rsp->data_len, "ipmi message header"); printf("<< IPMI Response Session Header\n"); printf("<< Authtype : %s\n", - val2str(rsp->session_hdr.authtype, ipmi_authtype_session_vals)); - printf("<< Sequence : 0x%08lx\n", rsp->session_hdr.seq); - printf("<< Session ID : 0x%08lx\n", rsp->session_hdr.id); + val2str(rsp->session.authtype, ipmi_authtype_session_vals)); + printf("<< Sequence : 0x%08lx\n", rsp->session.seq); + printf("<< Session ID : 0x%08lx\n", rsp->session.id); printf("<< IPMI Response Message Header\n"); - printf("<< Rq Addr : %02x\n", rsp->header.rq_addr); - printf("<< NetFn : %02x\n", rsp->header.netfn); - printf("<< Rq LUN : %01x\n", rsp->header.rq_lun); - printf("<< Rs Addr : %02x\n", rsp->header.rs_addr); - printf("<< Rq Seq : %02x\n", rsp->header.rq_seq); - printf("<< Rs Lun : %01x\n", rsp->header.rs_lun); - printf("<< Command : %02x\n", rsp->header.cmd); + printf("<< Rq Addr : %02x\n", rsp->payload.ipmi_response.rq_addr); + printf("<< NetFn : %02x\n", rsp->payload.ipmi_response.netfn); + printf("<< Rq LUN : %01x\n", rsp->payload.ipmi_response.rq_lun); + printf("<< Rs Addr : %02x\n", rsp->payload.ipmi_response.rs_addr); + printf("<< Rq Seq : %02x\n", rsp->payload.ipmi_response.rq_seq); + printf("<< Rs Lun : %01x\n", rsp->payload.ipmi_response.rs_lun); + printf("<< Command : %02x\n", rsp->payload.ipmi_response.cmd); printf("<< Compl Code : 0x%02x\n", rsp->ccode); } /* now see if we have oustanding entry in request list */ - entry = ipmi_req_lookup_entry(rsp->header.rq_seq, rsp->header.cmd); + entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); if (entry) { if (verbose > 2) printf("IPMI Request Match found\n"); - ipmi_req_remove_entry(rsp->header.rq_seq, rsp->header.cmd); + ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); } else { if (verbose) printf("WARNING: IPMI Request Match NOT FOUND!\n"); @@ -537,11 +539,11 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) * | rmcp.seq | * | rmcp.class | * +--------------------+ - * | session_hdr.authtype | 9 bytes - * | session_hdr.seq | - * | session_hdr.id | + * | session.authtype | 9 bytes + * | session.seq | + * | session.id | * +--------------------+ - * | [session_hdr.authcode] | 16 bytes (AUTHTYPE != none) + * | [session.authcode] | 16 bytes (AUTHTYPE != none) * +--------------------+ * | message length | 1 byte * +--------------------+ diff --git a/ipmitool/src/plugins/lanplus/Makefile.am b/ipmitool/src/plugins/lanplus/Makefile.am new file mode 100644 index 0000000..14e43f5 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/Makefile.am @@ -0,0 +1,47 @@ +# Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Sun Microsystems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +# +# You acknowledge that this software is not designed or intended for use +# in the design, construction, operation or maintenance of any nuclear +# facility. + +MAINTAINERCLEANFILES = Makefile.in + +plugindir = $(pluginpath) + +INCLUDES = -I$(top_srcdir)/include + +EXTRA_LTLIBRARIES = libintf_lanplus.la +plugin_LTLIBRARIES = @PLUGIN_INTF_LANPLUS@ +noinst_LTLIBRARIES = @STATIC_INTF_LANPLUS@ +libintf_lanplus_la_LDFLAGS = @LDFLAGS_INTF_LANPLUS@ +libintf_lanplus_la_LIBADD = $(top_srcdir)/lib/libipmitool.la +libintf_lanplus_la_SOURCES = lanplus.c lanplus.h rmcp.h lanplus_crypt.h lanplus_crypt.c lanplus_strings.c lanplus_dump.h lanplus_dump.c lanplus_crypt_impl.h lanplus_crypt_impl.c + diff --git a/ipmitool/src/plugins/lanplus/README.lanplus b/ipmitool/src/plugins/lanplus/README.lanplus new file mode 100644 index 0000000..4406d5b --- /dev/null +++ b/ipmitool/src/plugins/lanplus/README.lanplus @@ -0,0 +1,74 @@ +This interface exists to provide a means of connecting to an IPMIv2 enabled +BMC. In some places, the IPMIv2 specification is either unclear or +inconsistent, and interpretations of the intent of the specification had to +be made at the discretion of the implementor. The purpose of this +document is to make those decisions clear so that 1) they can be reviewed +by others and 2) so that the rationale for those decisions can be made +clear. + +* Though it's not stated explicitly with which algorithm the K1 and K2 keys +should be generated, we chose to use the authentication algorithm. The +specification states that K1 and K2 are generated with an HMAC algorithm, +and all of the authentication algorithms (except for "none") are HMAC +algorithms, whereas the integrity algorithms are not all HMAC. See section +13.32 for details about K1 and K2, and section + + +* The IPMIv2 specification describes a key, Kg, that is the "BMC key". +This key functions as a global key that is required to be known in addition +to the user's key, by authenticating users. If the BMC has a null Kg, the +users key, Kuid, is used in its place in algorithms where Kg is required, +per the specification section 13.33. A user can obtain the status of Kg by +querying the BMC with the Get Channel Authentication Capabilities command. +Currently, this implementation does not provide a way for a user to specify +Kg for BMCs that require it. + + +* The specification is unclear as to which key is used for HMAC based +integrity checking. One the one hand, section 13.28.4 states explicitly +that HMAC integrity algorithms use the session integrity key as the HMAC +key. Confusing that matter is a statement in section 13.32 regarding the +creation of additional keying material. In this section it is stated that +"all keying material for the RSP integrity and confidentiality algorithms +will be generated by processing a pre-defined set of constants using HMAC +per [RFC2104], keyed by sik". And "For the mandatory-to-implement +integrity and confidentiality algorithms defined in this specification, +processing the first two (2) constants will generate the require amount of +keying material." We decided to use K1 as our HMAC key for the generation +of authentication codes (integrity checking). Furthermore, we are using +all 20 bytes of K1. + + +* IPMIv2 compliant BMCs are supposed to support 20 byte passwords, as well +store metadata describing whether the password was stored as a 16 byte or +20 byte class password. We do not currently support 20 byte passwords. It +should be noted that there are obvious mistakes in the SET USER PASSWORD +command specification, as it mentions the ability to query for 16/20 byte +password status, but the packet format does not support this. + + +* The IPMIv2 specification describes a type of login called a "role only +login." This feature allows a user to login providing only a requested +privilege level and a password. We do not currently support this feature. +Supporting this feature would only require the ability to specify +username/privilege lookups in the RAKP 1 message sent from ipmitool. We +currently specify the use of username only lookups for authentication. + + +* In the IPMIv2 packet description in table 13-8 of the IPMv2 +specification, there are two fields that are rather ambiguous in meaning. +The fields are "Pad Length" and "Next Header". Although neither field is +listed as belonging to the IPMIv2 packet format, we include/expect them +both in our IPMIv2 packets. Are rationale is 1) the Next Headers field's +comment states what the value of that field should be for IPMIv2, and 2) +for the most part the ASF and IPMIv2 fields seem to parallel each other, +and we feel that the Pad Length and Next Header fields were left out of the +IPMIv2 column by mistake. + + +* The GET CHANNEL CIPHER SUITES command documentation seems to have +mistakes. The "start of record" byte is stated to be either 0x30 or 0x31, +whereas the detailed description in table 22-18 leads us to believe that +this byte should really be 0xC0 or 0xC1. Also the description of bits 5:0 +in the start of record byte should probably be 00_0000 rather than 00_000. + diff --git a/ipmitool/src/plugins/lanplus/asf.h b/ipmitool/src/plugins/lanplus/asf.h new file mode 100644 index 0000000..6942a08 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/asf.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#ifndef IPMI_ASF_H +#define IPMI_ASF_H + +#include +#include "lanplus.h" + +#define ASF_RMCP_IANA 0x000011be + +#define ASF_TYPE_PING 0x80 +#define ASF_TYPE_PONG 0x40 + +static const struct valstr asf_type_vals[] __attribute__((unused)) = { + { 0x10, "Reset" }, + { 0x11, "Power-up" }, + { 0x12, "Unconditional Power-down" }, + { 0x13, "Power Cycle" }, + { 0x40, "Presence Pong" }, + { 0x41, "Capabilities Response" }, + { 0x42, "System State Response" }, + { 0x80, "Presence Ping" }, + { 0x81, "Capabilities Request" }, + { 0x82, "System State Request" }, + { 0x00, NULL } +}; + +/* ASF message header */ +struct asf_hdr { + unsigned long iana; + unsigned char type; + unsigned char tag; + unsigned char __reserved; + unsigned char len; +} __attribute__((packed)); + +int handle_asf(struct ipmi_intf * intf, unsigned char * data, int data_len); + +#endif /* IPMI_ASF_H */ diff --git a/ipmitool/src/plugins/lanplus/lanplus.c b/ipmitool/src/plugins/lanplus/lanplus.c new file mode 100644 index 0000000..2da8aaa --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus.c @@ -0,0 +1,2720 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_dump.h" +#include "rmcp.h" +#include "asf.h" + +extern const struct valstr ipmi_rakp_return_codes[]; +extern const struct valstr ipmi_priv_levels[]; +extern const struct valstr ipmi_auth_algorithms[]; +extern const struct valstr ipmi_integrity_algorithms[]; +extern const struct valstr ipmi_encryption_algorithms[]; + +static struct ipmi_rq_entry * ipmi_req_entries; +static struct ipmi_rq_entry * ipmi_req_entries_tail; + + + +static int recv_timeout = IPMI_LAN_TIMEOUT; +static sigjmp_buf jmpbuf; + + +static int ipmi_lan_send_packet(struct ipmi_intf * intf, unsigned char * data, int data_len); +static struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); +static struct ipmi_rs * ipmi_lanplus_send_payload(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); +void getIpmiPayloadWireRep(unsigned char * out, + struct ipmi_rq * req, + unsigned char rq_seq); +static void read_open_session_response(struct ipmi_rs * rsp, int offset); +static void read_rakp2_message(struct ipmi_rs * rsp, int offset, unsigned char alg); +static void read_rakp4_message(struct ipmi_rs * rsp, int offset, unsigned char alg); +static void read_session_data(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v15(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v2x(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_ipmi_response(struct ipmi_rs * rsp, int * offset); + + + +struct ipmi_intf ipmi_lanplus_intf = { + .open = ipmi_lanplus_open, + .close = ipmi_lanplus_close, + .sendrecv = ipmi_lanplus_send_ipmi_cmd, + .sendrecv_v2 = ipmi_lanplus_send_payload, +}; + + +int verbose; + + +/* + * Reverse the order of arbitrarily long strings of bytes + */ +void lanplus_swap(unsigned char * buffer, + int length) +{ + int i; + unsigned char temp; + + for (i =0; i < length/2; ++i) + { + temp = buffer[i]; + buffer[i] = buffer[length - 1 - i]; + buffer[length - 1 - i] = temp; + } +} + + + +static void +query_alarm(int signo) +{ + siglongjmp(jmpbuf, 1); +} + + +static const struct valstr ipmi_channel_protocol_vals[] = { + { 0x00, "reserved" }, + { 0x01, "IPMB-1.0" }, + { 0x02, "ICMB-1.0" }, + { 0x03, "reserved" }, + { 0x04, "IPMI-SMBus" }, + { 0x05, "KCS" }, + { 0x06, "SMIC" }, + { 0x07, "BT-10" }, + { 0x08, "BT-15" }, + { 0x09, "TMode" }, + { 0x1c, "OEM 1" }, + { 0x1d, "OEM 2" }, + { 0x1e, "OEM 3" }, + { 0x1f, "OEM 4" }, + { 0x00, NULL }, +}; + +static const struct valstr ipmi_channel_medium_vals[] = { + { 0x00, "reserved" }, + { 0x01, "IPMB (I2C)" }, + { 0x02, "ICMB v1.0" }, + { 0x03, "ICMB v0.9" }, + { 0x04, "802.3 LAN" }, + { 0x05, "Serial/Modem" }, + { 0x06, "Other LAN" }, + { 0x07, "PCI SMBus" }, + { 0x08, "SMBus v1.0/v1.1" }, + { 0x09, "SMBus v2.0" }, + { 0x0a, "USB 1.x" }, + { 0x0b, "USB 2.x" }, + { 0x0c, "System Interface" }, + { 0x00, NULL }, +}; + +static struct ipmi_rq_entry * +ipmi_req_lookup_entry(unsigned char seq, unsigned char cmd) +{ + struct ipmi_rq_entry * e = ipmi_req_entries; + while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { + if (e == e->next) + return NULL; + e = e->next; + } + return e; +} + +static void +ipmi_req_remove_entry(unsigned char seq, unsigned char cmd) +{ + struct ipmi_rq_entry * p, * e; + + e = p = ipmi_req_entries; + + while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { + p = e; + e = e->next; + } + if (e) { + if (verbose > 3) + printf("removed list entry seq=0x%02x cmd=0x%02x\n", seq, cmd); + p->next = (p->next == e->next) ? NULL : e->next; + if (ipmi_req_entries == e) { + if (ipmi_req_entries != p) + ipmi_req_entries = p; + else + ipmi_req_entries = NULL; + } + if (ipmi_req_entries_tail == e) { + if (ipmi_req_entries_tail != p) + ipmi_req_entries_tail = p; + else + ipmi_req_entries_tail = NULL; + } + if (e->msg_data) + free(e->msg_data); + free(e); + } +} + +static void +ipmi_req_clear_entries(void) +{ + struct ipmi_rq_entry * p, * e; + + e = ipmi_req_entries; + while (e) { + if (verbose > 3) + printf("cleared list entry seq=0x%02x cmd=0x%02x\n", + e->rq_seq, e->req.msg.cmd); + p = e->next; + free(e); + e = p; + } +} + +static int +get_random(void *data, unsigned int len) +{ + int fd = open("/dev/random", O_RDONLY); + int rv; + + if (fd == -1) + return errno; + + rv = read(fd, data, len); + + close(fd); + return rv; +} + + + +int ipmi_lan_send_packet(struct ipmi_intf * intf, unsigned char * data, int data_len) +{ + if (verbose >= 2) + printbuf(data, data_len, ">> sending packet"); + + return send(intf->fd, data, data_len, 0); +} + + + +struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf) +{ + static struct ipmi_rs rsp; + int rc; + + /* setup alarm timeout */ + if (sigsetjmp(jmpbuf, 1) != 0) { + alarm(0); + return NULL; + } + + alarm(recv_timeout); + rc = recv(intf->fd, &rsp.data, BUF_SIZE, 0); + alarm(0); + + /* the first read may return ECONNREFUSED because the rmcp ping + * packet--sent to UDP port 623--will be processed by both the + * BMC and the OS. + * + * The problem with this is that the ECONNREFUSED takes + * priority over any other received datagram; that means that + * the Connection Refused shows up _before_ the response packet, + * regardless of the order they were sent out. (unless the + * response is read before the connection refused is returned) + */ + if (rc < 0) { + alarm(recv_timeout); + rc = recv(intf->fd, &rsp.data, BUF_SIZE, 0); + alarm(0); + if (rc < 0) { + perror("recv failed"); + return NULL; + } + } + rsp.data[rc] = '\0'; + rsp.data_len = rc; + + if (verbose >= 2) + { + printbuf(rsp.data, rsp.data_len, "<< Received data"); + } + + return &rsp; +} + + + +/* + * parse response RMCP "pong" packet + * + * return -1 if ping response not received + * returns 0 if IPMI is NOT supported + * returns 1 if IPMI is supported + * + * udp.source = 0x026f // RMCP_UDP_PORT + * udp.dest = ? // udp.source from rmcp-ping + * udp.len = ? + * udp.check = ? + * rmcp.ver = 0x06 // RMCP Version 1.0 + * rmcp.__res = 0x00 // RESERVED + * rmcp.seq = 0xff // no RMCP ACK + * rmcp.class = 0x06 // RMCP_CLASS_ASF + * asf.iana = 0x000011be // ASF_RMCP_IANA + * asf.type = 0x40 // ASF_TYPE_PONG + * asf.tag = ? // asf.tag from rmcp-ping + * asf.__res = 0x00 // RESERVED + * asf.len = 0x10 // 16 bytes + * asf.data[3:0]= 0x000011be // IANA# = RMCP_ASF_IANA if no OEM + * asf.data[7:4]= 0x00000000 // OEM-defined (not for IPMI) + * asf.data[8] = 0x81 // supported entities + * // [7]=IPMI [6:4]=RES [3:0]=ASF_1.0 + * asf.data[9] = 0x00 // supported interactions (reserved) + * asf.data[f:a]= 0x000000000000 + */ +static int +ipmi_handle_pong(struct ipmi_intf * intf, struct ipmi_rs * rsp) +{ + struct rmcp_pong { + struct rmcp_hdr rmcp; + struct asf_hdr asf; + uint32_t iana; + uint32_t oem; + unsigned char sup_entities; + unsigned char sup_interact; + unsigned char reserved[6]; + } * pong; + + if (!rsp) + return -1; + + pong = (struct rmcp_pong *)rsp->data; + + if (verbose) + printf("Received IPMI/RMCP response packet: " + "IPMI%s Supported\n", + (pong->sup_entities & 0x80) ? "" : " NOT"); + + if (verbose > 1) + printf(" ASF Version %s\n" + " RMCP Version %s\n" + " RMCP Sequence %d\n" + " IANA Enterprise %d\n\n", + (pong->sup_entities & 0x01) ? "1.0" : "unknown", + (pong->rmcp.ver == 6) ? "1.0" : "unknown", + pong->rmcp.seq, + ntohl(pong->iana)); + + return (pong->sup_entities & 0x80) ? 1 : 0; +} + + +/* build and send RMCP presence ping packet + * + * RMCP ping + * + * udp.source = ? + * udp.dest = 0x026f // RMCP_UDP_PORT + * udp.len = ? + * udp.check = ? + * rmcp.ver = 0x06 // RMCP Version 1.0 + * rmcp.__res = 0x00 // RESERVED + * rmcp.seq = 0xff // no RMCP ACK + * rmcp.class = 0x06 // RMCP_CLASS_ASF + * asf.iana = 0x000011be // ASF_RMCP_IANA + * asf.type = 0x80 // ASF_TYPE_PING + * asf.tag = ? // ASF sequence number + * asf.__res = 0x00 // RESERVED + * asf.len = 0x00 + * + */ +int +ipmiv2_lan_ping(struct ipmi_intf * intf) +{ + struct asf_hdr asf_ping = { + .iana = ASF_RMCP_IANA, + .type = ASF_TYPE_PING, + }; + struct rmcp_hdr rmcp_ping = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_ASF, + .seq = 0xff, + }; + unsigned char * data; + int len = sizeof(rmcp_ping) + sizeof(asf_ping); + int rv; + + data = malloc(len); + memset(data, 0, len); + memcpy(data, &rmcp_ping, sizeof(rmcp_ping)); + memcpy(data+sizeof(rmcp_ping), &asf_ping, sizeof(asf_ping)); + + if (verbose) + printf("Sending IPMI/RMCP presence ping packet\n"); + + rv = ipmi_lan_send_packet(intf, data, len); + + free(data); + + if (rv < 0) { + printf("Unable to send IPMI presence ping packet\n"); + return -1; + } + + if (!ipmi_lan_poll_recv(intf)) + return 0; + + return 1; +} + + +/* special packet, no idea what it does */ +ipmiv2_lan_first(struct ipmi_intf * intf) +{ + unsigned char data[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x20, 0x18, 0xc8, 0xc2, 0x01, 0x01, 0x3c }; + ipmi_lan_send_packet(intf, data, 16); + return 0; +} + +static int +ipmi_lan_pedantic(struct ipmi_intf * intf) +{ + unsigned char data[10] = "dummy"; + ipmi_lan_send_packet(intf, data, 10); + return 0; +} + + + +/** + * + * ipmi_lan_poll_recv + * + * Receive whatever comes back. Ignore received packets that don't correspond + * to a request we've sent. + * + * Returns: the ipmi_rs packet describing the/a reponse we expect. + */ +static struct ipmi_rs * +ipmi_lan_poll_recv(struct ipmi_intf * intf) +{ + struct rmcp_hdr rmcp_rsp; + struct ipmi_rs * rsp; + struct ipmi_session * session = intf->session; + + int offset, rv; + unsigned short payload_size; + + rsp = ipmi_lan_recv_packet(intf); + + /* + * Not positive why we're looping. Do we sometimes get stuff we don't + * expect? + */ + while (rsp) { + + /* parse response headers */ + memcpy(&rmcp_rsp, rsp->data, 4); + + if (rmcp_rsp.class == RMCP_CLASS_ASF) { + /* might be ping response packet */ + rv = ipmi_handle_pong(intf, rsp); + return (rv <= 0) ? NULL : rsp; + } + + if (rmcp_rsp.class != RMCP_CLASS_IPMI) { + printf("Invalid RMCP class: %x\n", rmcp_rsp.class); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + + /* + * The authtype / payload type determines what we are receiving + */ + offset = 4; + + + /*----------------------------------------------------------------------- + * + * The current packet could be one of several things: + * + * 1) An IPMI 1.5 packet (the response to our GET CHANNEL AUTHENTICATION + * CAPABILITIES request) + * 2) An RMCP+ message with an IPMI reponse payload + * 3) AN RMCP+ open session response + * 4) An RAKP-2 message (response to an RAKP 1 message) + * 5) An RAKP-4 message (response to an RAKP 3 message) + * 6) An Invalid packet (one that doesn't match a request) + * ---------------------------------------------------------------------- + */ + read_session_data(rsp, &offset, intf->session); + + + if (! lanplus_has_valid_auth_code(rsp, intf->session)) + { + printf("ERROR: Received message with invalid authcode!\n"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + + if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.bEncrypted)) + + { + lanplus_decrypt_payload(session->v2_data.crypt_alg, + session->v2_data.k2, + rsp->data + offset, + rsp->session.msglen, + rsp->data + offset, + &payload_size); + } + else + payload_size = rsp->session.msglen; + + + /* + * Handle IPMI responses (case #1 and #2) -- all IPMI reponses + */ + if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI) + { + struct ipmi_rq_entry * entry; + int payload_start = offset; + int extra_data_length; + read_ipmi_response(rsp, &offset); + + if (verbose > 2) { + //printbuf(rsp->data, offset, "ipmi message header"); + printf("<< IPMI Response Session Header\n"); + printf("<< Authtype : %s\n", + val2str(rsp->session.authtype, ipmi_authtype_vals)); + printf("<< Payload type : 0x%x\n", rsp->session.payloadtype); + printf("<< Session ID : 0x%08lx\n", rsp->session.id); + printf("<< Sequence : 0x%08lx\n", rsp->session.seq); + + printf("<< IPMI Msg/Payload Length : %d\n", rsp->session.msglen); + printf("<< IPMI Response Message Header\n"); + printf("<< Rq Addr : %02x\n", rsp->payload.ipmi_response.rq_addr); + printf("<< NetFn : %02x\n", rsp->payload.ipmi_response.netfn); + printf("<< Rq LUN : %01x\n", rsp->payload.ipmi_response.rq_lun); + printf("<< Rs Addr : %02x\n", rsp->payload.ipmi_response.rs_addr); + printf("<< Rq Seq : %02x\n", rsp->payload.ipmi_response.rq_seq); + printf("<< Rs Lun : %01x\n", rsp->payload.ipmi_response.rs_lun); + printf("<< Command : %02x\n", rsp->payload.ipmi_response.cmd); + printf("<< Compl Code : 0x%02x\n", rsp->ccode); + } + + /* Are we expecting this packet? */ + entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + if (entry) { + if (verbose > 2) + printf("IPMI Request Match found\n"); + ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + } else { + printf("WARNING: IPMI Request Match NOT FOUND!\n"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + + /* + * Good packet. Shift response data to start of array. + * rsp->data becomes the variable length IPMI response data + * rsp->data_len becomes the length of that data + */ + extra_data_length = payload_size - (offset - payload_start) - 1; + if (rsp && extra_data_length) + { + rsp->data_len = extra_data_length; + memmove(rsp->data, rsp->data + offset, extra_data_length); + } + + break; + } + + + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE) + { + if (session->v2_data.session_state != LANPLUS_STATE_OPEN_SESSION_SENT) + { + printf("Error: Received an Unexpected Open Session Response\n"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_open_session_response(rsp, offset); + break; + } + + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) + { + if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT) + { + printf("Error: Received an Unexpected RAKP 2 message\n"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_rakp2_message(rsp, offset, session->v2_data.auth_alg); + break; + } + + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4) + { + if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT) + { + printf("Error: Received an Unexpected RAKP 4 message\n"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_rakp4_message(rsp, offset, session->v2_data.integrity_alg); + break; + } + + else + { + printf("Invalid RMCP+ payload type : 0x%x\n", rsp->session.payloadtype); + assert(0); + } + } + + return rsp; +} + + + +/* + * read_open_session_reponse + * + * Initialize the ipmi_rs from the IMPI 2.x open session response data. + * + * The offset should point to the first byte of the the Open Session Response + * payload when this function is called. + * + * param rsp [in/out] reading from the data and writing to the open_session_response + * section + * param offset [in] tells us where the Open Session Response payload starts + * + * returns 0 on success, 1 on error + */ +void read_open_session_response(struct ipmi_rs * rsp, int offset) +{ + unsigned int id; + + /* Message tag */ + rsp->payload.open_session_response.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.open_session_response.rakp_return_code = rsp->data[offset + 1]; + + /* Maximum privilege level */ + rsp->payload.open_session_response.max_priv_level = rsp->data[offset + 2]; + + /* Remote console session ID */ + memcpy(&(rsp->payload.open_session_response.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.open_session_response.console_id = + BSWAP_32(rsp->payload.open_session_response.console_id); + #endif + + /* BMC session ID */ + memcpy(&(rsp->payload.open_session_response.bmc_id), + rsp->data + offset + 8, + 4); + #if WORDS_BIGENDIAN + rsp->payload.open_session_response.bmc_id = + BSWAP_32(rsp->payload.open_session_response.bmc_id); + #endif + + /* And of course, our negotiated algorithms */ + rsp->payload.open_session_response.auth_alg = rsp->data[offset + 16]; + rsp->payload.open_session_response.integrity_alg = rsp->data[offset + 24]; + rsp->payload.open_session_response.crypt_alg = rsp->data[offset + 32]; +} + + + +/* + * read_rakp2_message + * + * Initialize the ipmi_rs from the IMPI 2.x RAKP 2 message + * + * The offset should point the first byte of the the RAKP 2 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp 2 + * section + * param offset [in] tells us where hte rakp2 payload starts + * param auth_alg [in] describes the authentication algorithm was agreed upon in + * the open session request/response phase. We need to know that here so + * that we know how many bytes (if any) to read fromt the packet. + * + * returns 0 on success, 1 on error + */ +void read_rakp2_message(struct ipmi_rs * rsp, int offset, unsigned char auth_alg) +{ + int i; + + /* Message tag */ + rsp->payload.rakp2_message.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.rakp2_message.rakp_return_code = rsp->data[offset + 1]; + + /* Console session ID */ + memcpy(&(rsp->payload.rakp2_message.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.rakp2_message.console_id = + BSWAP_32(rsp->payload.rakp2_message.console_id); + #endif + + /* BMC random number */ + memcpy(&(rsp->payload.rakp2_message.bmc_rand), + rsp->data + offset + 8, + 16); + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp2_message.bmc_rand, 16); + #endif + + /* BMC GUID */ + memcpy(&(rsp->payload.rakp2_message.bmc_guid), + rsp->data + offset + 24, + 16); + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp2_message.bmc_guid, 16); + #endif + + /* Key exchange authentication code */ + switch (auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + /* Nothing to do here */ + break; + + case IPMI_AUTH_RAKP_HMAC_SHA1: + /* We need to copy 20 bytes */ + for (i = 0; i < 20; ++i) + rsp->payload.rakp2_message.key_exchange_auth_code[i] = + rsp->data[offset + 40 + i]; + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp2_message.key_exchange_auth_code, 20); + #endif + break; + + case IPMI_AUTH_RAKP_HMAC_MD5: + printf("read_rakp2_message: no support for IPMI_AUTH_RAKP_HMAC_MD5\n"); + assert(0); + break; + } +} + + +/* + * read_rakp4_message + * + * Initialize the ipmi_rs from the IMPI 2.x RAKP 4 message + * + * The offset should point the first byte of the the RAKP 4 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp 4 + * section + * param offset [in] tells us where hte rakp4 payload starts + * param integrity_alg [in] describes the authentication algorithm was agreed upon + * in the open session request/response phase. We need to know that here so + * that we know how many bytes (if any) to read fromt the packet. + * + * returns 0 on success, 1 on error + */ +void read_rakp4_message(struct ipmi_rs * rsp, int offset, unsigned char integrity_alg) +{ + int i; + + /* Message tag */ + rsp->payload.rakp4_message.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.rakp4_message.rakp_return_code = rsp->data[offset + 1]; + + /* Console session ID */ + memcpy(&(rsp->payload.rakp4_message.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.rakp4_message.console_id = + BSWAP_32(rsp->payload.rakp_message.console_id); + #endif + + + /* Integrity check value */ + switch (integrity_alg) + { + case IPMI_INTEGRITY_NONE: + /* Nothing to do here */ + break; + + case IPMI_INTEGRITY_HMAC_SHA1_96: + /* We need to copy 12 bytes */ + for (i = 0; i < 12; ++i) + rsp->payload.rakp4_message.integrity_check_value[i] = + rsp->data[offset + 8 + i]; + + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp4_message.integrity_check_value, 12); + #endif + + break; + + case IPMI_INTEGRITY_HMAC_MD5_128: + case IPMI_INTEGRITY_MD5_128: + printf("read_rakp4_message: no support for integrity algorithm 0x%x\n", + integrity_alg); + assert(0); + break; + } +} + + + + +/* + * read_session_data + * + * Initialize the ipmi_rsp from the session data in the packet + * + * The offset should point the first byte of the the IPMI session when this + * function is called. + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to the + * end of the session when this function exits. + * param session holds our session state + */ +void read_session_data(struct ipmi_rs * rsp, int * offset, struct ipmi_session * s) +{ + /* We expect to read different stuff depending on the authtype */ + rsp->session.authtype = rsp->data[*offset]; + + if (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) + read_session_data_v2x(rsp, offset, s); + else + read_session_data_v15(rsp, offset, s); +} + + + +/* + * read_session_data_v2x + * + * Initialize the ipmi_rsp from the v2.x session header of the packet. + * + * The offset should point to the first byte of the the IPMI session when this + * function is called. When this function exits, offset will point to the + * start of payload. + * + * Should decrypt and perform integrity checking here? + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to the + * end of the session when this function exits. + * param s holds our session state + */ +void read_session_data_v2x(struct ipmi_rs * rsp, + int * offset, + struct ipmi_session * s) +{ + //rsp->session.authtype = rsp->data[*offset]; + rsp->session.authtype = rsp->data[(*offset)++]; + + rsp->session.bEncrypted = (rsp->data[*offset] & 0x80 ? 1 : 0); + rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0); + //++(*offset); + + + /* + * We don't yet support encrypted or authenticated messages + */ + //if (rsp->session.bEncrypted || rsp->session.bAuthenticated) + //{ + // printf("Encryted : %d, Authenticated : %d, not supported\n", + // rsp->session.bEncrypted, rsp->session.bAuthenticated); + // assert(0); + //} + + + /* Payload type */ + //rsp->session.payloadtype = rsp->data[(*offset)++]; + rsp->session.payloadtype = rsp->data[(*offset)++] & 0x3F; + + /* Session ID */ + memcpy(&rsp->session.id, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.id = BSWAP_32(rsp->session.id); + #endif + + + /* + * Verify that the session ID is what we think it should be + */ + if ((s->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (rsp->session.id != s->v2_data.console_id)) + { + printf("packet session id 0x%x does not match active session 0x%0x\n", + rsp->session.id, s->v2_data.console_id); + assert(0); + } + + + /* Ignored, so far */ + memcpy(&rsp->session.seq, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.seq = BSWAP_32(rsp->session.seq); + #endif + + memcpy(&rsp->session.msglen, rsp->data + *offset, 2); + *offset += 2; + #if WORDS_BIGENDIAN + rsp->session.msglen = BSWAP_32(rsp->session.msglen); + #endif +} + + + +/* + * read_session_data_v2x + * + * Initialize the ipmi_rsp from the v2.x session header of the packet. + * + * The offset should point to the first byte of the the IPMI session when this + * function is called. When this function exits, offset will point to the + * start of payload. + * + * This function decrypts the packet if encryption is enabled, and checks the + * authcode if there is a specified integrity algorithm. + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to the + * end of the session when this function exits. + * param s holds our session state + */ +void read_session_data_v2x2(struct ipmi_rs * rsp, + int * offset, + struct ipmi_session * s) +{ + rsp->session.authtype = rsp->data[*offset]; + + rsp->session.bEncrypted = (rsp->data[*offset] & 0x80 ? 1 : 0); + rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0); + ++(*offset); + + + /* Payload type */ + rsp->session.payloadtype = rsp->data[(*offset)++]; + + /* Session ID */ + memcpy(&rsp->session.id, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.id = BSWAP_32(rsp->session.id); + #endif + + + /* + * Verify that the session ID is what we think it should be + */ + if ((s->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (rsp->session.id != s->v2_data.console_id)) + { + printf("packet session id 0x%x does not match active session 0x%0x\n", + rsp->session.id, s->v2_data.console_id); + assert(0); + } + + + /* Ignored, so far */ + memcpy(&rsp->session.seq, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.seq = BSWAP_32(rsp->session.seq); + #endif + + + memcpy(&rsp->session.msglen, rsp->data + *offset, 2); + *offset += 2; + #if WORDS_BIGENDIAN + rsp->session.msglen = BSWAP_32(rsp->session.msglen); + #endif + + + /* Confidentiality Header */ + //if (rsp->session.bEncrypted) + //{ + /* + * Some work here involved in decrypting the payload + */ + //printf("ERROR. We don't support encryption"); + //assert(0); + + /* + * BTW, when we decyrpt our packet, we should put the decyrpted + * version in the same place it would be if the payload were + * not encrypted, so that we can keep our code simple, below. + */ + // } + + + /* PAYLOAD WOULD BE HERE */ + + + /* Confidentiality trailer */ + //if (rsp->session.bEncrypted) + //{ + /* + * Some work here involved in decrypting the payload + */ + // printf("ERROR: we don't support encryption"); + // assert(0); + //} + + + /* + * Session Trailer + */ + //if (rsp->session.bEncrypted && (rsp->session.id != 0)) + //{ + /* + * Some work here involved in validating the packet + */ + // printf("ERROR: we don't support encryption"); + // assert(0); + //} +} + + + +/* + * read_session_data_v15 + * + * Initialize the ipmi_rsp from the session header of the packet. + * + * The offset should point the first byte of the the IPMI session when this + * function is called. When this function exits, the offset will point to + * the start of the IPMI message. + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to the + * end of the session when this function exits. + * param s holds our session state + */ +void read_session_data_v15(struct ipmi_rs * rsp, int * offset, struct ipmi_session * s) +{ + /* All v15 messages are IPMI messages */ + rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI; + + rsp->session.authtype = rsp->data[(*offset)++]; + + /* All v15 messages that we will receive are unencrypted/unauthenticated */ + rsp->session.bEncrypted = 0; + rsp->session.bAuthenticated = 0; + + /* skip the session id and sequence number fields */ + *offset += 8; + + /* This is the size of the whole payload */ + rsp->session.msglen = rsp->data[(*offset)++]; +} + + + +/* + * read_ipmi_response + * + * Initialize the impi_rs from with the IPMI response specific data + * + * The offset should point the first byte of the the IPMI payload when this + * function is called. + * + * param rsp [in/out] we read from the data buffer and populate the IPMI + * specific fields. + * param offset [in/out] should point to the beginning of the IPMI payload when + * this function is called. + */ +void read_ipmi_response(struct ipmi_rs * rsp, int * offset) +{ + /* + * The data here should be decrypted by now. + */ + rsp->payload.ipmi_response.rq_addr = rsp->data[(*offset)++]; + rsp->payload.ipmi_response.netfn = rsp->data[*offset] >> 2; + rsp->payload.ipmi_response.rq_lun = rsp->data[(*offset)++] & 0x3; + (*offset)++; /* checksum */ + rsp->payload.ipmi_response.rs_addr = rsp->data[(*offset)++]; + rsp->payload.ipmi_response.rq_seq = rsp->data[*offset] >> 2; + rsp->payload.ipmi_response.rs_lun = rsp->data[(*offset)++] & 0x3; + rsp->payload.ipmi_response.cmd = rsp->data[(*offset)++]; + rsp->ccode = rsp->data[(*offset)++]; + +} + + + +/* + * getIpmiPayloadWireRepOld + * + * Place the IPMI request in proper wire representation, starting at + * msg[len]. Encyrpt the request according to crypt_alg, the output + * includes the confidentiality header and and trailer if appropriate. + * + * param msg [out] will contain our wire representation + * param len [in, out] is our index, where we start writing + * param req [in] is the IPMI request to be written + * param crypt_alg [in] specifies the encryption to use + * param rq_seq [in] is the IPMI command sequence number. + */ +/* void getIpmiPayloadWireRepOld(char * msg, */ +/* int * len, */ +/* struct ipmi_rq * req, */ +/* unsigned char crypt_alg, */ +/* unsigned char * k2, */ +/* unsigned char rq_seq) */ +/* { */ +/* // Write our plaintext output to a buffer and call */ +/* // lanplus_encrypt. We should probably create our session */ +/* // header here -- with the IV. */ +/* // The confidentiality trailer is encrypted, so make room for it, too. */ + +/* int cs, mp, tmp; */ + +/* /\* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec *\/ */ +/* cs = mp = *len; */ + +/* /\* rsAddr *\/ */ +/* msg[(*len)++] = IPMI_BMC_SLAVE_ADDR; */ + +/* /\* net Fn *\/ */ +/* msg[(*len)++] = req->msg.netfn << 2; */ +/* tmp = *len - cs; */ + +/* /\* checkSum *\/ */ +/* msg[(*len)++] = ipmi_csum(msg+cs, tmp); */ +/* cs = (*len); */ + +/* /\* rqAddr *\/ */ +/* msg[(*len)++] = IPMI_REMOTE_SWID; */ + +/* /\* rqSeq / rqLUN *\/ */ +/* msg[(*len)++] = rq_seq << 2; */ + +/* /\* cmd *\/ */ +/* msg[(*len)++] = req->msg.cmd; */ + +/* /\* message data *\/ */ +/* if (req->msg.data_len) { */ +/* memcpy(msg + *len, */ +/* req->msg.data, */ +/* req->msg.data_len); */ +/* *len += req->msg.data_len; */ +/* } */ + +/* /\* second checksum *\/ */ +/* tmp = *len - cs; */ +/* msg[(*len)++] = ipmi_csum(msg+cs, tmp); */ + + +/* /\* Encrypt if necessary *\/ */ +/* if (crypt_alg != IPMI_CRYPT_NONE) */ +/* { */ +/* printf("unknown encryption type"); */ +/* assert(0); */ +/* } */ +/* } */ + + +/* + * getIpmiPayloadWireRep + * + * param out [out] will contain our wire representation + * param req [in] is the IPMI request to be written + * param crypt_alg [in] specifies the encryption to use + * param rq_seq [in] is the IPMI command sequence number. + */ +void getIpmiPayloadWireRep(unsigned char * msg, + struct ipmi_rq * req, + unsigned char rq_seq) +{ + int cs, mp, tmp, i; + + i = 0; + + /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ + cs = mp = i, + + /* rsAddr */ + msg[i++] = IPMI_BMC_SLAVE_ADDR; + + /* net Fn */ + msg[i++] = req->msg.netfn << 2; + tmp = i - cs; + + /* checkSum */ + msg[i++] = ipmi_csum(msg+cs, tmp); + cs = i; + + /* rqAddr */ + msg[i++] = IPMI_REMOTE_SWID; + + /* rqSeq / rqLUN */ + msg[i++] = rq_seq << 2; + + /* cmd */ + msg[i++] = req->msg.cmd; + + /* message data */ + if (req->msg.data_len) { + memcpy(msg + i, + req->msg.data, + req->msg.data_len); + i += req->msg.data_len; + } + + /* second checksum */ + tmp = i - cs; + msg[i++] = ipmi_csum(msg+cs, tmp); +} + + + +/* + * ipmi_lanplus_build_v2x_msg + * + * Encapsulates the payload data to create the IPMI v2.0 / RMCP+ packet. + * + * + * IPMI v2.0 LAN Request Message Format + * +----------------------+ + * | rmcp.ver | 4 bytes + * | rmcp.__reserved | + * | rmcp.seq | + * | rmcp.class | + * +----------------------+ + * | session.authtype | 10 bytes + * | session.payloadtype | + * | session.id | + * | session.seq | + * +----------------------+ + * | message length | 2 bytes + * +----------------------+ + * | Confidentiality Hdr | var (possibly absent) + * +----------------------+ + * | Paylod | var Payload + * +----------------------+ + * | Confidentiality Trlr | var (possibly absent) + * +----------------------+ + * | Integrity pad | var (possibly absent) + * +----------------------+ + * | Pad length | 1 byte (WTF?) + * +----------------------+ + * | Next Header | 1 byte (WTF?) + * +----------------------+ + * | Authcode | var (possibly absent) + * +----------------------+ + */ +void + ipmi_lanplus_build_v2x_msg(struct ipmi_intf * intf, /* in */ + struct ipmi_v2_payload * payload, /* in */ + int * msg_len, /* out */ + unsigned char ** msg_data) /* out */ +{ + unsigned int session_trailer_length = 0; + struct ipmi_session * session = intf->session; + struct rmcp_hdr rmcp = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_IPMI, + .seq = 0xff, + }; + + // msg will hold the entire message to be sent + unsigned char * msg; + unsigned short payloadLength; + + int cs, mp, ap = 0, len = 0, tmp, bytes_encrypted; + + + len = + sizeof(rmcp) + // RMCP Header (4) + 10 + // IPMI Session Header + 2 + // Message length + IPMI_MAX_PAYLOAD_SIZE + // The actual payload + IPMI_MAX_INTEGRITY_PAD_SIZE + // Integrity Pad + 1 + // Pad Length + 1 + // Next Header + IPMI_MAX_AUTH_CODE_SIZE; // Authcode + + + msg = malloc(len); + memset(msg, 0, len); + + /* + *------------------------------------------ + * RMCP HEADER + *------------------------------------------ + */ + memcpy(msg, &rmcp, sizeof(rmcp)); + len = sizeof(rmcp); + + + /* + *------------------------------------------ + * IPMI SESSION HEADER + *------------------------------------------ + */ + /* ipmi session Auth Type / Format is always 0x06 for IPMI v2 */ + msg[IMPI_LANPLUS_OFFSET_AUTHTYPE] = 0x06; + + /* Payload Type -- also specifies whether were authenticated/encyrpted */ + msg[IMPI_LANPLUS_OFFSET_PAYLOAD_TYPE] = payload->payload_type; + + + if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) + { + msg[IMPI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= + ((session->v2_data.crypt_alg != IPMI_CRYPT_NONE )? 0x80 : 0x00); + msg[IMPI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= + ((session->v2_data.auth_alg != IPMI_AUTH_RAKP_NONE)? 0x40 : 0x00); + } + + /* Session ID -- making it LSB */ + msg[IMPI_LANPLUS_OFFSET_SESSION_ID ] = session->v2_data.bmc_id & 0xff; + msg[IMPI_LANPLUS_OFFSET_SESSION_ID + 1] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[IMPI_LANPLUS_OFFSET_SESSION_ID + 2] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[IMPI_LANPLUS_OFFSET_SESSION_ID + 3] = (session->v2_data.bmc_id >> 24) & 0xff; + + + /* Sequence Number -- making it LSB */ + msg[IMPI_LANPLUS_OFFSET_SEQUENCE_NUM ] = session->out_seq & 0xff; + msg[IMPI_LANPLUS_OFFSET_SEQUENCE_NUM + 1] = (session->out_seq >> 8) & 0xff; + msg[IMPI_LANPLUS_OFFSET_SEQUENCE_NUM + 2] = (session->out_seq >> 16) & 0xff; + msg[IMPI_LANPLUS_OFFSET_SEQUENCE_NUM + 3] = (session->out_seq >> 24) & 0xff; + + + /* + * Payload Length is set below (we don't know how big the payload is until after + * encryption). + */ + + + /* + * Payload + * + * At this point we are ready to slam the payload in. + * This includes: + * 1) The confidentiality header + * 2) The payload proper (possibly encrypted) + * 3) The confidentiality trailer + * + */ + switch (payload->payload_type) + { + case IPMI_PAYLOAD_TYPE_IPMI: + //getIpmiPayloadWireRep(msg, /* out */ + // &len, /* in/out */ + // payload->payload.ipmi_request.request, /* in */ + // session->v2_data.crypt_alg, /* in */ + // session->v2_data.k2, /* in */ + // payload->payload.ipmi_request.rq_seq); /* in */ + getIpmiPayloadWireRep(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.ipmi_request.request, + payload->payload.ipmi_request.rq_seq); + break; + + case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.open_session_request.request, + payload->payload_length); + len += payload->payload_length; + break; + + case IPMI_PAYLOAD_TYPE_RAKP_1: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.rakp_1_message.message, + payload->payload_length); + len += payload->payload_length; + break; + + case IPMI_PAYLOAD_TYPE_RAKP_3: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.rakp_3_message.message, + payload->payload_length); + len += payload->payload_length; + break; + + case IPMI_PAYLOAD_TYPE_SOL: + default: + printf("unsupported payload type 0x%x\n", payload->payload_type); + assert(0); + break; + } + + + + /* + *------------------------------------------ + * ENCRYPT THE PAYLOAD IF NECESSARY + *------------------------------------------ + */ + if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) + { + /* Payload length is adjusted as necessary by lanplus_encrypt_payload */ + lanplus_encrypt_payload(session->v2_data.crypt_alg, /* input */ + session->v2_data.k2, /* input */ + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* input */ + payload->payload_length, /* input */ + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* output */ + &(payload->payload_length)); /* output */ + + } + + + /* Now we know the payload length */ + msg[IMPI_LANPLUS_OFFSET_PAYLOAD_SIZE ] = payload->payload_length & 0xff; + msg[IMPI_LANPLUS_OFFSET_PAYLOAD_SIZE + 1] = (payload->payload_length >> 8) & 0xff; + + + + /* + *------------------------------------------ + * SESSION TRAILER + *------------------------------------------ + */ + if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (session->v2_data.auth_alg != IPMI_AUTH_RAKP_NONE)) + { + unsigned int i, hmac_length, integrity_pad_size, hmac_input_size; + unsigned char * hmac_output; + unsigned int start_of_session_trailer = + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length; + + //printf("authcode sees payload size of %d\n", payload->payload_length); + + /* + * Determine the required integrity pad length. We have to make the + * data range covered by the authcode a multiple of 4. + */ + unsigned int length_before_authcode = + 12 + /* the stuff before the payload */ + payload->payload_length + + 1 + /* pad length field */ + 1; /* next header field */ + + + if (length_before_authcode % 4) + integrity_pad_size = 4 - (length_before_authcode % 4); + + for (i = 0; i < integrity_pad_size; ++i) + msg[start_of_session_trailer + i] = 0xFF; + + + /* Pad length */ + msg[start_of_session_trailer + integrity_pad_size] = integrity_pad_size; + + /* Next Header */ + msg[start_of_session_trailer + integrity_pad_size + 1] = + 0x07; /* Hardcoded per the spec, table 13-8 */ + + + hmac_input_size = + 12 + + payload->payload_length + + integrity_pad_size + + 2; + + hmac_output = + msg + + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length + + integrity_pad_size + + 2; + + if (verbose > 2) + printbuf(msg + IMPI_LANPLUS_OFFSET_AUTHTYPE, hmac_input_size, "authcode input"); + + + /* Auth Code */ + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.k1, /* key */ + 20, /* key length */ + msg + IMPI_LANPLUS_OFFSET_AUTHTYPE, /* hmac input */ + hmac_input_size, + hmac_output, + &hmac_length); + + assert(hmac_length == 20); + + if (verbose > 2) + printbuf(hmac_output, 12, "authcode output"); + + /* Set session_trailer_length appropriately */ + session_trailer_length = + integrity_pad_size + + 2 + /* pad length + next header */ + 12; /* Size of the authcode (we only use the first 12 bytes) */ + } + + + ++(session->out_seq); + if (!session->out_seq) + ++(session->out_seq); + + *msg_len = + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length + + session_trailer_length; + *msg_data = msg; +} + + + +/* + * ipmi_lanplus_build_v2x_ipmi_cmd + * + * Wraps ipmi_lanplus_build_v2x_msg and returns a new entry object for the + * command + * + */ +static struct ipmi_rq_entry * + ipmi_lanplus_build_v2x_ipmi_cmd(struct ipmi_intf * intf, + struct ipmi_rq * req) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_rq_entry * entry; + + /* + * We have a problem. we need to know the sequence number here, because we use it + * in our stored entry. But we also need to know the sequence number when we + * generate our IPMI representation far below. + */ + static unsigned char curr_seq = 0; + if (curr_seq >= 64) + curr_seq = 0; + + entry = malloc(sizeof(struct ipmi_rq_entry)); + memset(entry, 0, sizeof(struct ipmi_rq_entry)); + memcpy(&entry->req, req, sizeof(struct ipmi_rq)); + + entry->intf = intf; + entry->rq_seq = curr_seq; + + // Factor out + if (!ipmi_req_entries) + ipmi_req_entries = entry; + else + ipmi_req_entries_tail->next = entry; + ipmi_req_entries_tail = entry; + + + if (verbose > 3) + printf("added list entry seq=0x%02x cmd=0x%02x\n", + curr_seq, req->msg.cmd); + + + // Build our payload + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI; + v2_payload.payload_length = req->msg.data_len + 7; + v2_payload.payload.ipmi_request.request = req; + v2_payload.payload.ipmi_request.rq_seq = curr_seq; + + ipmi_lanplus_build_v2x_msg(intf, // in + &v2_payload, // in + &(entry->msg_len), // out + &(entry->msg_data)); // out + + return entry; +} + + + + + +/* + * IPMI LAN Request Message Format + * +--------------------+ + * | rmcp.ver | 4 bytes + * | rmcp.__reserved | + * | rmcp.seq | + * | rmcp.class | + * +--------------------+ + * | session.authtype | 9 bytes + * | session.seq | + * | session.id | + * +--------------------+ + * | [session.authcode] | 16 bytes (AUTHTYPE != none) + * +--------------------+ + * | message length | 1 byte + * +--------------------+ + * | message.rs_addr | 6 bytes + * | message.netfn_lun | + * | message.checksum | + * | message.rq_addr | + * | message.rq_seq | + * | message.cmd | + * +--------------------+ + * | [request data] | data_len bytes + * +--------------------+ + * | checksum | 1 byte + * +--------------------+ + */ +static struct ipmi_rq_entry * + ipmi_lanplus_build_v15_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req) +{ + struct rmcp_hdr rmcp = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_IPMI, + .seq = 0xff, + }; + unsigned char * msg; + int cs, mp, ap = 0, len = 0, tmp; + struct ipmi_session * session = intf->session; + struct ipmi_rq_entry * entry; + + entry = malloc(sizeof(struct ipmi_rq_entry)); + memset(entry, 0, sizeof(struct ipmi_rq_entry)); + memcpy(&entry->req, req, sizeof(struct ipmi_rq)); + + entry->intf = intf; + + // Can be factored out + if (!ipmi_req_entries) + ipmi_req_entries = entry; + else + ipmi_req_entries_tail->next = entry; + + ipmi_req_entries_tail = entry; + + len = req->msg.data_len + 21; + + msg = malloc(len); + memset(msg, 0, len); + + /* rmcp header */ + memcpy(msg, &rmcp, sizeof(rmcp)); + len = sizeof(rmcp); + + /* + * ipmi session header + */ + /* Authtype should always be none for 1.5 packets sent from this interface */ + msg[len++] = IPMI_SESSION_AUTHTYPE_NONE; + + msg[len++] = session->out_seq & 0xff; + msg[len++] = (session->out_seq >> 8) & 0xff; + msg[len++] = (session->out_seq >> 16) & 0xff; + msg[len++] = (session->out_seq >> 24) & 0xff; + + /* + * The session ID should be all zeroes for pre-session commands. We should only + * be using the 1.5 interface for the pre-session Get Channel Authentication + * Capabilities command + */ + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + /* message length */ + msg[len++] = req->msg.data_len + 7; + + /* ipmi message header */ + cs = mp = len; + msg[len++] = IPMI_BMC_SLAVE_ADDR; + msg[len++] = req->msg.netfn << 2; + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + cs = len; + msg[len++] = IPMI_REMOTE_SWID; + + entry->rq_seq = 0; + + msg[len++] = entry->rq_seq << 2; + msg[len++] = req->msg.cmd; + + if (verbose > 2) { + printf(">> IPMI Request Session Header\n"); + printf(">> Authtype : %s\n", val2str(IPMI_SESSION_AUTHTYPE_NONE, + ipmi_authtype_vals)); + printf(">> Sequence : 0x%08lx\n", session->out_seq); + printf(">> Session ID : 0x%08lx\n", 0); + + printf(">> IPMI Request Message Header\n"); + printf(">> Rs Addr : %02x\n", IPMI_BMC_SLAVE_ADDR); + printf(">> NetFn : %02x\n", req->msg.netfn); + printf(">> Rs LUN : %01x\n", 0); + printf(">> Rq Addr : %02x\n", IPMI_REMOTE_SWID); + printf(">> Rq Seq : %02x\n", entry->rq_seq); + printf(">> Rq Lun : %01x\n", 0); + printf(">> Command : %02x\n", req->msg.cmd); + } + + /* message data */ + if (req->msg.data_len) { + memcpy(msg+len, req->msg.data, req->msg.data_len); + len += req->msg.data_len; + } + + /* second checksum */ + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + + entry->msg_len = len; + entry->msg_data = msg; + + return entry; +} + + + +/* + * ipmi_lanplus_send_payload + * + */ +struct ipmi_rs * + ipmi_lanplus_send_payload(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload) +{ + struct ipmi_rs * rsp; + unsigned char * msg_data; + int msg_length; + struct ipmi_session * session = intf->session; + int try = 0; + + if (!intf->opened) { + intf->opened = 1; + if (intf->open(intf) < 0) { + intf->opened = 0; + return NULL; + } + } + + + if (payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) + { + /* + * Build an IPMI v1.5 or v2 command + */ + struct ipmi_rq_entry * entry; + struct ipmi_rq * ipmi_request = payload->payload.ipmi_request.request; + + /* + * If we are presession, and the command is GET CHANNEL AUTHENTICATION + * CAPABILITIES, we will build the command in v1.5 format. This is so + * that we can ask any server whether it supports IPMI v2 / RMCP+ before + * we attempt to open a v2.x session. + */ + if ((ipmi_request->msg.netfn == IPMI_NETFN_APP) && + (ipmi_request->msg.cmd == IPMI_GET_CHANNEL_AUTH_CAP) && + (session->v2_data.bmc_id == 0)) // jme - check + { + if (verbose) + printf("BUILDING A v1.5 COMMAND\n"); + entry = ipmi_lanplus_build_v15_ipmi_cmd(intf, ipmi_request); + } + else + { + if (verbose) + printf("BUILDING A v2 COMMAND\n"); + entry = ipmi_lanplus_build_v2x_ipmi_cmd(intf, ipmi_request); + } + + if (!entry) { + printf("Aborting send command, unable to build.\n"); + return NULL; + } + + msg_data = entry->msg_data; + msg_length = entry->msg_len; + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST) + { + if (verbose) + printf(">> SENDING AN OPEN SESSION REQUEST\n\n"); + assert(session->v2_data.session_state == LANPLUS_STATE_PRESESSION); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data); /* out */ + + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_1) + { + if (verbose) + printf(">> SENDING A RAKP 1 MESSAGE \n\n"); + assert(session->v2_data.session_state == LANPLUS_STATE_OPEN_SESSION_RECEIEVED); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data); /* out */ + + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_3) + { + if (verbose) + printf(">> SENDING A RAKP 3 MESSAGE \n\n"); + assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data); /* out */ + + } + + else + { + + printf("we dont yet support sending other payload types (0x%0x)!\n", + payload->payload_type); + assert(0); + } + + + while (try < IPMI_LAN_RETRY) { + if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) { + printf("ipmi_lan_send_cmd failed\n"); + return NULL; + } + + if (intf->pedantic) + ipmi_lan_pedantic(intf); + + usleep(100); + + + switch (payload->payload_type) + { + case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: + session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_SENT; + break; + case IPMI_PAYLOAD_TYPE_RAKP_1: + session->v2_data.session_state = LANPLUS_STATE_RAKP_1_SENT; + break; + case IPMI_PAYLOAD_TYPE_RAKP_3: + session->v2_data.session_state = LANPLUS_STATE_RAKP_3_SENT; + break; + } + + + + rsp = ipmi_lan_poll_recv(intf); + if (rsp) + break; + + usleep(5000); + try++; + } + + return rsp; +} + + + +/** + * ipmi_lanplus_send_ipmi_cmd + * + * Build a payload request and dispatch it. + */ +struct ipmi_rs * + ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req) +{ + struct ipmi_v2_payload v2_payload; + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI; + v2_payload.payload.ipmi_request.request = req; + + return ipmi_lanplus_send_payload(intf, &v2_payload); +} + + + + +/* + * ipmi_get_auth_capabilities_cmd + * + * This command may have to be sent twice. We first ask for the + * authentication capabilities with the "request IMPI v2 data bit" + * set. If this fails, we send the same command without that bit + * set. + * + * param intf is the initialized (but possibly) pre-session interface + * on which we will send the command + * param auth_cap [out] will be initialized to hold the Get Channel + * Authentication Capabilities return data on success. Its + * contents will be undefined on error. + * + * returns 0 on success + * non-zero if we were unable to contact the BMC, or we cannot + * get a successful response + * + */ +static int ipmi_get_auth_capabilities_cmd(struct ipmi_intf * intf, + struct get_channel_auth_cap_rsp * auth_cap) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data[2]; + + msg_data[0] = IPMI_LAN_CHANNEL_E | 0x80; // Ask for IPMI v2 data as well + msg_data[1] = intf->session->privlvl; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_AUTH_CAP; // 0x38 + req.msg.data = msg_data; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + + + 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 + */ + msg_data[0] &= 0x7F; + + rsp = intf->sendrecv(intf, &req); + + if (!rsp || rsp->ccode) { + if (rsp && verbose) + printf("Get Auth Capabilities error: %02x\n", rsp->ccode); + return 1; + } + } + + + memcpy(auth_cap, + rsp->data, + sizeof(struct get_channel_auth_cap_rsp)); + + + return 0; +} + + + +static int impi_close_session_cmd(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char msg_data[4]; + uint32_t bmc_session_lsbf; + + if (intf->session->v2_data.session_state != LANPLUS_STATE_ACTIVE) + return -1; + + + bmc_session_lsbf = intf->session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + bmc_session_lsbf = BSWAP_32(bmc_session_lsbf); + #endif + + memcpy(&msg_data, &bmc_session_lsbf, 4); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x3c; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (!rsp) { + printf("error in Close Session Command\n"); + return -1; + } + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "close_session"); + + if (rsp->ccode == 0x87) { + printf("Failed to Close Session: invalid session ID %08lx\n", + intf->session->v2_data.bmc_id); + return -1; + } + if (rsp->ccode) { + printf("Failed to Close Session: %x\n", rsp->ccode); + return -1; + } + + if (verbose > 1) + printf("\nClosed Session %08lx\n\n", intf->session->v2_data.bmc_id); + + return 0; +} + + + +/* + * ipmi_lanplus_open_session + * + * Build and send the open session command. See section 13.17 of the IPMI + * v2 specification for details. + */ +static int ipmi_lanplus_open_session(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + unsigned char * msg; + struct ipmi_rs * rsp; + int rc = 0; + + + /* + * Build an Open Session Request Payload + */ + msg = (unsigned char*)malloc(IPMI_OPEN_SESSION_REQUEST_SIZE); + memset(msg, 0, IPMI_OPEN_SESSION_REQUEST_SIZE); + + msg[0] = 0; /* Message tag */ + msg[1] = 0; /* Give us highest privlg level based on supported algorithms */ + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* Choose our session ID for easy recognition in the packet dump */ + session->v2_data.console_id = 0xA0A2A3A4; + msg[4] = session->v2_data.console_id & 0xff; + msg[5] = (session->v2_data.console_id >> 8) & 0xff; + msg[6] = (session->v2_data.console_id >> 16) & 0xff; + msg[7] = (session->v2_data.console_id >> 24) & 0xff; + + /* + * Authentication payload + */ + msg[8] = 0; /* specifies authentication payload */ + msg[9] = 0; /* reserved */ + msg[10] = 0; /* reserved */ + msg[11] = 8; /* payload length */ + msg[12] = IPMI_AUTH_RAKP_HMAC_SHA1; + msg[13] = 0; /* reserved */ + msg[14] = 0; /* reserved */ + msg[15] = 0; /* reserved */ + + /* + * Integrity payload + */ + msg[16] = 1; /* specifies integrity payload */ + msg[17] = 0; /* reserved */ + msg[18] = 0; /* reserved */ + msg[19] = 8; /* payload length */ + msg[20] = IPMI_INTEGRITY_HMAC_SHA1_96; + msg[21] = 0; /* reserved */ + msg[22] = 0; /* reserved */ + msg[23] = 0; /* reserved */ + + /* + * Confidentiality/Encryption payload + */ + msg[24] = 2; /* specifies confidentiality payload */ + msg[25] = 0; /* reserved */ + msg[26] = 0; /* reserved */ + msg[27] = 8; /* payload length */ + msg[28] = IPMI_CRYPT_AES_CBC_128; + msg[29] = 0; /* reserved */ + msg[30] = 0; /* reserved */ + msg[31] = 0; /* reserved */ + + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST; + v2_payload.payload_length = IPMI_OPEN_SESSION_REQUEST_SIZE; + v2_payload.payload.open_session_request.request = msg; + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + + if (verbose) + lanplus_dump_open_session_response(rsp); + + + if (rsp->payload.open_session_response.rakp_return_code != + IPMI_RAKP_STATUS_NO_ERRORS) + { + if (verbose) + printf("Error in open session response message : %s\n", + val2str(rsp->payload.open_session_response.rakp_return_code, + ipmi_rakp_return_codes)); + return rc = 1; + } + else + { + if (rsp->payload.open_session_response.console_id != + session->v2_data.console_id) + printf("Warning: Console session ID is not what we requested\n"); + + session->v2_data.max_priv_level = rsp->payload.open_session_response.max_priv_level; + session->v2_data.bmc_id = rsp->payload.open_session_response.bmc_id; + session->v2_data.auth_alg = rsp->payload.open_session_response.auth_alg; + session->v2_data.integrity_alg = rsp->payload.open_session_response.integrity_alg; + session->v2_data.crypt_alg = rsp->payload.open_session_response.crypt_alg; + session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_RECEIEVED; + } + + return rc; +} + + + +/* + * ipmi_lanplus_rakp1 + * + * Build and send the RAKP 1 message as part of the IMPI v2 / RMCP+ session + * negotiation protocol. We also read and validate the RAKP 2 message received + * from the BMC, here. See section 13.20 of the IPMI v2 specification for + * details. + * + * returns 0 on success + * 1 on failure + * + * Note that failure is only indicated if we have an internal error of some kind. If + * we actually get a RAKP 2 message in response to our RAKP 1 message, any errors + * will be stored in session->v2_data.rakp2_return_code and sent to the BMC in + * the RAKP 3 message. + */ +static int ipmi_lanplus_rakp1(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + unsigned char * msg; + struct ipmi_rs * rsp; + int i, rc = 0; + + /* + * Build a RAKP 1 message + */ + msg = (unsigned char*)malloc(IPMI_RAKP1_MESSAGE_SIZE); + memset(msg, 0, IPMI_RAKP1_MESSAGE_SIZE); + + + msg[0] = 0; /* Message tag */ + + msg[1] = 0; /* reserved */ + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* BMC session ID */ + msg[4] = session->v2_data.bmc_id & 0xff; + msg[5] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + + + /* We need a 16 byte random number */ + if (lanplus_rand(session->v2_data.console_rand, 16)) + { + // ERROR; + printf("ERROR generating random number in ipmi_lanplus_rakp1\n"); + return 1; + } + memcpy(msg + 8, session->v2_data.console_rand, 16); + #if WORDS_BIGENDIAN + lanplus_swap(msg + 8, 16); + #endif + + if (verbose > 1) + printbuf(session->v2_data.console_rand, 16, ">> Console generated random number"); + + + /* + * Requested maximum privilege level. + */ + msg[24] = 0x10; /* We will specify a name-only lookup */ + msg[24] |= IPMI_PRIV_ADMIN; + session->v2_data.requested_role = msg[24]; + + msg[25] = 0; /* reserved */ + msg[26] = 0; /* reserved */ + + + /* Username specification */ + msg[27] = strlen(session->username); + if (msg[27] > IPMI_MAX_USER_NAME_LENGTH) + { + printf("ERROR: user name too long. (Exceeds %d characters)\n", + IPMI_MAX_USER_NAME_LENGTH); + return 1; + } + memcpy(msg + 28, session->username, msg[27]); + + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_1; + v2_payload.payload_length = IPMI_RAKP1_MESSAGE_SIZE; + v2_payload.payload.rakp_1_message.message = msg; + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + if (! rsp) + { + if (verbose) + printf("> Error: no response from RAKP 1 message\n"); + return 1; + } + + session->v2_data.session_state = LANPLUS_STATE_RAKP_2_RECEIVED; + + if (verbose) + lanplus_dump_rakp2_message(rsp, session->v2_data.auth_alg); + + + + if (rsp->payload.rakp2_message.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + if (verbose) + printf("RAKP 2 message indicates an error : %s\n", + val2str(rsp->payload.rakp2_message.rakp_return_code, + ipmi_rakp_return_codes)); + rc = 1; + } + + else + { + memcpy(session->v2_data.bmc_rand, rsp->payload.rakp2_message.bmc_rand, 16); + memcpy(session->v2_data.bmc_guid, rsp->payload.rakp2_message.bmc_guid, 16); + + /* + * It is at this point that we have to decode the random number and determine + * whether the BMC has authenticated. + */ + if (! lanplus_rakp2_hmac_matches(session, + rsp->payload.rakp2_message.key_exchange_auth_code)) + { + /* Error */ + if (verbose) + printf("> RAKP 2 HMAC is invalid\n"); + session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE; + } + else + { + /* Success */ + session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_NO_ERRORS; + } + } + + return rc; +} + + + +/* + * ipmi_lanplus_rakp3 + * + * Build and send the RAKP 3 message as part of the IMPI v2 / RMCP+ session + * negotiation protocol. We also read and validate the RAKP 4 message received + * from the BMC, here. See section 13.20 of the IPMI v2 specification for + * details. + * + * If the RAKP 2 return code is not IPMI_RAKP_STATUS_NO_ERRORS, we will exit with + * an error code immediately after sendint the RAKP 3 message. + * + * param intf is the intf that holds all the state we are concerned with + * + * returns 0 on success + * 1 on failure + */ +static int ipmi_lanplus_rakp3(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + unsigned char * msg; + struct ipmi_rs * rsp; + int i; + + assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED); + + /* + * Build a RAKP 3 message + */ + msg = (unsigned char*)malloc(IPMI_RAKP3_MESSAGE_MAX_SIZE); + memset(msg, 0, IPMI_RAKP3_MESSAGE_MAX_SIZE); + + + msg[0] = 0; /* Message tag */ + msg[1] = session->v2_data.rakp2_return_code; + + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* BMC session ID */ + msg[4] = session->v2_data.bmc_id & 0xff; + msg[5] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_3; + v2_payload.payload_length = 8; + v2_payload.payload.rakp_1_message.message = msg; + + /* + * If the rakp2 return code indicates and error, we don't have to generate an + * authcode or session integrity key. In that case, we are simply sending a + * RAKP 3 message to indicate to the BMC that the RAKP 2 message caused an + * error. + */ + if (session->v2_data.rakp2_return_code == IPMI_RAKP_STATUS_NO_ERRORS) + { + int auth_length; + + if (lanplus_generate_rakp3_authcode(msg + 8, session, &auth_length)) + { + /* Error */ + if (verbose) + printf("> Error generating RAKP 3 authcode\n"); + + return 1; + } + else + { + /* Success */ + #if WORDS_BIGENDIAN + lanplus_swap(msg + 8, auth_length); + #endif + v2_payload.payload_length += auth_length; + } + + /* Generate our Session Integrity Key, K1, and K2 */ + if (lanplus_generate_sik(session)) + { + /* Error */ + if (verbose) + printf("> Error generating session integrity key\n"); + return 1; + } + else if (lanplus_generate_k1(session)) + { + /* Error */ + if (verbose) + printf("> Error generating K1 key\n"); + return 1; + } + else if (lanplus_generate_k2(session)) + { + /* Error */ + if (verbose) + printf("> Error generating K1 key\n"); + return 1; + } + } + + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + if (session->v2_data.rakp2_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + /* + * If the previous RAKP 2 message received was deemed erroneous, + * we have nothing else to do here. We only sent the RAKP 3 message + * to indicate to the BMC that the RAKP 2 message failed. + */ + return 1; + } + else if (! rsp) + { + if (verbose) + printf("> Error: no response from RAKP 3 message\n"); + return 1; + } + + + /* + * We have a RAKP 4 message to chew on. + */ + if (verbose) + lanplus_dump_rakp4_message(rsp, session->v2_data.auth_alg); + + + if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + if (verbose) + printf("RAKP 4 message indicates an error : %s\n", + val2str(rsp->payload.rakp4_message.rakp_return_code, + ipmi_rakp_return_codes)); + return 1; + } + + else + { + /* Validate the authcode */ + if (lanplus_rakp4_hmac_matches(session, + rsp->payload.rakp4_message.integrity_check_value)) + { + /* Success */ + session->v2_data.session_state = LANPLUS_STATE_ACTIVE; + } + else + { + /* Error */ + if (verbose) + printf("> RAKP 4 message has invalid integrity check value\n"); + return 1; + } + } + + return 0; +} + + + +/** + * ipmi_lan_close + */ +void ipmi_lanplus_close(struct ipmi_intf * intf) +{ + if (!intf->abort) + impi_close_session_cmd(intf); + close(intf->fd); + ipmi_req_clear_entries(); +} + + + +/** + * ipmi_lanplus_open + */ +int ipmi_lanplus_open(struct ipmi_intf * intf) +{ + int rc; + struct sigaction act; + struct get_channel_auth_cap_rsp auth_cap; + struct sockaddr_in addr; + struct ipmi_session *session; + + if (!intf || !intf->session) + return -1; + session = intf->session; + + + if (!session->port) + session->port = IPMI_LANPLUS_PORT; + if (!session->privlvl) + session->privlvl = IPMI_SESSION_PRIV_USER; + + if (!session->hostname) { + printf("No hostname specified!\n"); + return -1; + } + + intf->abort = 1; + + + /* Setup our lanplus session state */ + session->v2_data.session_state = LANPLUS_STATE_PRESESSION; + session->v2_data.auth_alg = IPMI_AUTH_RAKP_NONE; + session->v2_data.crypt_alg = IPMI_CRYPT_NONE; + session->v2_data.console_id = 0x00; + session->v2_data.bmc_id = 0x00; + memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); + memset(session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE); + + + /* open port to BMC */ + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(session->port); + + rc = inet_pton(AF_INET, session->hostname, &addr.sin_addr); + if (rc <= 0) { + struct hostent *host = gethostbyname(session->hostname); + if (!host) { + printf("address lookup failed\n"); + return -1; + } + addr.sin_family = host->h_addrtype; + memcpy(&addr.sin_addr, host->h_addr, host->h_length); + } + + if (verbose > 1) + printf("IPMI LAN host %s port %d\n", + session->hostname, ntohs(addr.sin_port)); + + intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (intf->fd < 0) { + perror("socket failed"); + return -1; + } + + + /* connect to UDP socket so we get async errors */ + rc = connect(intf->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (rc < 0) { + perror("connect failed"); + intf->close(intf); + return -1; + } + + /* setup alarm handler */ + act.sa_handler = query_alarm; + act.sa_flags = 0; + if (!sigemptyset(&act.sa_mask) && sigaction(SIGALRM, &act, NULL) < 0) { + perror("alarm signal"); + intf->close(intf); + return -1; + } + + + if (intf->pedantic) + ipmiv2_lan_first(intf); + + + /* + * + * Make sure the BMC supports IPMI v2 / RMCP+ + * + * I'm not sure why we accept a failure for the first call + */ + if (ipmi_get_auth_capabilities_cmd(intf, &auth_cap)) { + sleep(1); + if (ipmi_get_auth_capabilities_cmd(intf, &auth_cap)); + { + if (verbose) + printf("Error issuing Get Channel Authentication Capabilies request\n"); + goto fail; + } + } + + if (! auth_cap.v20_data_available) + { + if (verbose) + printf("This BMC does not support IPMI v2 / RMCP+\n"); + goto fail; + } + + + /* + * Open session + */ + if (ipmi_lanplus_open_session(intf)){ + intf->close(intf); + goto fail; + } + + /* + * RAKP 1 + */ + if (ipmi_lanplus_rakp1(intf)){ + intf->close(intf); + goto fail; + } + + /* + * RAKP 3 + */ + if (ipmi_lanplus_rakp3(intf)){ + intf->close(intf); + goto fail; + } + + + if (verbose) + printf("IPMIv2 / RMCP+ SESSION OPENED SUCCESSFULLY\n\n"); + + return intf->fd; + + fail: + printf("Error: Unable to establish IPMI v2 / RMCP+ session\n"); + return -1; +} + + + +void test_crypt1() +{ + unsigned char key[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + unsigned char iv[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + + unsigned short bytes_encrypted; + unsigned short bytes_decrypted; + unsigned char decrypt_buffer[1000]; + unsigned char encrypt_buffer[1000]; + + unsigned char data[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12}; + + printbuf(data, sizeof(data), "original data"); + + if (lanplus_encrypt_payload(IPMI_CRYPT_AES_CBC_128, + key, + data, + sizeof(data), + encrypt_buffer, + &bytes_encrypted)) + { + printf("test encrypt failed\n"); + assert(0); + } + printbuf(encrypt_buffer, bytes_encrypted, "encrypted payload"); + + + if (lanplus_decrypt_payload(IPMI_CRYPT_AES_CBC_128, + key, + encrypt_buffer, + bytes_encrypted, + decrypt_buffer, + &bytes_decrypted)) + { + printf("test decrypt failed\n"); + assert(0); + } + printbuf(decrypt_buffer, bytes_decrypted, "decrypted payload"); + + printf("\nDone testing the encrypt/decyrpt methods!\n\n"); + exit(0); +} + + + +void test_crypt2() +{ + unsigned char key[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + unsigned char iv[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + unsigned char * data = "12345678"; + + char encrypt_buffer[1000]; + char decrypt_buffer[1000]; + int bytes_encrypted; + int bytes_decrypted; + + printbuf(data, strlen(data), "input data"); + + lanplus_encrypt_aes_cbc_128(iv, + key, + data, + strlen(data), + encrypt_buffer, + &bytes_encrypted); + printbuf(encrypt_buffer, bytes_encrypted, "encrypt_buffer"); + + lanplus_decrypt_aes_cbc_128(iv, + key, + encrypt_buffer, + bytes_encrypted, + decrypt_buffer, + &bytes_decrypted); + printbuf(decrypt_buffer, bytes_decrypted, "decrypt_buffer"); + + printf("\nDone testing the encrypt/decyrpt methods!\n\n"); + exit(0); +} + + + +/** + * lanplus_intf_setup + */ +int lanplus_intf_setup(struct ipmi_intf ** intf) +{ + //test_crypt1(); + + if (lanplus_seed_prng(16)) + return -1; + + *intf = &ipmi_lanplus_intf; + (*intf)->session = malloc(sizeof(struct ipmi_session)); + memset((*intf)->session, 0, sizeof(struct ipmi_session)); + return ((*intf)->session) ? 0 : -1; +} + + +int intf_setup(struct ipmi_intf ** intf) __attribute__ ((weak, alias("lanplus_intf_setup"))); diff --git a/ipmitool/src/plugins/lanplus/lanplus.h b/ipmitool/src/plugins/lanplus/lanplus.h new file mode 100644 index 0000000..b89a7f7 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#ifndef IPMI_LANPLUS_H +#define IPMI_LANPLUS_H + +#include + +#define IPMI_LANPLUS_PORT 0x26f + +/* + * RAKP return codes. These values come from table 13-15 of the IPMI v2 + * specification. + */ +#define IPMI_RAKP_STATUS_NO_ERRORS 0x00 +#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION 0x01 +#define IPMI_RAKP_STATUS_INVALID_SESSION_ID 0x02 +#define IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE 0x03 +#define IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM 0x04 +#define IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM 0x05 +#define IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD 0x06 +#define IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD 0x07 +#define IPMI_RAKP_STATUS_INACTIVE_SESSION_ID 0x08 +#define IPMI_RAKP_STATUS_INVALID_ROLE 0x09 +#define IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED 0x0A +#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE 0x0B +#define IPMI_RAKP_STATUS_INVALID_NAME_LENGTH 0x0C +#define IPMI_RAKP_STATUS_UNAUTHORIZED_NAME 0x0D +#define IPMI_RAKP_STATUS_UNAUTHORIZED_GUID 0x0E +#define IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE 0x0F +#define IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM 0x10 +#define IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH 0x11 +#define IPMI_RAKP_STATUS_ILLEGAL_PARAMTER 0x12 + + +#define IPMI_LAN_CHANNEL_1 0x07 +#define IPMI_LAN_CHANNEL_2 0x06 +#define IPMI_LAN_CHANNEL_E 0x0e + +#define IPMI_LAN_TIMEOUT 2 +#define IPMI_LAN_RETRY 4 + +#define IPMI_PRIV_CALLBACK 1 +#define IPMI_PRIV_USER 2 +#define IPMI_PRIV_OPERATOR 3 +#define IPMI_PRIV_ADMIN 4 +#define IPMI_PRIV_OEM 5 + + +/* From table 13-17 of the IPMI v2 specification */ +#define IPMI_AUTH_RAKP_NONE 0x00 +#define IPMI_AUTH_RAKP_HMAC_SHA1 0x01 +#define IPMI_AUTH_RAKP_HMAC_MD5 0x02 + +/* From table 13-18 of the IPMI v2 specification */ +#define IPMI_INTEGRITY_NONE 0x00 +#define IPMI_INTEGRITY_HMAC_SHA1_96 0x01 +#define IPMI_INTEGRITY_HMAC_MD5_128 0x02 +#define IPMI_INTEGRITY_MD5_128 0x03 + +/* From table 13-19 of the IPMI v2 specfication */ +#define IPMI_CRYPT_NONE 0x00 +#define IPMI_CRYPT_AES_CBC_128 0x01 +#define IPMI_CRYPT_XRC4_128 0x02 +#define IPMI_CRYPT_XRC4_40 0x03 + +#define IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE 0x10 + + +/* Session message offsets, from table 13-8 of the v2 specification */ +#define IMPI_LANPLUS_OFFSET_AUTHTYPE 0x04 +#define IMPI_LANPLUS_OFFSET_PAYLOAD_TYPE 0x05 +#define IMPI_LANPLUS_OFFSET_SESSION_ID 0x06 +#define IMPI_LANPLUS_OFFSET_SEQUENCE_NUM 0x0A +#define IMPI_LANPLUS_OFFSET_PAYLOAD_SIZE 0x0E +#define IPMI_LANPLUS_OFFSET_PAYLOAD 0x10 + + +#define IPMI_GET_CHANNEL_AUTH_CAP 0x38 + +/* + * TODO: these are wild guesses and should be checked + */ +#define IPMI_MAX_CONF_HEADER_SIZE 0x20 +#define IPMI_MAX_PAYLOAD_SIZE 0x40 /* Includes confidentiality header/trailer */ +#define IPMI_MAX_CONF_TRAILER_SIZE 0x20 +#define IPMI_MAX_INTEGRITY_PAD_SIZE 0x20 +#define IPMI_MAX_AUTH_CODE_SIZE 0x20 + +#define IPMI_REQUEST_MESSAGE_SIZE 0x07 +#define IPMI_MAX_MAC_SIZE 0x14 /* The largest mac we ever expect to generate */ +#define IPMI_SHA1_AUTHCODE_SIZE 0x0C + +/* + *This is accurate, as long as we're only passing 1 auth algorithm, + * one integrity algorithm, and 1 encyrption alogrithm + */ +#define IPMI_OPEN_SESSION_REQUEST_SIZE 32 +#define IPMI_RAKP1_MESSAGE_SIZE 43 +#define IPMI_RAKP3_MESSAGE_MAX_SIZE 28 + +#define IPMI_MAX_USER_NAME_LENGTH 16 + +extern const struct valstr ipmi_privlvl_vals[]; +extern const struct valstr ipmi_authtype_vals[]; + +extern struct ipmi_session lan_session; + +struct ipmi_rs * ipmi_lan_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); +int ipmi_lanplus_open(struct ipmi_intf * intf); +void ipmi_lanplus_close(struct ipmi_intf * intf); +int ipmiv2_lan_ping(struct ipmi_intf * intf); + + +int lanplus_intf_setup(struct ipmi_intf ** intf); + + +#endif /*IPMI_LAN_H*/ diff --git a/ipmitool/src/plugins/lanplus/lanplus_crypt.c b/ipmitool/src/plugins/lanplus/lanplus_crypt.c new file mode 100644 index 0000000..911372b --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_crypt.c @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#include +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_crypt_impl.h" + + + +/* + * lanplus_rakp2_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 2 message + * + * The HMAC was generated [per RFC2404] from : + * + * SIDm - Remote console session ID + * SIDc - BMC session ID + * Rm - Remote console random number + * Rc - BMC random number + * GUIDc - BMC guid + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * - Username (absent for null user names) + * + * generated by using Kuid. I am aware that the subscripts on the values + * look backwards, but that's the way they are written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 0 on success (the authcode matches) + * 1 on failure (the authcode does not match) + */ +int lanplus_rakp2_hmac_matches(const struct ipmi_session * session, + const unsigned char * bmc_mac) +{ + char * buffer; + int bufferLength, i; + unsigned char mac[20]; + int macLength; + + unsigned int SIDm_lsbf, SIDc_lsbf; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other alogrithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + + bufferLength = + 4 + /* SIDm */ + 4 + /* SIDc */ + 16 + /* Rm */ + 16 + /* Rc */ + 16 + /* GUIDc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen(session->username); /* optional */ + + buffer = malloc(bufferLength); + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + memcpy(buffer, &SIDm_lsbf, 4); + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 4, &SIDc_lsbf, 4); + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[i]; + #endif + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[i]; + #endif + + /* ROLEm */ + buffer[56] = session->v2_data.requested_role; + + /* ULENGTHm */ + buffer[57] = strlen(session->username); + + /* UserName [optional] */ + for (i = 0; i < buffer[57]; ++i) + buffer[58 + i] = session->username[i]; + + if (verbose > 2) + { + printbuf(buffer, bufferLength, ">> rakp2 mac input buffer"); + printbuf((char*)(session->authcode), IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key"); + } + + + /* + * The buffer is complete. Let's hash. + */ + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + (session->authcode[IPMI_AUTHCODE_BUFFER_SIZE] == 0? + strlen(session->authcode): IPMI_AUTHCODE_BUFFER_SIZE), + buffer, + bufferLength, + mac, + &macLength); + + free(buffer); + + + if (verbose > 2) + { + printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console"); + } + + return (memcmp(bmc_mac, mac, macLength) == 0); +} + + + +/* + * lanplus_rakp4_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 4 message + * + * The HMAC was generated [per RFC2404] from : + * + * Rm - Remote console random number + * SIDc - BMC session ID + * GUIDc - BMC guid + * + * generated by using SIK (the session integrity key). I am aware that the + * subscripts on the values look backwards, but that's the way they are + * written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 0 on success (the authcode matches) + * 1 on failure (the authcode does not match) + */ +int lanplus_rakp4_hmac_matches(const struct ipmi_session * session, + const unsigned char * bmc_mac) +{ + char * buffer; + int bufferLength, i; + unsigned char mac[20]; + int macLength; + + unsigned int SIDm_lsbf, SIDc_lsbf; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other alogrithms */ + assert(session->v2_data.auth_alg == IPMI_INTEGRITY_HMAC_SHA1_96); + + + bufferLength = + 16 + /* Rm */ + 4 + /* SIDc */ + 16; /* GUIDc */ + + buffer = (char*)malloc(bufferLength); + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 16, &SIDc_lsbf, 4); + + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[i]; + #endif + + + /* + * The buffer is complete. Let's hash. + */ + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + 20, + buffer, + bufferLength, + mac, + &macLength); + + free(buffer); + assert(macLength == 20); + return (memcmp(bmc_mac, mac, 12) == 0); +} + + + +/* + * lanplus_generate_rakp3_auth_code + * + * This auth code is an HMAC generated with : + * + * Rc - BMC random number + * SIDm - Console session ID + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * - Usename (absent for null usernames) + * + * The key used to generated the MAC is Kuid + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param output_buffer [out] will hold the generated MAC + * param session [in] holds all the state data we need to generate the auth code + * param mac_length [out] will be set to the length of the auth code + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_rakp3_authcode(char * output_buffer, + const struct ipmi_session * session, + unsigned int * mac_length) +{ + int ret = 0; + int input_buffer_length, i; + char * input_buffer; + unsigned int SIDm_lsbf; + + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + { + *mac_length = 0; + return 0; + } + + /* We don't yet support other alogrithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + input_buffer_length = + 16 + /* Rc */ + 4 + /* SIDm */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen(session->username); + + input_buffer = malloc(input_buffer_length); + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[i]; + #endif + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + memcpy(input_buffer + 16, &SIDm_lsbf, 4); + + /* ROLEm */ + input_buffer[20] = session->v2_data.requested_role; + + /* ULENGTHm */ + input_buffer[21] = strlen(session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[21]; ++i) + input_buffer[22 + i] = session->username[i]; + + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + (session->authcode[IPMI_AUTHCODE_BUFFER_SIZE - 1] == 0? + strlen(session->authcode): IPMI_AUTHCODE_BUFFER_SIZE), + input_buffer, + input_buffer_length, + output_buffer, + mac_length); + + free(input_buffer); + + return ret; +} + + + +/* + * lanplus_generate_sik + * + * Generate the session integrity key (SIK) used for integrity checking + * during the IPMI v2 / RMCP+ session + * + * This session integrity key is a HMAC generated with : + * + * Rm - Console generated random number + * Rc - BMC generated random number + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * - Usename (absent for null usernames) + * + * The key used to generated the SIK is Kg if Kg is not null (two-key logins are + * enabled). Otherwise Kuid (the user authcode) is used as the key to genereate + * the SIK. + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param session [in/out] contains our input and output fields. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_sik(struct ipmi_session * session) +{ + char * input_buffer; + int input_buffer_length, i; + char * input_key; + int input_key_length; + int mac_length; + + + memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 0; + + /* We don't yet support other alogrithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + input_buffer_length = + 16 + /* Rm */ + 16 + /* Rc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen(session->username); + + input_buffer = malloc(input_buffer_length); + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* ROLEm */ + input_buffer[32] = session->v2_data.requested_role; + + /* ULENGTHm */ + input_buffer[33] = strlen(session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[33]; ++i) + input_buffer[34 + i] = session->username[i]; + + if (session->v2_data.kg[0]) + { + /* We will be hashing with Kg */ + /* + * TODO: Section 13.31 of the IPMI v2 spec describes the SIK creation + * using Kg. It specifies that Kg should not be truncated, but I + * do not know what is meant by that. + */ + printf("lanplus_generate_sik: We dont yet support hashing with Kg"); + assert(0); + + input_key = session->v2_data.kg; + input_key_length = strlen(session->v2_data.kg); + } + else + { + /* We will be hashing with Kuid */ + input_key = session->authcode; + input_key_length = (session->authcode[IPMI_AUTHCODE_BUFFER_SIZE - 1] == 0? + strlen(session->authcode): IPMI_AUTHCODE_BUFFER_SIZE); + } + + + lanplus_HMAC(session->v2_data.auth_alg, + input_key, + input_key_length, + input_buffer, + input_buffer_length, + session->v2_data.sik, + &mac_length); + + free(input_buffer); + assert(mac_length == 20); + + /* + * The key MAC generated is 20 bytes, but we will only be using the first + * 12 for SHA1 96 + */ + if (verbose >= 2) + printbuf(session->v2_data.sik, 20, "Generated session integrity key"); + + return 0; +} + + + +/* + * lanplus_generate_k1 + * + * Generate K1, the key presumably used to generate integrity authcodes + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_k1(struct ipmi_session * session) +{ + unsigned int mac_length; + + unsigned char CONST_1[] = + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k1, CONST_1, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + 20, /* SIK length */ + CONST_1, + 20, + session->v2_data.k1, + &mac_length); + assert(mac_length == 20); + } + + if (verbose >= 2) + printbuf(session->v2_data.k1, 20, "Generated K1"); + + return 0; +} + + + +/* + * lanplus_generate_k2 + * + * Generate K2, the key used for RMCP+ AES encryption. + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int lanplus_generate_k2(struct ipmi_session * session) +{ + unsigned int mac_length; + + unsigned char CONST_2[] = + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k2, CONST_2, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + 20, /* SIK length */ + CONST_2, + 20, + session->v2_data.k2, + &mac_length); + assert(mac_length == 20); + } + + if (verbose >= 2) + printbuf(session->v2_data.k2, 20, "Generated K2"); + + return 0; +} + + + +/* + * lanplus_encrypt_payload + * + * Perform the appropriate encryption on the input data. Output the encrypted + * data to output, including the required confidentiality header and trailer. + * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and + * set bytes_written to input_length. + * + * param crypt_alg specifies the encryption algorithm (from table 13-19 of the + * IPMI v2 spec) + * param key is the used as input to the encryption algorithmf + * param input is the input data to be encrypted + * param input_length is the length of the input data to be encrypted + * param output is the cipher text generated by the encryption process + * param bytes_written is the number of bytes written during the encryption + * process + * + * returns 0 on success + * 1 on failure + */ +int lanplus_encrypt_payload(unsigned char crypt_alg, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned short * bytes_written) +{ + unsigned char * padded_input; + unsigned int mod, i, bytes_encrypted; + unsigned char pad_length = 0; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + printf("NOT ENCRYPTING\n"); + /* Just copy the input to the output */ + //memcpy(output, input, input_length); + + //printf("input_length : %d\n", input_length); + + *bytes_written = input_length; + + //printf("bytes_written : %d\n", *bytes_written); + return 0; + } + + /* Currently, we only support AES */ + assert(crypt_alg == IPMI_CRYPT_AES_CBC_128); + + /* + * The input to the AES encryption algorithm has to be a multiple of the block + * size (16 bytes). The extra byte we are adding is the pad length byte. + */ + mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE; + if (mod) + pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod; + + padded_input = (unsigned char*)malloc(input_length + pad_length + 1); + memcpy(padded_input, input, input_length); + + /* add the pad */ + for (i = 0; i < pad_length; ++i) + padded_input[input_length + i] = i + 1; + + /* add the pad length */ + padded_input[input_length + pad_length] = pad_length; + + /* Generate an initialization vector, IV, for the encryption process */ + if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE)) + { + printf("lanplus_encrypt_payload: Error generating IV\n"); + assert(0); + return 1; + } + + if (verbose > 2) + printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector"); + + + + lanplus_encrypt_aes_cbc_128(output, /* IV */ + key, /* K2 */ + padded_input, /* Data to encrypt */ + input_length + pad_length + 1, /* Input length */ + output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output */ + &bytes_encrypted); /* bytes written */ + + *bytes_written = + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */ + bytes_encrypted; + + return 0; +} + + + +/* + * lanplus_has_valid_auth_code + * + * Determine whether the packets authcode field is valid for packet. + * + * We always return success if any of the following are true. + * - this is not an IPMIv2 packet + * - the session is not yet active + * - the packet specifies that it is not authenticated + * - the integrity algorithm agreed upon during session creation is "none" + * + * The authcode is computed using the specified integrity algorithm starting + * with the AuthType / Format field, and ending with the field immediately + * preceeding the authcode itself. + * + * The key key used to generate the authcode MAC is K1. + * + * param rs holds the response structure. + * param session holds our session state, including our chosen algorithm, key, etc. + * + * returns 1 on success (authcode is valid) + * 0 on failure (autchode integrity check failed) + */ +int lanplus_has_valid_auth_code(struct ipmi_rs * rs, + struct ipmi_session * session) +{ + unsigned char * bmc_authcode; + unsigned char generated_authcode[IPMI_MAX_MAC_SIZE]; + unsigned int generated_authcode_length; + + + if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) || + (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) || + (! rs->session.bAuthenticated) || + (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)) + return 1; + + /* We only support SHA1-96 now */ + assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96); + + /* + * For SHA1-96, the authcode will be the last 12 bytes in the packet + */ + bmc_authcode = rs->data + (rs->data_len - IPMI_SHA1_AUTHCODE_SIZE); + + lanplus_HMAC(session->v2_data.integrity_alg, + session->v2_data.k1, + 20, + rs->data + IMPI_LANPLUS_OFFSET_AUTHTYPE, + rs->data_len - IMPI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE, + generated_authcode, + &generated_authcode_length); + + assert(generated_authcode_length == 20); + return (memcmp(bmc_authcode, generated_authcode, 12) == 0); +} + + + +/* + * lanplus_decrypt_payload + * + * + * param input points to the beginning of the payload (which will be the IV if + * we are using AES) + * param payload_size [out] will be set to the size of the payload EXCLUDING + * padding + * + * returns 0 on success (we were able to successfully decrypt the packet) + * 1 on failure (we were unable to successfully decrypt the packet) + */ +int lanplus_decrypt_payload(unsigned char crypt_alg, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned short * payload_size) +{ + unsigned char * decrypted_payload; + unsigned int bytes_decrypted; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + /* We are not encrypted. The paylaod size is is everything. */ + *payload_size = input_length; + memmove(output, input, input_length); + return 0; + } + + /* We only support AES */ + assert(crypt_alg == IPMI_CRYPT_AES_CBC_128); + + decrypted_payload = (unsigned char*)malloc(input_length); + + lanplus_decrypt_aes_cbc_128(input, /* IV */ + key, /* Key */ + input + + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Data to decrypt */ + input_length - + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Input length */ + decrypted_payload, /* output */ + &bytes_decrypted); /* bytes written */ + + if (bytes_decrypted != 0) + { + /* Success */ + unsigned char conf_pad_length; + int i; + + memmove(output, + decrypted_payload, + bytes_decrypted); + + /* + * We have to determine the payload size, by substracting the padding, etc. + * The last byte of the decrypted payload is the confidentiality pad length. + */ + conf_pad_length = decrypted_payload[bytes_decrypted - 1]; + *payload_size = bytes_decrypted - conf_pad_length - 1; + + /* + * Extra test to make sure that the padding looks like it should (should start + * with 0x01, 0x02, 0x03, etc... + */ + for (i = 0; i < conf_pad_length; ++i) + { + if (decrypted_payload[*payload_size + i] == i) + { + printf("ERROR: Malformed payload padding\n"); + assert(0); + } + } + } + else + { + printf("ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes\n"); + assert(0); + } + + free(decrypted_payload); + return (bytes_decrypted == 0); +} + diff --git a/ipmitool/src/plugins/lanplus/lanplus_crypt.h b/ipmitool/src/plugins/lanplus/lanplus_crypt.h new file mode 100644 index 0000000..6075671 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_crypt.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#ifndef IPMI_LANPLUS_CRYPT_H +#define IPMI_LANPLUS_CRYPT_H + +#include + +/* + * See the implementation file for documentation + */ + +int lanplus_rakp2_hmac_matches(const struct ipmi_session * session, + const unsigned char * hmac); +int lanplus_rakp4_hmac_matches(const struct ipmi_session * session, + const unsigned char * hmac); +int lanplus_generate_rakp3_authcode(char * buffer, + const struct ipmi_session * session, + unsigned int * auth_length); +int lanplus_generate_sik(struct ipmi_session * session); +int lanplus_generate_k1(struct ipmi_session * session); +int lanplus_generate_k2(struct ipmi_session * session); +int lanplus_encrypt_payload(unsigned char crypt_alg, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned short * bytesWritten); +int lanplus_decrypt_payload(unsigned char crypt_alg, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned short * payload_size); +int lanplus_has_valid_auth_code(struct ipmi_rs * rs, + struct ipmi_session * session); + + + + +#endif /* IPMI_LANPLUS_CRYPT_H */ diff --git a/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.c b/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.c new file mode 100644 index 0000000..468189f --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#include "lanplus.h" +#include "lanplus_crypt_impl.h" +#include +#include +#include +#include + + + +/* + * lanplus_seed_prng + * + * Seed our PRNG with the specified number of bytes from /dev/random + * + * param bytes specifies the number of bytes to read from /dev/random + * + * returns 0 on success + * 1 on failure + */ +int lanplus_seed_prng(unsigned int bytes) +{ + if (! RAND_load_file("/dev/random", bytes)) + return 1; + else + return 0; +} + + + +/* + * lanplus_rand + * + * Generate a random number of the specified size + * + * param num_bytes [in] is the size of the random number to be + * generated + * param buffer [out] is where we will place our random number + * + * return 0 on success + * 1 on failure + */ +int +lanplus_rand(unsigned char * buffer, unsigned int num_bytes) +{ +#define IPMI_LANPLUS_FAKE_RAND 1 +#ifdef IPMI_LANPLUS_FAKE_RAND + + /* + * This code exists so that we can easily find the generated random number + * in the hex dumps. + */ + int i; + for (i = 0; i < num_bytes; ++i) + buffer[i] = 0x70 | i; + + return 0; +#else + return (! RAND_bytes(buffer, num_bytes)); +#endif +} + + + +/* + * lanplus_HMAC + * + * param mac specifies the algorithm to be used, currently only SHA1 is supported + * param key is the key used for HMAC generation + * param key_len is the lenght of key + * param d is the data to be MAC'd + * param n is the length of the data at d + * param md is the result of the HMAC algorithm + * param md_len is the length of md + * + * returns a pointer to md + */ +unsigned char * +lanplus_HMAC(unsigned char mac, + const void *key, + int key_len, + const unsigned char *d, + int n, + unsigned char *md, + unsigned int *md_len) +{ + + const EVP_MD *evp_md; + + if ((mac == IPMI_AUTH_RAKP_HMAC_SHA1) || + (mac == IPMI_INTEGRITY_HMAC_SHA1_96)) + evp_md = EVP_sha1(); + else + { + printf("Invalid mac type 0x%x in lanplus_HMAC\n", mac); + assert(0); + } + + + return HMAC(evp_md, key, key_len, d, n, md, md_len); +} + + +/* + * lanplus_encrypt_aes_cbc_128 + * + * Encrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be encrypted + * param input_length is the number of bytes to be encrypted. This MUST + * be a multiple of the block size, 16. + * param output is the encrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_encrypt_aes_cbc_128(const unsigned char * iv, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned int * bytes_written) +{ + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + + + *bytes_written = 0; + + if (input_length == 0) + return; + + if (verbose > 2) + { + printbuf(iv, 16, "encrypting with this IV"); + printbuf(key, 16, "encrypting with this key"); + printbuf(input, input_length, "encrypting this data"); + } + + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + + + if(!EVP_EncryptUpdate(&ctx, output, bytes_written, input, input_length)) + { + /* Error */ + *bytes_written = 0; + return; + } + else + { + unsigned int tmplen; + + if(!EVP_EncryptFinal_ex(&ctx, output + *bytes_written, &tmplen)) + { + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written += tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } +} + + + +/* + * lanplus_decrypt_aes_cbc_128 + * + * Decrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be decrypted + * param input_length is the number of bytes to be decrypted. This MUST + * be a multiple of the block size, 16. + * param output is the decrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_decrypt_aes_cbc_128(const unsigned char * iv, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned int * bytes_written) +{ + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + + + if (verbose > 2) + { + printbuf(iv, 16, "decrypting with this IV"); + printbuf(key, 16, "decrypting with this key"); + printbuf(input, input_length, "decrypting this data"); + } + + + *bytes_written = 0; + + if (input_length == 0) + return; + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + + + if(!EVP_DecryptUpdate(&ctx, output, bytes_written, input, input_length)) + { + /* Error */ + printf("ERROR: decrypt update failed\n"); + *bytes_written = 0; + return; + } + else + { + unsigned int tmplen; + + if(!EVP_DecryptFinal_ex(&ctx, output + *bytes_written, &tmplen)) + { + char buffer[1000]; + ERR_error_string(ERR_get_error(), buffer); + printf("the ERR error %s\n", buffer); + printf("ERROR: decrypt final failed\n"); + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written += tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } + + if (verbose > 2) + printbuf(output, *bytes_written, "decrypted this data"); +} + diff --git a/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.h b/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.h new file mode 100644 index 0000000..426f9a7 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_crypt_impl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#ifndef IPMI_LANPLUS_CRYPT_IMPL_H +#define IPMI_LANPLUS_CRYPT_IMPL_H + + +int +lanplus_seed_prng(unsigned int bytes); + +int +lanplus_rand(unsigned char * buffer, unsigned int num_bytes); + +unsigned char * +lanplus_HMAC(unsigned char mac, const void *key, int key_len, + const unsigned char *d, int n, unsigned char *md, + unsigned int *md_len); + +void +lanplus_encrypt_aes_cbc_128(const unsigned char * iv, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned int * bytes_written); + + +void +lanplus_decrypt_aes_cbc_128(const unsigned char * iv, + const unsigned char * key, + const unsigned char * input, + unsigned int input_length, + unsigned char * output, + unsigned int * bytes_written); + + +#endif /* IPMI_LANPLUS_CRYPT_IMPL_H */ diff --git a/ipmitool/src/plugins/lanplus/lanplus_dump.c b/ipmitool/src/plugins/lanplus/lanplus_dump.c new file mode 100644 index 0000000..a9ef94e --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_dump.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#include "lanplus.h" +#include "lanplus_dump.h" + +extern const struct valstr ipmi_rakp_return_codes[]; +extern const struct valstr ipmi_priv_levels[]; +extern const struct valstr ipmi_auth_algorithms[]; +extern const struct valstr ipmi_integrity_algorithms[]; +extern const struct valstr ipmi_encryption_algorithms[]; + +#define DUMP_PREFIX_INCOMING "<< " + +void lanplus_dump_open_session_response(const struct ipmi_rs * rsp) +{ + printf("%sOPEN SESSION RESPONSE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.open_session_response.message_tag); + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.rakp_return_code, + ipmi_rakp_return_codes)); + printf("%s Maximum privilege level : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.max_priv_level, + ipmi_priv_levels)); + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + rsp->payload.open_session_response.console_id); + printf("%s BMC Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + rsp->payload.open_session_response.bmc_id); + printf("%s Negotiated authenticatin algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.auth_alg, + ipmi_auth_algorithms)); + printf("%s Negotiated integrity algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.integrity_alg, + ipmi_integrity_algorithms)); + printf("%s Negotiated encryption algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.crypt_alg, + ipmi_encryption_algorithms)); + printf("\n"); +} + + + +void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, unsigned char auth_alg) +{ + int i; + + printf("%sRAKP 2 MESSAGE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp2_message.message_tag); + + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.rakp2_message.rakp_return_code, + ipmi_rakp_return_codes)); + + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp2_message.console_id); + + printf("%s BMC random number : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_rand[i]); + printf("\n"); + + printf("%s BMC GUID : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_guid[i]); + printf("\n"); + + switch(auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING); + break; + case IPMI_AUTH_RAKP_HMAC_SHA1: + printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 20; ++i) + printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]); + printf("\n"); + break; + case IPMI_AUTH_RAKP_HMAC_MD5: + printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]); + printf("\n"); + break; + default: + printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING); + } + printf("\n"); +} + + + +void lanplus_dump_rakp4_message(const struct ipmi_rs * rsp, unsigned char auth_alg) +{ + int i; + + printf("%sRAKP 4 MESSAGE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp2_message.message_tag); + + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.rakp2_message.rakp_return_code, + ipmi_rakp_return_codes)); + + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp2_message.console_id); + + printf("%s BMC random number : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_rand[i]); + printf("\n"); + + printf("%s BMC GUID : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_guid[i]); + printf("\n"); + + switch(auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING); + break; + case IPMI_AUTH_RAKP_HMAC_SHA1: + printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 12; ++i) + printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]); + printf("\n"); + break; + case IPMI_AUTH_RAKP_HMAC_MD5: + printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 12; ++i) + printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]); + printf("\n"); + break; + default: + printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING); + } + printf("\n"); +} + diff --git a/ipmitool/src/plugins/lanplus/lanplus_dump.h b/ipmitool/src/plugins/lanplus/lanplus_dump.h new file mode 100644 index 0000000..49b5cb4 --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_dump.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + + +#ifndef IPMI_LANPLUS_DUMP_H +#define IPMI_LANPLUS_DUMP_H + +#include + +/* See the implementation file for documentation */ +void lanplus_dump_open_session_response(const struct ipmi_rs * rsp); +void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, unsigned char auth_alg); + + +#endif /* IPMI_LANPLUS_DUMP_H */ diff --git a/ipmitool/src/plugins/lanplus/lanplus_strings.c b/ipmitool/src/plugins/lanplus/lanplus_strings.c new file mode 100644 index 0000000..ddd694b --- /dev/null +++ b/ipmitool/src/plugins/lanplus/lanplus_strings.c @@ -0,0 +1,60 @@ +#include "lanplus.h" + +const struct valstr ipmi_rakp_return_codes[] = { + + { IPMI_RAKP_STATUS_NO_ERRORS, "no errors" }, + { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION, "insufficient resources for session" }, + { IPMI_RAKP_STATUS_INVALID_SESSION_ID, "invalid session ID" }, + { IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE, "invalid payload type" }, + { IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM, "invalid authentication algorithm" }, + { IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM, "invalid integrity algorithm" }, + { IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD, "no matching authentication algorithm"}, + { IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD, "no matching integrity payload" }, + { IPMI_RAKP_STATUS_INACTIVE_SESSION_ID, "inactive session ID" }, + { IPMI_RAKP_STATUS_INVALID_ROLE, "invalid role" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED, "unauthorized role requested" }, + { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE, "insufficient resources for role" }, + { IPMI_RAKP_STATUS_INVALID_NAME_LENGTH, "invalid name length" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_NAME, "unauthorized name" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_GUID, "unauthorized GUID" }, + { IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE, "invlalid integrity check value" }, + { IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM, "invalid confidentiality algorithm" }, + { IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH, "no matching cipher suite" }, + { IPMI_RAKP_STATUS_ILLEGAL_PARAMTER, "illegal parameter" }, + { 0, 0 }, +}; + + +const struct valstr ipmi_priv_levels[] = { + { IPMI_PRIV_CALLBACK, "callback" }, + { IPMI_PRIV_USER, "user" }, + { IPMI_PRIV_OPERATOR, "operator" }, + { IPMI_PRIV_ADMIN, "admin" }, + { IPMI_PRIV_OEM, "oem" }, + { 0, 0 }, +}; + + +const struct valstr ipmi_auth_algorithms[] = { + { IPMI_AUTH_RAKP_NONE, "none" }, + { IPMI_AUTH_RAKP_HMAC_SHA1, "hmac_sha1" }, + { IPMI_AUTH_RAKP_HMAC_MD5, "hmac_md5" }, + { 0, 0 }, +}; + +const struct valstr ipmi_integrity_algorithms[] = { + { IPMI_INTEGRITY_NONE, "none" }, + { IPMI_INTEGRITY_HMAC_SHA1_96, "hmac_sha1_96" }, + { IPMI_INTEGRITY_HMAC_MD5_128, "hmac_md5_128" }, + { IPMI_INTEGRITY_MD5_128 , "md5_128" }, + { 0, 0 }, +}; + +const struct valstr ipmi_encryption_algorithms[] = { + { IPMI_CRYPT_NONE, "none" }, + { IPMI_CRYPT_AES_CBC_128, "aes_cbc_128" }, + { IPMI_CRYPT_XRC4_128, "xrc4_128" }, + { IPMI_CRYPT_XRC4_40, "xrc4_40" }, + { 0, 0 }, +}; + diff --git a/ipmitool/src/plugins/lanplus/rmcp.h b/ipmitool/src/plugins/lanplus/rmcp.h new file mode 100644 index 0000000..b8c2eaa --- /dev/null +++ b/ipmitool/src/plugins/lanplus/rmcp.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +#ifndef IPMI_RMCP_H +#define IPMI_RMCP_H + +#include +#include "lanplus.h" + +#define RMCP_VERSION_1 0x06 + +#define RMCP_UDP_PORT 0x26f /* port 623 */ +#define RMCP_UDP_SECURE_PORT 0x298 /* port 664 */ + +#define RMCP_TYPE_MASK 0x80 +#define RMCP_TYPE_NORM 0x00 +#define RMCP_TYPE_ACK 0x01 + +static const struct valstr rmcp_type_vals[] __attribute__((unused)) = { + { RMCP_TYPE_NORM, "Normal RMCP" }, + { RMCP_TYPE_ACK, "RMCP ACK" }, + { 0, NULL } +}; + +#define RMCP_CLASS_MASK 0x1f +#define RMCP_CLASS_ASF 0x06 +#define RMCP_CLASS_IPMI 0x07 +#define RMCP_CLASS_OEM 0x08 + +static const struct valstr rmcp_class_vals[] __attribute__((unused)) = { + { RMCP_CLASS_ASF, "ASF" }, + { RMCP_CLASS_IPMI, "IPMI" }, + { RMCP_CLASS_OEM, "OEM" }, + { 0, NULL } +}; + +/* RMCP message header */ +struct rmcp_hdr { + unsigned char ver; + unsigned char __reserved; + unsigned char seq; + unsigned char class; +} __attribute__((packed)); + +int handle_rmcp(struct ipmi_intf * intf, unsigned char * data, int data_len); + +#endif /* IPMI_RMCP_H */