/* * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include "lanplus.h" #include "lanplus_crypt.h" #include "lanplus_crypt_impl.h" #include "lanplus_dump.h" #include "rmcp.h" #include "asf.h" /* * LAN interface is required to support 45 byte request transactions and * 42 byte response transactions. */ #define IPMI_LAN_MAX_REQUEST_SIZE 38 /* 45 - 7 */ #define IPMI_LAN_MAX_RESPONSE_SIZE 34 /* 42 - 8 */ 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 ipmi_lanplus_setup(struct ipmi_intf * intf); static int ipmi_lanplus_keepalive(struct ipmi_intf * intf); static int ipmi_lan_send_packet(struct ipmi_intf * intf, uint8_t * 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); static void getIpmiPayloadWireRep( struct ipmi_intf * intf, struct ipmi_v2_payload * payload, /* in */ uint8_t * out, struct ipmi_rq * req, uint8_t rq_seq, uint8_t curr_seq); static void getSolPayloadWireRep( struct ipmi_intf * intf, uint8_t * msg, struct ipmi_v2_payload * payload); static void read_open_session_response(struct ipmi_rs * rsp, int offset); static void read_rakp2_message(struct ipmi_rs * rsp, int offset, uint8_t alg); static void read_rakp4_message(struct ipmi_rs * rsp, int offset, uint8_t 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); static void read_sol_packet(struct ipmi_rs * rsp, int * offset); static struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf); static struct ipmi_rs * ipmi_lanplus_send_sol( struct ipmi_intf * intf, struct ipmi_v2_payload * payload); static int check_sol_packet_for_new_data( struct ipmi_intf * intf, struct ipmi_rs *rsp); static void ack_sol_packet( struct ipmi_intf * intf, struct ipmi_rs * rsp); static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size); static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size); static uint8_t bridgePossible = 0; struct ipmi_intf ipmi_lanplus_intf = { .name = "lanplus", .desc = "IPMI v2.0 RMCP+ LAN Interface", .setup = ipmi_lanplus_setup, .open = ipmi_lanplus_open, .close = ipmi_lanplus_close, .sendrecv = ipmi_lanplus_send_ipmi_cmd, .recv_sol = ipmi_lanplus_recv_sol, .send_sol = ipmi_lanplus_send_sol, .keepalive = ipmi_lanplus_keepalive, .set_max_request_data_size = ipmi_lanp_set_max_rq_data_size, .set_max_response_data_size = ipmi_lanp_set_max_rp_data_size, .target_addr = IPMI_BMC_SLAVE_ADDR, }; extern int verbose; /* * lanplus_get_requested_ciphers * * Set the authentication, integrity and encryption algorithms based * on the cipher suite ID. See table 22-19 in the IPMIv2 spec for the * source of this information. * * param cipher_suite_id [in] * param auth_alg [out] * param integrity_alg [out] * param crypt_alg [out] * * returns 0 on success * 1 on failure */ int lanplus_get_requested_ciphers(int cipher_suite_id, uint8_t * auth_alg, uint8_t * integrity_alg, uint8_t * crypt_alg) { #ifdef HAVE_CRYPTO_SHA256 if ((cipher_suite_id < 0) || (cipher_suite_id > 17)) { return 1; } #else if ((cipher_suite_id < 0) || (cipher_suite_id > 14)) return 1; #endif /* HAVE_CRYPTO_SHA256 */ /* See table 22-19 for the source of the statement */ switch (cipher_suite_id) { case 0: *auth_alg = IPMI_AUTH_RAKP_NONE; *integrity_alg = IPMI_INTEGRITY_NONE; *crypt_alg = IPMI_CRYPT_NONE; break; case 1: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *integrity_alg = IPMI_INTEGRITY_NONE; *crypt_alg = IPMI_CRYPT_NONE; break; case 2: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *crypt_alg = IPMI_CRYPT_NONE; break; case 3: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *crypt_alg = IPMI_CRYPT_AES_CBC_128; break; case 4: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *crypt_alg = IPMI_CRYPT_XRC4_128; break; case 5: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; *crypt_alg = IPMI_CRYPT_XRC4_40; break; case 6: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_NONE; *crypt_alg = IPMI_CRYPT_NONE; break; case 7: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *crypt_alg = IPMI_CRYPT_NONE; break; case 8: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128; break; case 9: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *crypt_alg = IPMI_CRYPT_XRC4_128; break; case 10: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; *crypt_alg = IPMI_CRYPT_XRC4_40; break; case 11: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_MD5_128; *crypt_alg = IPMI_CRYPT_NONE; break; case 12: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_MD5_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128; break; case 13: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_MD5_128; *crypt_alg = IPMI_CRYPT_XRC4_128; break; case 14: *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; *integrity_alg = IPMI_INTEGRITY_MD5_128; *crypt_alg = IPMI_CRYPT_XRC4_40; break; #ifdef HAVE_CRYPTO_SHA256 case 15: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *integrity_alg = IPMI_INTEGRITY_NONE; *crypt_alg = IPMI_CRYPT_NONE; break; case 16: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128; *crypt_alg = IPMI_CRYPT_NONE; break; case 17: *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA256; *integrity_alg = IPMI_INTEGRITY_HMAC_SHA256_128; *crypt_alg = IPMI_CRYPT_AES_CBC_128; break; #endif /* HAVE_CRYPTO_SHA256 */ } return 0; } /* * Reverse the order of arbitrarily long strings of bytes */ void lanplus_swap( uint8_t * buffer, int length) { int i; uint8_t temp; for (i =0; i < length/2; ++i) { temp = buffer[i]; buffer[i] = buffer[length - 1 - i]; buffer[length - 1 - i] = temp; } } static const struct valstr plus_payload_types_vals[] = { { IPMI_PAYLOAD_TYPE_IPMI, "IPMI (0)" }, // IPMI Message { IPMI_PAYLOAD_TYPE_SOL, "SOL (1)" }, // SOL (Serial over LAN) { IPMI_PAYLOAD_TYPE_OEM, "OEM (2)" }, // OEM Explicid { IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST, "OpenSession Req (0x10)" }, { IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE,"OpenSession Resp (0x11)" }, { IPMI_PAYLOAD_TYPE_RAKP_1, "RAKP1 (0x12)" }, { IPMI_PAYLOAD_TYPE_RAKP_2, "RAKP2 (0x13)" }, { IPMI_PAYLOAD_TYPE_RAKP_3, "RAKP3 (0x14)" }, { IPMI_PAYLOAD_TYPE_RAKP_4, "RAKP4 (0x15)" }, { 0x00, NULL }, }; static struct ipmi_rq_entry * ipmi_req_add_entry(struct ipmi_intf * intf, struct ipmi_rq * req, uint8_t req_seq) { struct ipmi_rq_entry * e; e = malloc(sizeof(struct ipmi_rq_entry)); if (e == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return NULL; } memset(e, 0, sizeof(struct ipmi_rq_entry)); memcpy(&e->req, req, sizeof(struct ipmi_rq)); e->intf = intf; e->rq_seq = req_seq; if (ipmi_req_entries == NULL) ipmi_req_entries = e; else ipmi_req_entries_tail->next = e; ipmi_req_entries_tail = e; lprintf(LOG_DEBUG+3, "added list entry seq=0x%02x cmd=0x%02x", e->rq_seq, e->req.msg.cmd); return e; } static struct ipmi_rq_entry * ipmi_req_lookup_entry(uint8_t seq, uint8_t 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(uint8_t seq, uint8_t cmd) { struct ipmi_rq_entry * p, * e, * saved_next_entry; e = p = ipmi_req_entries; while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { p = e; e = e->next; } if (e) { lprintf(LOG_DEBUG+3, "removed list entry seq=0x%02x cmd=0x%02x", seq, cmd); saved_next_entry = e->next; p->next = (p->next == e->next) ? NULL : e->next; /* If entry being removed is first in list, fix up list head */ if (ipmi_req_entries == e) { if (ipmi_req_entries != p) ipmi_req_entries = p; else ipmi_req_entries = saved_next_entry; } /* If entry being removed is last in list, fix up list tail */ 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); e->msg_data = NULL; } free(e); e = NULL; } } static void ipmi_req_clear_entries(void) { struct ipmi_rq_entry * p, * e; e = ipmi_req_entries; while (e) { lprintf(LOG_DEBUG+3, "cleared list entry seq=0x%02x cmd=0x%02x", e->rq_seq, e->req.msg.cmd); p = e->next; free(e); e = p; } ipmi_req_entries = NULL; ipmi_req_entries_tail = NULL; } int ipmi_lan_send_packet( struct ipmi_intf * intf, uint8_t * data, int data_len) { if (verbose >= 5) 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; fd_set read_set, err_set; struct timeval tmout; int ret; FD_ZERO(&read_set); FD_SET(intf->fd, &read_set); FD_ZERO(&err_set); FD_SET(intf->fd, &err_set); tmout.tv_sec = intf->session->timeout; tmout.tv_usec = 0; ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) return NULL; /* 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) */ ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); if (ret < 0) { FD_ZERO(&read_set); FD_SET(intf->fd, &read_set); FD_ZERO(&err_set); FD_SET(intf->fd, &err_set); tmout.tv_sec = intf->session->timeout; tmout.tv_usec = 0; ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) return NULL; ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); if (ret < 0) return NULL; } if (ret == 0) return NULL; rsp.data[ret] = '\0'; rsp.data_len = ret; if (verbose >= 5) printbuf(rsp.data, rsp.data_len, "<< received packet"); 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; uint8_t sup_entities; uint8_t sup_interact; uint8_t 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 %lu\n\n", (pong->sup_entities & 0x01) ? "1.0" : "unknown", (pong->rmcp.ver == 6) ? "1.0" : "unknown", pong->rmcp.seq, (unsigned long)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 = htonl(ASF_RMCP_IANA), .type = ASF_TYPE_PING, }; struct rmcp_hdr rmcp_ping = { .ver = RMCP_VERSION_1, .class = RMCP_CLASS_ASF, .seq = 0xff, }; uint8_t * data; int len = sizeof(rmcp_ping) + sizeof(asf_ping); int rv; data = malloc(len); if (data == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return -1; } memset(data, 0, len); memcpy(data, &rmcp_ping, sizeof(rmcp_ping)); memcpy(data+sizeof(rmcp_ping), &asf_ping, sizeof(asf_ping)); lprintf(LOG_DEBUG, "Sending IPMI/RMCP presence ping packet"); rv = ipmi_lan_send_packet(intf, data, len); free(data); data = NULL; if (rv < 0) { lprintf(LOG_ERR, "Unable to send IPMI presence ping packet"); return -1; } if (ipmi_lan_poll_recv(intf) == 0) return 0; return 1; } /** * * ipmi_lan_poll_single * * 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 response we expect. */ static struct ipmi_rs * ipmi_lan_poll_single(struct ipmi_intf * intf) { struct rmcp_hdr * rmcp_rsp; struct ipmi_rs * rsp; struct ipmi_session * session = intf->session; int offset, rv; uint16_t payload_size; /* receive packet */ rsp = ipmi_lan_recv_packet(intf); /* check if no packet has come */ if (rsp == NULL) { return NULL; } /* parse response headers */ rmcp_rsp = (struct rmcp_hdr *)rsp->data; 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) { lprintf(LOG_DEBUG, "Invalid RMCP class: %x", rmcp_rsp->class); /* read one more packet */ return (struct ipmi_rs *)1; } /* * 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 response 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) A Serial Over LAN packet * 7) An Invalid packet (one that doesn't match a request) * ------------------------------------------------------------------- */ read_session_data(rsp, &offset, intf->session); /* * Skip packets that are not intended for this session */ if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && (rsp->session.id != intf->session->v2_data.console_id)) { lprintf(LOG_INFO, "packet session id 0x%x does not " "match active session 0x%0x", rsp->session.id, intf->session->v2_data.console_id); lprintf(LOG_ERR, "ERROR: Received an Unexpected message ID"); /* read one more packet */ return (struct ipmi_rs *)1; } if (lanplus_has_valid_auth_code(rsp, intf->session) == 0) { lprintf(LOG_ERR, "ERROR: Received message with invalid authcode!"); return NULL; } 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 responses */ if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI) { struct ipmi_rq_entry * entry; int payload_start = offset; int extra_data_length; int loop = 1; while (loop--) { /* fill-in response data */ read_ipmi_response(rsp, &offset); lprintf(LOG_DEBUG+1, "<< IPMI Response Session Header"); lprintf(LOG_DEBUG+1, "<< Authtype : %s", val2str(rsp->session.authtype, ipmi_authtype_session_vals)); lprintf(LOG_DEBUG+1, "<< Payload type : %s", val2str(rsp->session.payloadtype, plus_payload_types_vals)); lprintf(LOG_DEBUG+1, "<< Session ID : 0x%08lx", (long)rsp->session.id); lprintf(LOG_DEBUG+1, "<< Sequence : 0x%08lx", (long)rsp->session.seq); lprintf(LOG_DEBUG+1, "<< IPMI Msg/Payload Length : %d", rsp->session.msglen); lprintf(LOG_DEBUG+1, "<< IPMI Response Message Header"); lprintf(LOG_DEBUG+1, "<< Rq Addr : %02x", rsp->payload.ipmi_response.rq_addr); lprintf(LOG_DEBUG+1, "<< NetFn : %02x", rsp->payload.ipmi_response.netfn); lprintf(LOG_DEBUG+1, "<< Rq LUN : %01x", rsp->payload.ipmi_response.rq_lun); lprintf(LOG_DEBUG+1, "<< Rs Addr : %02x", rsp->payload.ipmi_response.rs_addr); lprintf(LOG_DEBUG+1, "<< Rq Seq : %02x", rsp->payload.ipmi_response.rq_seq); lprintf(LOG_DEBUG+1, "<< Rs Lun : %01x", rsp->payload.ipmi_response.rs_lun); lprintf(LOG_DEBUG+1, "<< Command : %02x", rsp->payload.ipmi_response.cmd); lprintf(LOG_DEBUG+1, "<< Compl Code : 0x%02x", 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 == NULL) { lprintf(LOG_INFO, "IPMI Request Match NOT FOUND"); /* read one more packet */ return (struct ipmi_rs *)1; }; uint8_t target_cmd = entry->req.msg.target_cmd; lprintf(LOG_DEBUG+2, "IPMI Request Match found"); if (entry->bridging_level) { /* Check completion code */ if (rsp->ccode) { lprintf(LOG_DEBUG, "WARNING: Bridged " "cmd ccode = 0x%02x", rsp->ccode); } else { /* decrement bridging level */ entry->bridging_level--; if (!entry->bridging_level) { entry->req.msg.cmd = entry->req.msg.target_cmd; } /* check if bridged response is embedded */ if (payload_size > 8) { printbuf(&rsp->data[offset], (rsp->data_len-offset-1), "bridge command response"); /* * decrement payload size * (cks2 for outer Send Message) */ payload_size--; /* * need to make a loop for embedded bridged response */ loop++; } else { lprintf(LOG_DEBUG, "Bridged command answer," " waiting for next answer... "); /* read one more packet */ return (struct ipmi_rs *)1; } } } /* Remove request entry */ ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq, rsp->payload.ipmi_response.cmd); /* * 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 (extra_data_length) { rsp->data_len = extra_data_length; memmove(rsp->data, rsp->data + offset, extra_data_length); } else { rsp->data_len = 0; } } /* * Open Response */ } else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE) { if (session->v2_data.session_state != LANPLUS_STATE_OPEN_SESSION_SENT) { lprintf(LOG_ERR, "Error: Received an Unexpected Open Session " "Response"); /* read one more packet */ return (struct ipmi_rs *)1; } read_open_session_response(rsp, offset); /* * RAKP 2 */ } else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) { if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT) { lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 2 message"); /* read one more packet */ return (struct ipmi_rs *)1; } read_rakp2_message(rsp, offset, session->v2_data.auth_alg); /* * RAKP 4 */ } else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4) { if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT) { lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 4 message"); /* read one more packet */ return (struct ipmi_rs *)1; } read_rakp4_message(rsp, offset, session->v2_data.auth_alg); /* * SOL */ } else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) { int payload_start = offset; int extra_data_length; if (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) { lprintf(LOG_ERR, "Error: Received an Unexpected SOL packet"); /* read one more packet */ return (struct ipmi_rs *)1; } read_sol_packet(rsp, &offset); extra_data_length = payload_size - (offset - payload_start); if (rsp && extra_data_length) { rsp->data_len = extra_data_length; memmove(rsp->data, rsp->data + offset, extra_data_length); } else { rsp->data_len = 0; } /* * Unknown Payload type */ } else { lprintf(LOG_ERR, "Invalid RMCP+ payload type : 0x%x", rsp->session.payloadtype); /* read one more packet */ return (struct ipmi_rs *)1; } return rsp; } /** * * 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 response we expect. */ static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf) { struct ipmi_rs * rsp; do { /* poll single packet */ rsp = ipmi_lan_poll_single(intf); } while (rsp == (struct ipmi_rs *) 1); return rsp; } /* * read_open_session_response * * Initialize the ipmi_rs from the IPMI 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) { memset(&rsp->payload.open_session_response, 0, sizeof(rsp->payload.open_session_response)); /* Message tag */ rsp->payload.open_session_response.message_tag = rsp->data[offset]; /* RAKP response 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]; /*** offset + 3 is reserved ***/ /* 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 /* only tag, status, privlvl, and console id are returned if error */ if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) return; /* 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 IPMI 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, uint8_t auth_alg) { int i; /* Message tag */ rsp->payload.rakp2_message.message_tag = rsp->data[offset]; /* RAKP response 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 < IPMI_SHA_DIGEST_LENGTH; ++i) { rsp->payload.rakp2_message.key_exchange_auth_code[i] = rsp->data[offset + 40 + i]; } break; case IPMI_AUTH_RAKP_HMAC_MD5: /* We need to copy 16 bytes */ for (i = 0; i < IPMI_MD5_DIGEST_LENGTH; ++i) { rsp->payload.rakp2_message.key_exchange_auth_code[i] = rsp->data[offset + 40 + i]; } break; #ifdef HAVE_CRYPTO_SHA256 case IPMI_AUTH_RAKP_HMAC_SHA256: /* We need to copy 32 bytes */ for (i = 0; i < IPMI_SHA256_DIGEST_LENGTH; ++i) { rsp->payload.rakp2_message.key_exchange_auth_code[i] = rsp->data[offset + 40 + i]; } break; #endif /* HAVE_CRYPTO_SHA256 */ default: lprintf(LOG_ERR, "read_rakp2_message: no support " "for authentication algorithm 0x%x", auth_alg); assert(0); break; } } /* * read_rakp4_message * * Initialize the ipmi_rs from the IPMI 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 * from the packet. * * returns 0 on success, 1 on error */ void read_rakp4_message( struct ipmi_rs * rsp, int offset, uint8_t auth_alg) { int i; /* Message tag */ rsp->payload.rakp4_message.message_tag = rsp->data[offset]; /* RAKP response 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.rakp4_message.console_id); #endif /* Integrity check value */ switch (auth_alg) { case IPMI_AUTH_RAKP_NONE: /* Nothing to do here */ break; case IPMI_AUTH_RAKP_HMAC_SHA1: /* We need to copy 12 bytes */ for (i = 0; i < IPMI_SHA1_AUTHCODE_SIZE; ++i) { rsp->payload.rakp4_message.integrity_check_value[i] = rsp->data[offset + 8 + i]; } break; case IPMI_AUTH_RAKP_HMAC_MD5: /* We need to copy 16 bytes */ for (i = 0; i < IPMI_HMAC_MD5_AUTHCODE_SIZE; ++i) { rsp->payload.rakp4_message.integrity_check_value[i] = rsp->data[offset + 8 + i]; } break; #ifdef HAVE_CRYPTO_SHA256 case IPMI_AUTH_RAKP_HMAC_SHA256: /* We need to copy 16 bytes */ for (i = 0; i < IPMI_HMAC_SHA256_AUTHCODE_SIZE; ++i) { rsp->payload.rakp4_message.integrity_check_value[i] = rsp->data[offset + 8 + i]; } break; #endif /* HAVE_CRYPTO_SHA256 */ default: lprintf(LOG_ERR, "read_rakp4_message: no support " "for authentication algorithm 0x%x", auth_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.bEncrypted = (rsp->data[*offset] & 0x80 ? 1 : 0); rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0); /* Payload type */ 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 /* 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_16(rsp->session.msglen); #endif } /* * 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 ipmi_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)++]; } /* * read_sol_packet * * Initialize the ipmi_rs with the SOL response data * * The offset should point the first byte of the the SOL payload when this * function is called. * * param rsp [in/out] we read from the data buffer and populate the * SOL specific fields. * param offset [in/out] should point to the beginning of the SOL payload * when this function is called. */ void read_sol_packet(struct ipmi_rs * rsp, int * offset) { /* * The data here should be decrypted by now. */ rsp->payload.sol_packet.packet_sequence_number = rsp->data[(*offset)++] & 0x0F; rsp->payload.sol_packet.acked_packet_number = rsp->data[(*offset)++] & 0x0F; rsp->payload.sol_packet.accepted_character_count = rsp->data[(*offset)++]; rsp->payload.sol_packet.is_nack = rsp->data[*offset] & 0x40; rsp->payload.sol_packet.transfer_unavailable = rsp->data[*offset] & 0x20; rsp->payload.sol_packet.sol_inactive = rsp->data[*offset] & 0x10; rsp->payload.sol_packet.transmit_overrun = rsp->data[*offset] & 0x08; rsp->payload.sol_packet.break_detected = rsp->data[(*offset)++] & 0x04; lprintf(LOG_DEBUG, "<<<<<<<<<< RECV FROM BMC <<<<<<<<<<<"); lprintf(LOG_DEBUG, "< SOL sequence number : 0x%02x", rsp->payload.sol_packet.packet_sequence_number); lprintf(LOG_DEBUG, "< SOL acked packet : 0x%02x", rsp->payload.sol_packet.acked_packet_number); lprintf(LOG_DEBUG, "< SOL accepted char count : 0x%02x", rsp->payload.sol_packet.accepted_character_count); lprintf(LOG_DEBUG, "< SOL is nack : %s", rsp->payload.sol_packet.is_nack? "true" : "false"); lprintf(LOG_DEBUG, "< SOL xfer unavailable : %s", rsp->payload.sol_packet.transfer_unavailable? "true" : "false"); lprintf(LOG_DEBUG, "< SOL inactive : %s", rsp->payload.sol_packet.sol_inactive? "true" : "false"); lprintf(LOG_DEBUG, "< SOL transmit overrun : %s", rsp->payload.sol_packet.transmit_overrun? "true" : "false"); lprintf(LOG_DEBUG, "< SOL break detected : %s", rsp->payload.sol_packet.break_detected? "true" : "false"); lprintf(LOG_DEBUG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); if (verbose >= 5) printbuf(rsp->data + *offset - 4, 4, "SOL MSG FROM BMC"); } /* * 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( struct ipmi_intf * intf, /* in out */ struct ipmi_v2_payload * payload, /* in */ uint8_t * msg, struct ipmi_rq * req, uint8_t rq_seq, uint8_t curr_seq) { int cs, tmp, len; int cs2 = 0; int cs3 = 0; uint8_t ourAddress = intf->my_addr; uint8_t bridgedRequest = 0; if (ourAddress == 0) ourAddress = IPMI_BMC_SLAVE_ADDR; len = 0; /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ if ((intf->target_addr == ourAddress) || (!bridgePossible)) { cs = len; } else { bridgedRequest = 1; if(intf->transit_addr != ourAddress && intf->transit_addr != 0) { bridgedRequest++; } /* bridged request: encapsulate w/in Send Message */ cs = len; msg[len++] = IPMI_BMC_SLAVE_ADDR; msg[len++] = IPMI_NETFN_APP << 2; tmp = len - cs; msg[len++] = ipmi_csum(msg+cs, tmp); cs2 = len; msg[len++] = IPMI_REMOTE_SWID; msg[len++] = curr_seq << 2; msg[len++] = 0x34; /* Send Message rqst */ if(bridgedRequest == 2) msg[len++] = (0x40|intf->transit_channel); /* Track request*/ else msg[len++] = (0x40|intf->target_channel); /* Track request*/ payload->payload_length += 7; cs = len; if(bridgedRequest == 2) { /* bridged request: encapsulate w/in Send Message */ cs = len; msg[len++] = intf->transit_addr; msg[len++] = IPMI_NETFN_APP << 2; tmp = len - cs; msg[len++] = ipmi_csum(msg+cs, tmp); cs3 = len; msg[len++] = IPMI_REMOTE_SWID; msg[len++] = curr_seq << 2; msg[len++] = 0x34; /* Send Message rqst */ msg[len++] = (0x40|intf->target_channel); /* Track request*/ payload->payload_length += 7; cs = len; } } lprintf(LOG_DEBUG,"%s RqAddr %#x transit %#x:%#x target %#x:%#x " "bridgePossible %d", bridgedRequest ? "Bridging" : "Local", intf->my_addr, intf->transit_addr, intf->transit_channel, intf->target_addr, intf->target_channel, bridgePossible); /* rsAddr */ if (bridgedRequest) msg[len++] = intf->target_addr; else msg[len++] = IPMI_BMC_SLAVE_ADDR; /* net Fn */ msg[len++] = req->msg.netfn << 2 | (req->msg.lun & 3); tmp = len - cs; /* checkSum */ msg[len++] = ipmi_csum(msg+cs, tmp); cs = len; /* rqAddr */ if (bridgedRequest < 2) msg[len++] = IPMI_REMOTE_SWID; else /* Bridged message */ msg[len++] = intf->my_addr; /* 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); /* Dual bridged request: 2nd checksum */ if (bridgedRequest == 2) { tmp = len - cs3; msg[len++] = ipmi_csum(msg+cs3, tmp); payload->payload_length += 1; } /* bridged request: 2nd checksum */ if (bridgedRequest) { tmp = len - cs2; msg[len++] = ipmi_csum(msg+cs2, tmp); payload->payload_length += 1; } } /* * getSolPayloadWireRep * * param msg [out] will contain our wire representation * param payload [in] holds the v2 payload with our SOL data */ void getSolPayloadWireRep( struct ipmi_intf * intf, /* in out */ uint8_t * msg, /* output */ struct ipmi_v2_payload * payload) /* input */ { int i = 0; lprintf(LOG_DEBUG, ">>>>>>>>>> SENDING TO BMC >>>>>>>>>>"); lprintf(LOG_DEBUG, "> SOL sequence number : 0x%02x", payload->payload.sol_packet.packet_sequence_number); lprintf(LOG_DEBUG, "> SOL acked packet : 0x%02x", payload->payload.sol_packet.acked_packet_number); lprintf(LOG_DEBUG, "> SOL accepted char count : 0x%02x", payload->payload.sol_packet.accepted_character_count); lprintf(LOG_DEBUG, "> SOL is nack : %s", payload->payload.sol_packet.is_nack ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL assert ring wor : %s", payload->payload.sol_packet.assert_ring_wor ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL generate break : %s", payload->payload.sol_packet.generate_break ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL deassert cts : %s", payload->payload.sol_packet.deassert_cts ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL deassert dcd dsr : %s", payload->payload.sol_packet.deassert_dcd_dsr ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL flush inbound : %s", payload->payload.sol_packet.flush_inbound ? "true" : "false"); lprintf(LOG_DEBUG, "> SOL flush outbound : %s", payload->payload.sol_packet.flush_outbound ? "true" : "false"); msg[i++] = payload->payload.sol_packet.packet_sequence_number; msg[i++] = payload->payload.sol_packet.acked_packet_number; msg[i++] = payload->payload.sol_packet.accepted_character_count; msg[i] = payload->payload.sol_packet.is_nack ? 0x40 : 0; msg[i] |= payload->payload.sol_packet.assert_ring_wor ? 0x20 : 0; msg[i] |= payload->payload.sol_packet.generate_break ? 0x10 : 0; msg[i] |= payload->payload.sol_packet.deassert_cts ? 0x08 : 0; msg[i] |= payload->payload.sol_packet.deassert_dcd_dsr ? 0x04 : 0; msg[i] |= payload->payload.sol_packet.flush_inbound ? 0x02 : 0; msg[i++] |= payload->payload.sol_packet.flush_outbound ? 0x01 : 0; /* We may have data to add */ memcpy(msg + i, payload->payload.sol_packet.data, payload->payload.sol_packet.character_count); lprintf(LOG_DEBUG, "> SOL character count : %d", payload->payload.sol_packet.character_count); lprintf(LOG_DEBUG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); if (verbose >= 5 && payload->payload.sol_packet.character_count) printbuf(payload->payload.sol_packet.data, payload->payload.sol_packet.character_count, "SOL SEND DATA"); /* * At this point, the payload length becomes the whole payload * length, including the 4 bytes at the beginning of the SOL * packet */ payload->payload_length = payload->payload.sol_packet.character_count + 4; } /* * 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 */ uint8_t ** msg_data, /* out */ uint8_t curr_seq) { uint32_t 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 */ uint8_t * msg; int len = 0; len = sizeof(rmcp) + // RMCP Header (4) 10 + // IPMI Session Header 2 + // Message length payload->payload_length + // 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); if (msg == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return; } 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[IPMI_LANPLUS_OFFSET_AUTHTYPE] = 0x06; /* Payload Type -- also specifies whether were authenticated/encyrpted */ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] = payload->payload_type; if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) { msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= ((session->v2_data.crypt_alg != IPMI_CRYPT_NONE )? 0x80 : 0x00); msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= ((session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)? 0x40 : 0x00); } if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) { /* Session ID -- making it LSB */ msg[IPMI_LANPLUS_OFFSET_SESSION_ID ] = session->v2_data.bmc_id & 0xff; msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 1] = (session->v2_data.bmc_id >> 8) & 0xff; msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 2] = (session->v2_data.bmc_id >> 16) & 0xff; msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 3] = (session->v2_data.bmc_id >> 24) & 0xff; /* Sequence Number -- making it LSB */ msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM ] = session->out_seq & 0xff; msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 1] = (session->out_seq >> 8) & 0xff; msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 2] = (session->out_seq >> 16) & 0xff; msg[IPMI_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(intf, payload, /* in */ msg + IPMI_LANPLUS_OFFSET_PAYLOAD, payload->payload.ipmi_request.request, payload->payload.ipmi_request.rq_seq, curr_seq); break; case IPMI_PAYLOAD_TYPE_SOL: getSolPayloadWireRep(intf, msg + IPMI_LANPLUS_OFFSET_PAYLOAD, payload); if (verbose >= 5) printbuf(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, 4, "SOL MSG TO BMC"); len += payload->payload_length; 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; default: lprintf(LOG_ERR, "unsupported payload type 0x%x", payload->payload_type); free(msg); msg = NULL; assert(0); break; } /* *------------------------------------------ * ENCRYPT THE PAYLOAD IF NECESSARY *------------------------------------------ */ if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) { /* Payload len 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[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE ] = payload->payload_length & 0xff; msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE + 1] = (payload->payload_length >> 8) & 0xff; /* *------------------------------------------ * SESSION TRAILER *------------------------------------------ */ if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && (session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)) { uint32_t i; uint32_t hmac_length; uint32_t auth_length = 0; uint32_t integrity_pad_size = 0; uint32_t hmac_input_size; uint8_t * hmac_output; uint32_t start_of_session_trailer = IPMI_LANPLUS_OFFSET_PAYLOAD + payload->payload_length; /* * Determine the required integrity pad length. We have to make the * data range covered by the authcode a multiple of 4. */ uint32_t length_before_authcode; if (ipmi_oem_active(intf, "icts")) { length_before_authcode = 12 + /* the stuff before the payload */ payload->payload_length; } else { 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 + IPMI_LANPLUS_OFFSET_AUTHTYPE, hmac_input_size, "authcode input"); /* Auth Code */ lanplus_HMAC(session->v2_data.integrity_alg, session->v2_data.k1, /* key */ session->v2_data.k1_len, /* key length */ msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, /* hmac input */ hmac_input_size, hmac_output, &hmac_length); switch(session->v2_data.integrity_alg) { case IPMI_INTEGRITY_HMAC_SHA1_96: assert(hmac_length == IPMI_SHA_DIGEST_LENGTH); auth_length = IPMI_SHA1_AUTHCODE_SIZE; break; case IPMI_INTEGRITY_HMAC_MD5_128 : assert(hmac_length == IPMI_MD5_DIGEST_LENGTH); auth_length = IPMI_HMAC_MD5_AUTHCODE_SIZE; break; #ifdef HAVE_CRYPTO_SHA256 case IPMI_INTEGRITY_HMAC_SHA256_128: assert(hmac_length == IPMI_SHA256_DIGEST_LENGTH); auth_length = IPMI_HMAC_SHA256_AUTHCODE_SIZE; break; #endif /* HAVE_CRYPTO_SHA256 */ default: assert(0); break; } if (verbose > 2) printbuf(hmac_output, auth_length, "authcode output"); /* Set session_trailer_length appropriately */ session_trailer_length = integrity_pad_size + 2 + /* pad length + next header */ auth_length; /* Size of the authcode. We only * use the first 12(SHA1) or * 16(MD5/SHA256) 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, int isRetry) { 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 uint8_t curr_seq = 0; if( isRetry == 0 ) curr_seq += 1; if (curr_seq >= 64) curr_seq = 0; /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ if ((intf->target_addr == intf->my_addr) || (!bridgePossible)) { entry = ipmi_req_add_entry(intf, req, curr_seq); /* it's a bridge command */ } else { unsigned char backup_cmd; /* Add entry for cmd */ entry = ipmi_req_add_entry(intf, req, curr_seq); if (entry) { entry->req.msg.target_cmd = entry->req.msg.cmd; entry->req.msg.cmd = 0x34; if (intf->transit_addr && intf->transit_addr != intf->my_addr) entry->bridging_level = 2; else entry->bridging_level = 1; } } if (entry == NULL) return NULL; // 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 curr_seq); // in 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, }; uint8_t * msg; int cs, mp, len = 0, tmp; struct ipmi_session * session = intf->session; struct ipmi_rq_entry * entry; entry = ipmi_req_add_entry(intf, req, 0); if (entry == NULL) return NULL; len = req->msg.data_len + 21; msg = malloc(len); if (msg == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return NULL; } 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; lprintf(LOG_DEBUG+1, ">> IPMI Request Session Header"); lprintf(LOG_DEBUG+1, ">> Authtype : %s", val2str(IPMI_SESSION_AUTHTYPE_NONE, ipmi_authtype_session_vals)); lprintf(LOG_DEBUG+1, ">> Sequence : 0x%08lx", (long)session->out_seq); lprintf(LOG_DEBUG+1, ">> Session ID : 0x%08lx", (long)0); lprintf(LOG_DEBUG+1, ">> IPMI Request Message Header"); lprintf(LOG_DEBUG+1, ">> Rs Addr : %02x", IPMI_BMC_SLAVE_ADDR); lprintf(LOG_DEBUG+1, ">> NetFn : %02x", req->msg.netfn); lprintf(LOG_DEBUG+1, ">> Rs LUN : %01x", 0); lprintf(LOG_DEBUG+1, ">> Rq Addr : %02x", IPMI_REMOTE_SWID); lprintf(LOG_DEBUG+1, ">> Rq Seq : %02x", entry->rq_seq); lprintf(LOG_DEBUG+1, ">> Rq Lun : %01x", 0); lprintf(LOG_DEBUG+1, ">> Command : %02x", 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; } /* * is_sol_packet */ static int is_sol_packet(struct ipmi_rs * rsp) { return (rsp && (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)); } /* * sol_response_acks_packet */ static int sol_response_acks_packet( struct ipmi_rs * rsp, struct ipmi_v2_payload * payload) { return (is_sol_packet(rsp) && payload && (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) && (rsp->payload.sol_packet.acked_packet_number == payload->payload.sol_packet.packet_sequence_number)); } /* * ipmi_lanplus_send_payload * */ struct ipmi_rs * ipmi_lanplus_send_payload( struct ipmi_intf * intf, struct ipmi_v2_payload * payload) { struct ipmi_rs * rsp = NULL; uint8_t * msg_data = NULL; int msg_length; struct ipmi_session * session = intf->session; struct ipmi_rq_entry * entry = NULL; int try = 0; int xmit = 1; time_t ltime; uint32_t saved_timeout; if (!intf->opened && intf->open && intf->open(intf) < 0) return NULL; /* * The session timeout is initialized in the above interface open, * so it will only be valid after the open completes. */ saved_timeout = session->timeout; while (try < intf->ssn_params.retry) { //ltime = time(NULL); if (xmit) { ltime = time(NULL); if (payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) { /* * Build an IPMI v1.5 or v2 command */ struct ipmi_rq * ipmi_request = payload->payload.ipmi_request.request; lprintf(LOG_DEBUG, ""); lprintf(LOG_DEBUG, ">> Sending IPMI command payload"); lprintf(LOG_DEBUG, ">> netfn : 0x%02x", ipmi_request->msg.netfn); lprintf(LOG_DEBUG, ">> command : 0x%02x", ipmi_request->msg.cmd); if (verbose > 1) { uint16_t i; fprintf(stderr, ">> data : "); for (i = 0; i < ipmi_request->msg.data_len; ++i) fprintf(stderr, "0x%02x ", ipmi_request->msg.data[i]); fprintf(stderr, "\n\n"); } /* * 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 { lprintf(LOG_DEBUG+1, "BUILDING A v1.5 COMMAND"); entry = ipmi_lanplus_build_v15_ipmi_cmd(intf, ipmi_request); } else { int isRetry = ( try > 0 ? 1 : 0 ); lprintf(LOG_DEBUG+1, "BUILDING A v2 COMMAND"); entry = ipmi_lanplus_build_v2x_ipmi_cmd(intf, ipmi_request, isRetry); } if (entry == NULL) { lprintf(LOG_ERR, "Aborting send command, unable to build"); return NULL; } msg_data = entry->msg_data; msg_length = entry->msg_len; } else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST) { lprintf(LOG_DEBUG, ">> SENDING AN OPEN SESSION REQUEST\n"); assert(session->v2_data.session_state == LANPLUS_STATE_PRESESSION || session->v2_data.session_state == LANPLUS_STATE_OPEN_SESSION_SENT); ipmi_lanplus_build_v2x_msg(intf, /* in */ payload, /* in */ &msg_length, /* out */ &msg_data, /* out */ 0); /* irrelevant for this msg*/ } else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_1) { lprintf(LOG_DEBUG, ">> SENDING A RAKP 1 MESSAGE\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 */ 0); /* irrelevant for this msg*/ } else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_3) { lprintf(LOG_DEBUG, ">> SENDING A RAKP 3 MESSAGE\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 */ 0); /* irrelevant for this msg*/ } else if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) { lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n"); assert(session->v2_data.session_state == LANPLUS_STATE_ACTIVE); ipmi_lanplus_build_v2x_msg(intf, /* in */ payload, /* in */ &msg_length, /* out */ &msg_data, /* out */ 0); /* irrelevant for this msg*/ } else { lprintf(LOG_ERR, "Payload type 0x%0x is unsupported!", payload->payload_type); assert(0); } if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) { lprintf(LOG_ERR, "IPMI LAN send command failed"); return NULL; } } /* if we are set to noanswer we do not expect response */ if (intf->noanswer) break; usleep(100); /* Not sure what this is for */ /* Remember our connection state */ switch (payload->payload_type) { case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_SENT; /* not retryable for timeouts, force no retry */ try = intf->ssn_params.retry; break; case IPMI_PAYLOAD_TYPE_RAKP_1: session->v2_data.session_state = LANPLUS_STATE_RAKP_1_SENT; /* not retryable for timeouts, force no retry */ try = intf->ssn_params.retry; break; case IPMI_PAYLOAD_TYPE_RAKP_3: /* not retryable for timeouts, force no retry */ try = intf->ssn_params.retry; session->v2_data.session_state = LANPLUS_STATE_RAKP_3_SENT; break; } /* * Special case for SOL outbound packets. */ if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) { if (! payload->payload.sol_packet.packet_sequence_number) { /* We're just sending an ACK. No need to retry. */ break; } rsp = ipmi_lanplus_recv_sol(intf); /* Grab the next packet */ if (!is_sol_packet(rsp)) { break; } if (sol_response_acks_packet(rsp, payload)) break; else if (is_sol_packet(rsp) && rsp->data_len) { /* * We're still waiting for our ACK, but we more data from * the BMC */ intf->session->sol_data.sol_input_handler(rsp); /* In order to avoid duplicate output, just set data_len to 0 */ rsp->data_len = 0; break; } } /* Non-SOL processing */ else { rsp = ipmi_lan_poll_recv(intf); /* Duplicate Request ccode most likely indicates a response to a previous retry. Ignore and keep polling. */ while ((rsp != NULL) && (rsp->ccode == 0xcf)) { rsp = NULL; rsp = ipmi_lan_poll_recv(intf); } if (rsp) break; /* This payload type is retryable for timeouts. */ if ((payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) && entry) { ipmi_req_remove_entry( entry->rq_seq, entry->req.msg.cmd); } } /* only timeout if time exceeds the timeout value */ xmit = ((time(NULL) - ltime) >= session->timeout); usleep(5000); if (xmit) { /* increment session timeout by 1 second each retry */ session->timeout++; } try++; } session->timeout = saved_timeout; /* IPMI messages are deleted under ipmi_lan_poll_recv() */ switch (payload->payload_type) { case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: case IPMI_PAYLOAD_TYPE_RAKP_1: case IPMI_PAYLOAD_TYPE_RAKP_3: case IPMI_PAYLOAD_TYPE_SOL: free(msg_data); msg_data = NULL; break; } return rsp; } /* * is_sol_partial_ack * * Determine if the response is a partial ACK/NACK that indicates * we need to resend part of our packet. * * returns the number of characters we need to resend, or * 0 if this isn't an ACK or we don't need to resend anything */ int is_sol_partial_ack( struct ipmi_intf * intf, struct ipmi_v2_payload * v2_payload, struct ipmi_rs * rs) { int chars_to_resend = 0; if (v2_payload && rs && is_sol_packet(rs) && sol_response_acks_packet(rs, v2_payload) && (rs->payload.sol_packet.accepted_character_count < v2_payload->payload.sol_packet.character_count)) { if (ipmi_oem_active(intf, "intelplus") && rs->payload.sol_packet.accepted_character_count == 0) return 0; chars_to_resend = v2_payload->payload.sol_packet.character_count - rs->payload.sol_packet.accepted_character_count; } return chars_to_resend; } /* * set_sol_packet_sequence_number */ static void set_sol_packet_sequence_number( struct ipmi_intf * intf, struct ipmi_v2_payload * v2_payload) { /* Keep our sequence number sane */ if (intf->session->sol_data.sequence_number > 0x0F) intf->session->sol_data.sequence_number = 1; v2_payload->payload.sol_packet.packet_sequence_number = intf->session->sol_data.sequence_number++; } /* * ipmi_lanplus_send_sol * * Sends a SOL packet.. We handle partial ACK/NACKs from the BMC here. * * Returns a pointer to the SOL ACK we received, or * 0 on failure * */ struct ipmi_rs * ipmi_lanplus_send_sol( struct ipmi_intf * intf, struct ipmi_v2_payload * v2_payload) { struct ipmi_rs * rs; /* * chars_to_resend indicates either that we got a NACK telling us * that we need to resend some part of our data. */ int chars_to_resend = 0; v2_payload->payload_type = IPMI_PAYLOAD_TYPE_SOL; /* * Payload length is just the length of the character * data here. */ v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; v2_payload->payload.sol_packet.acked_packet_number = 0; /* NA */ set_sol_packet_sequence_number(intf, v2_payload); v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */ rs = ipmi_lanplus_send_payload(intf, v2_payload); /* Determine if we need to resend some of our data */ chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); while (rs && !rs->payload.sol_packet.transfer_unavailable && !rs->payload.sol_packet.is_nack && chars_to_resend) { /* * We first need to handle any new data we might have * received in our NACK */ if (rs->data_len) intf->session->sol_data.sol_input_handler(rs); set_sol_packet_sequence_number(intf, v2_payload); /* Just send the required data */ memmove(v2_payload->payload.sol_packet.data, v2_payload->payload.sol_packet.data + rs->payload.sol_packet.accepted_character_count, chars_to_resend); v2_payload->payload.sol_packet.character_count = chars_to_resend; v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; rs = ipmi_lanplus_send_payload(intf, v2_payload); chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); } return rs; } /* * check_sol_packet_for_new_data * * Determine whether the SOL packet has already been seen * and whether the packet has new data for us. * * This function has the side effect of removing an previously * seen data, and moving new data to the front. * * It also "Remembers" the data so we don't get repeats. * * returns the number of new bytes in the SOL packet */ static int check_sol_packet_for_new_data( struct ipmi_intf * intf, struct ipmi_rs *rsp) { static uint8_t last_received_sequence_number = 0; static uint8_t last_received_byte_count = 0; int new_data_size = 0; if (rsp && (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) { /* Store the data length before we mod it */ uint8_t unaltered_data_len = rsp->data_len; if (rsp->payload.sol_packet.packet_sequence_number == last_received_sequence_number) { /* * This is the same as the last packet, but may include * extra data */ new_data_size = rsp->data_len - last_received_byte_count; if (new_data_size > 0) { /* We have more data to process */ memmove(rsp->data, rsp->data + rsp->data_len - new_data_size, new_data_size); } rsp->data_len = new_data_size; } /* *Rember the data for next round */ if (rsp->payload.sol_packet.packet_sequence_number) { last_received_sequence_number = rsp->payload.sol_packet.packet_sequence_number; last_received_byte_count = unaltered_data_len; } } return new_data_size; } /* * ack_sol_packet * * Provided the specified packet looks reasonable, ACK it. */ static void ack_sol_packet( struct ipmi_intf * intf, struct ipmi_rs * rsp) { if (rsp && (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && (rsp->payload.sol_packet.packet_sequence_number)) { struct ipmi_v2_payload ack; memset(&ack, 0, sizeof(struct ipmi_v2_payload)); ack.payload_type = IPMI_PAYLOAD_TYPE_SOL; /* * Payload length is just the length of the character * data here. */ ack.payload_length = 0; /* ACK packets have sequence numbers of 0 */ ack.payload.sol_packet.packet_sequence_number = 0; ack.payload.sol_packet.acked_packet_number = rsp->payload.sol_packet.packet_sequence_number; ack.payload.sol_packet.accepted_character_count = rsp->data_len; ipmi_lanplus_send_payload(intf, &ack); } } /* * ipmi_lanplus_recv_sol * * Receive a SOL packet and send an ACK in response. * */ struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf) { struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); if (rsp && rsp->session.authtype != 0) { ack_sol_packet(intf, rsp); /* * Remembers the data sent, and alters the data to just * include the new stuff. */ check_sol_packet_for_new_data(intf, rsp); } 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 IPMI 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; uint8_t msg_data[2]; uint8_t backupBridgePossible; backupBridgePossible = bridgePossible; bridgePossible = 0; msg_data[0] = IPMI_LAN_CHANNEL_E | 0x80; // Ask for IPMI v2 data as well msg_data[1] = intf->ssn_params.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 == NULL || rsp->ccode > 0) { /* * 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 == NULL) { lprintf(LOG_INFO, "Get Auth Capabilities error"); return 1; } if (rsp->ccode > 0) { lprintf(LOG_INFO, "Get Auth Capabilities error: %s", val2str(rsp->ccode, completion_code_vals)); return 1; } } memcpy(auth_cap, rsp->data, sizeof(struct get_channel_auth_cap_rsp)); bridgePossible = backupBridgePossible; return 0; } static int ipmi_close_session_cmd(struct ipmi_intf * intf) { struct ipmi_rs * rsp; struct ipmi_rq req; uint8_t msg_data[4]; uint32_t bmc_session_lsbf; uint8_t backupBridgePossible; if (intf->session == NULL || intf->session->v2_data.session_state != LANPLUS_STATE_ACTIVE) return -1; backupBridgePossible = bridgePossible; intf->target_addr = IPMI_BMC_SLAVE_ADDR; bridgePossible = 0; 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 == NULL) { /* Looks like the session was closed */ lprintf(LOG_ERR, "Close Session command failed"); return -1; } if (verbose > 2) printbuf(rsp->data, rsp->data_len, "close_session"); if (rsp->ccode == 0x87) { lprintf(LOG_ERR, "Failed to Close Session: invalid " "session ID %08lx", (long)intf->session->v2_data.bmc_id); return -1; } if (rsp->ccode > 0) { lprintf(LOG_ERR, "Close Session command failed: %s", val2str(rsp->ccode, completion_code_vals)); return -1; } lprintf(LOG_DEBUG, "Closed Session %08lx\n", (long)intf->session->v2_data.bmc_id); bridgePossible = backupBridgePossible; 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; uint8_t * msg; struct ipmi_rs * rsp; /* 0 = success, 1 = error, 2 = timeout */ int rc = 0; /* * Build an Open Session Request Payload */ msg = (uint8_t*)malloc(IPMI_OPEN_SESSION_REQUEST_SIZE); if (msg == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return 1; } memset(msg, 0, IPMI_OPEN_SESSION_REQUEST_SIZE); msg[0] = 0; /* Message tag */ if (ipmi_oem_active(intf, "intelplus") || intf->ssn_params.privlvl != IPMI_SESSION_PRIV_ADMIN) msg[1] = intf->ssn_params.privlvl; else 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; if (lanplus_get_requested_ciphers(intf->ssn_params.cipher_suite_id, &(session->v2_data.requested_auth_alg), &(session->v2_data.requested_integrity_alg), &(session->v2_data.requested_crypt_alg))) { lprintf(LOG_WARNING, "Unsupported cipher suite ID : %d\n", intf->ssn_params.cipher_suite_id); free(msg); msg = NULL; return 1; } /* * Authentication payload */ msg[8] = 0; /* specifies authentication payload */ msg[9] = 0; /* reserved */ msg[10] = 0; /* reserved */ msg[11] = 8; /* payload length */ msg[12] = session->v2_data.requested_auth_alg; 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] = session->v2_data.requested_integrity_alg; 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] = session->v2_data.requested_crypt_alg; 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); free(msg); msg = NULL; if (rsp == NULL ) { lprintf(LOG_DEBUG, "Timeout in open session response message."); return 2; } if (verbose) lanplus_dump_open_session_response(rsp); if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) { lprintf(LOG_WARNING, "Error in open session response message : %s\n", val2str(rsp->payload.open_session_response.rakp_return_code, ipmi_rakp_return_codes)); return 1; } else { if (rsp->payload.open_session_response.console_id != session->v2_data.console_id) { lprintf(LOG_WARNING, "Warning: Console session ID is not " "what we requested"); } 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; /* * Verify that we have agreed on a cipher suite */ if (rsp->payload.open_session_response.auth_alg != session->v2_data.requested_auth_alg) { lprintf(LOG_WARNING, "Authentication algorithm 0x%02x is " "not what we requested 0x%02x\n", rsp->payload.open_session_response.auth_alg, session->v2_data.requested_auth_alg); rc = 1; } else if (rsp->payload.open_session_response.integrity_alg != session->v2_data.requested_integrity_alg) { lprintf(LOG_WARNING, "Integrity algorithm 0x%02x is " "not what we requested 0x%02x\n", rsp->payload.open_session_response.integrity_alg, session->v2_data.requested_integrity_alg); rc = 1; } else if (rsp->payload.open_session_response.crypt_alg != session->v2_data.requested_crypt_alg) { lprintf(LOG_WARNING, "Encryption algorithm 0x%02x is " "not what we requested 0x%02x\n", rsp->payload.open_session_response.crypt_alg, session->v2_data.requested_crypt_alg); rc = 1; } } return rc; } /* * ipmi_lanplus_rakp1 * * Build and send the RAKP 1 message as part of the IPMI 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; uint8_t * msg; struct ipmi_rs * rsp; int rc = 0; /* 0 = success, 1 = error, 2 = timeout */ /* * Build a RAKP 1 message */ msg = (uint8_t*)malloc(IPMI_RAKP1_MESSAGE_SIZE); if (msg == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return 1; } 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; lprintf(LOG_ERR, "ERROR generating random number " "in ipmi_lanplus_rakp1"); free(msg); msg = NULL; 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] = intf->ssn_params.privlvl | intf->ssn_params.lookupbit; session->v2_data.requested_role = msg[24]; msg[25] = 0; /* reserved */ msg[26] = 0; /* reserved */ /* Username specification */ msg[27] = strlen((const char *)intf->ssn_params.username); if (msg[27] > IPMI_MAX_USER_NAME_LENGTH) { lprintf(LOG_ERR, "ERROR: user name too long. " "(Exceeds %d characters)", IPMI_MAX_USER_NAME_LENGTH); free(msg); msg = NULL; return 1; } memcpy(msg + 28, intf->ssn_params.username, msg[27]); v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_1; if (ipmi_oem_active(intf, "i82571spt")) { /* * The IPMI v2.0 spec hints on that all user name bytes * must be occupied (29:44). The Intel 82571 GbE refuses * to establish a session if this field is shorter. */ v2_payload.payload_length = IPMI_RAKP1_MESSAGE_SIZE; } else { v2_payload.payload_length = IPMI_RAKP1_MESSAGE_SIZE - (16 - msg[27]); } v2_payload.payload.rakp_1_message.message = msg; rsp = ipmi_lanplus_send_payload(intf, &v2_payload); free(msg); msg = NULL; if (rsp == NULL) { lprintf(LOG_WARNING, "> Error: no response from RAKP 1 message"); return 2; } 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) { lprintf(LOG_INFO, "RAKP 2 message indicates an error : %s", 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); if (verbose > 2) printbuf(session->v2_data.bmc_rand, 16, "bmc_rand"); /* * 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, intf)) { /* Error */ lprintf(LOG_INFO, "> RAKP 2 HMAC is invalid"); session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE; rc = 1; } 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 IPMI 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; uint8_t * msg; struct ipmi_rs * rsp; assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED); /* * Build a RAKP 3 message */ msg = (uint8_t*)malloc(IPMI_RAKP3_MESSAGE_MAX_SIZE); if (msg == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return 1; } 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_3_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) { uint32_t auth_length; if (lanplus_generate_rakp3_authcode(msg + 8, session, &auth_length, intf)) { /* Error */ lprintf(LOG_INFO, "> Error generating RAKP 3 authcode"); free(msg); msg = NULL; return 1; } else { /* Success */ v2_payload.payload_length += auth_length; } /* Generate our Session Integrity Key, K1, and K2 */ if (lanplus_generate_sik(session, intf)) { /* Error */ lprintf(LOG_INFO, "> Error generating session integrity key"); free(msg); msg = NULL; return 1; } else if (lanplus_generate_k1(session)) { /* Error */ lprintf(LOG_INFO, "> Error generating K1 key"); free(msg); msg = NULL; return 1; } else if (lanplus_generate_k2(session)) { /* Error */ lprintf(LOG_INFO, "> Error generating K1 key"); free(msg); msg = NULL; return 1; } } rsp = ipmi_lanplus_send_payload(intf, &v2_payload); free(msg); msg = NULL; 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 == NULL) { lprintf(LOG_WARNING, "> Error: no response from RAKP 3 message"); return 2; } /* * 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) { lprintf(LOG_INFO, "RAKP 4 message indicates an error : %s", 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, intf)) { /* Success */ session->v2_data.session_state = LANPLUS_STATE_ACTIVE; } else { /* Error */ lprintf(LOG_INFO, "> RAKP 4 message has invalid integrity check value"); return 1; } } intf->abort = 0; return 0; } /** * ipmi_lan_close */ void ipmi_lanplus_close(struct ipmi_intf * intf) { if (!intf->abort && intf->session) ipmi_close_session_cmd(intf); if (intf->fd >= 0) { close(intf->fd); intf->fd = -1; } ipmi_req_clear_entries(); ipmi_intf_session_cleanup(intf); intf->opened = 0; intf->manufacturer_id = IPMI_OEM_UNKNOWN; intf = NULL; } static int ipmi_set_session_privlvl_cmd(struct ipmi_intf * intf) { struct ipmi_rs * rsp; struct ipmi_rq req; uint8_t backupBridgePossible; uint8_t privlvl = intf->ssn_params.privlvl; if (privlvl <= IPMI_SESSION_PRIV_USER) return 0; /* no need to set higher */ backupBridgePossible = bridgePossible; bridgePossible = 0; memset(&req, 0, sizeof(req)); req.msg.netfn = IPMI_NETFN_APP; req.msg.cmd = 0x3b; req.msg.data = &privlvl; req.msg.data_len = 1; rsp = intf->sendrecv(intf, &req); if (rsp == NULL) { lprintf(LOG_ERR, "Set Session Privilege Level to %s failed", val2str(privlvl, ipmi_privlvl_vals)); bridgePossible = backupBridgePossible; return -1; } if (verbose > 2) printbuf(rsp->data, rsp->data_len, "set_session_privlvl"); if (rsp->ccode > 0) { lprintf(LOG_ERR, "Set Session Privilege Level to %s failed: %s", val2str(privlvl, ipmi_privlvl_vals), val2str(rsp->ccode, completion_code_vals)); bridgePossible = backupBridgePossible; return -1; } lprintf(LOG_DEBUG, "Set Session Privilege Level to %s\n", val2str(rsp->data[0], ipmi_privlvl_vals)); bridgePossible = backupBridgePossible; return 0; } /** * ipmi_lanplus_open */ int ipmi_lanplus_open(struct ipmi_intf * intf) { int rc; int retry; struct get_channel_auth_cap_rsp auth_cap; struct ipmi_session_params *params; struct ipmi_session *session; if (!intf) return -1; if (intf->opened) return intf->fd; params = &intf->ssn_params; if (!params->port) params->port = IPMI_LANPLUS_PORT; if (!params->privlvl) params->privlvl = IPMI_SESSION_PRIV_ADMIN; if (!params->timeout) params->timeout = IPMI_LAN_TIMEOUT; if (!params->retry) params->retry = IPMI_LAN_RETRY; if (params->hostname == NULL || strlen((const char *)params->hostname) == 0) { lprintf(LOG_ERR, "No hostname specified!"); return -1; } if (ipmi_intf_socket_connect(intf) == -1) { lprintf(LOG_ERR, "Could not open socket!"); goto fail; } session = (struct ipmi_session *)malloc(sizeof (struct ipmi_session)); if (!session) { lprintf(LOG_ERR, "ipmitool: malloc failure"); goto fail; } intf->session = session; /* Setup our lanplus session state */ memset(session, 0, sizeof(struct ipmi_session)); session->timeout = params->timeout; memcpy(&session->authcode, ¶ms->authcode_set, sizeof(session->authcode)); session->v2_data.auth_alg = IPMI_AUTH_RAKP_NONE; session->v2_data.crypt_alg = IPMI_CRYPT_NONE; session->sol_data.sequence_number = 1; intf->opened = 1; intf->abort = 1; /* * * Make sure the BMC supports IPMI v2 / RMCP+ */ if (!ipmi_oem_active(intf, "i82571spt") && ipmi_get_auth_capabilities_cmd(intf, &auth_cap)) { lprintf(LOG_INFO, "Error issuing Get Channel " "Authentication Capabilities request"); goto fail; } if (!ipmi_oem_active(intf, "i82571spt") && ! auth_cap.v20_data_available) { lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+"); goto fail; } /* * If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence * needs to restart. The individual messages are not individually retryable, * as the session state is advancing. */ for (retry = 0; retry < IPMI_LAN_RETRY; retry++) { session->v2_data.session_state = LANPLUS_STATE_PRESESSION; /* * Open session */ if ((rc = ipmi_lanplus_open_session(intf)) == 1) { goto fail; } if (rc == 2) { lprintf(LOG_DEBUG, "Retry lanplus open session, %d", retry); continue; } /* * RAKP 1 */ if ((rc = ipmi_lanplus_rakp1(intf)) == 1) { goto fail; } if (rc == 2) { lprintf(LOG_DEBUG, "Retry lanplus rakp1, %d", retry); continue; } /* * RAKP 3 */ if ((rc = ipmi_lanplus_rakp3(intf)) == 1) { goto fail; } if (rc == 0) break; lprintf(LOG_DEBUG,"Retry lanplus rakp3, %d", retry); } lprintf(LOG_DEBUG, "IPMIv2 / RMCP+ SESSION OPENED SUCCESSFULLY\n"); intf->abort = 0; if (!ipmi_oem_active(intf, "i82571spt")) { rc = ipmi_set_session_privlvl_cmd(intf); if (rc < 0) { goto fail; } /* automatically detect interface request and response sizes */ hpm2_detect_max_payload_size(intf); } bridgePossible = 1; if (!ipmi_oem_active(intf, "i82571spt")) { intf->manufacturer_id = ipmi_get_oem(intf); } return intf->fd; fail: lprintf(LOG_ERR, "Error: Unable to establish IPMI v2 / RMCP+ session"); intf->close(intf); return -1; } void test_crypt1(void) { uint8_t key[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; uint16_t bytes_encrypted; uint16_t bytes_decrypted; uint8_t decrypt_buffer[1000]; uint8_t encrypt_buffer[1000]; uint8_t 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)) { lprintf(LOG_ERR, "Encrypt test failed"); 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)) { lprintf(LOG_ERR, "Decrypt test failed\n"); assert(0); } printbuf(decrypt_buffer, bytes_decrypted, "decrypted payload"); lprintf(LOG_DEBUG, "\nDone testing the encrypt/decyrpt methods!\n"); exit(0); } void test_crypt2(void) { uint8_t key[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; uint8_t iv[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; uint8_t data[8] = "12345678"; uint8_t encrypt_buffer[1000]; uint8_t decrypt_buffer[1000]; uint32_t bytes_encrypted; uint32_t bytes_decrypted; printbuf((const uint8_t *)data, strlen((const char *)data), "input data"); lanplus_encrypt_aes_cbc_128(iv, key, data, strlen((const char *)data), encrypt_buffer, &bytes_encrypted); printbuf((const uint8_t *)encrypt_buffer, bytes_encrypted, "encrypt_buffer"); lanplus_decrypt_aes_cbc_128(iv, key, encrypt_buffer, bytes_encrypted, decrypt_buffer, &bytes_decrypted); printbuf((const uint8_t *)decrypt_buffer, bytes_decrypted, "decrypt_buffer"); lprintf(LOG_INFO, "\nDone testing the encrypt/decyrpt methods!\n"); exit(0); } /** * send a get device id command to keep session active */ static int ipmi_lanplus_keepalive(struct ipmi_intf * intf) { struct ipmi_rs * rsp; struct ipmi_rq req = { .msg = { .netfn = IPMI_NETFN_APP, .cmd = 1, } }; if (!intf->opened) return 0; rsp = intf->sendrecv(intf, &req); while (rsp != NULL && is_sol_packet(rsp)) { /* rsp was SOL data instead of our answer */ /* since it didn't go through the sol recv, do sol recv stuff here */ ack_sol_packet(intf, rsp); check_sol_packet_for_new_data(intf, rsp); if (rsp->data_len) intf->session->sol_data.sol_input_handler(rsp); rsp = ipmi_lan_poll_recv(intf); if (rsp == NULL) /* the get device id answer never got back, but retry mechanism was bypassed by SOL data */ return 0; /* so get device id command never returned, the connection is still alive */ } if (rsp == NULL) return -1; if (rsp->ccode > 0) return -1; return 0; } /** * ipmi_lanplus_setup */ static int ipmi_lanplus_setup(struct ipmi_intf * intf) { //test_crypt1(); assert("ipmi_lanplus_setup"); if (lanplus_seed_prng(16)) return -1; /* setup default LAN maximum request and response sizes */ intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE; intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE; return 0; } static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) { if (intf->ssn_params.cipher_suite_id == 3) { /* * encrypted payload can only be multiple of 16 bytes */ size &= ~15; /* * decrement payload size on confidentiality header size * plus minimal confidentiality trailer size */ size -= (16 + 1); } intf->max_request_data_size = size; } static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) { if (intf->ssn_params.cipher_suite_id == 3) { /* * encrypted payload can only be multiple of 16 bytes */ size &= ~15; /* * decrement payload size on confidentiality header size * plus minimal confidentiality trailer size */ size -= (16 + 1); } intf->max_response_data_size = size; }