Commit message: session: Fix buffer overflow in ipmi_get_session_info

Partial fix for CVE-2020-5208, see
https://github.com/ipmitool/ipmitool/security/advisories/GHSA-g659-9qxw-p7cp

The `ipmi_get_session_info` function does not properly check the
response `data_len`, which is used as a copy size, allowing stack buffer
overflow.
CVE: CVE-2020-5208
CWE: CWE-120
Vulnerability description: It's been found that multiple functions in ipmitool before 1.8.19 neglect proper checking of the data received from a remote LAN party, which may lead to buffer overflows and potentially to remote code execution on the ipmitool side. This is especially dangerous if ipmitool is run as a privileged user. This problem is fixed in version 1.8.19.

Project: ipmitool
File: ipmi_session.c

ipmi_session_main(struct ipmi_intf * intf, int argc, char ** argv)
{
	int retval = 0;

	if (argc == 0 || strncmp(argv[0], "help", 4) == 0)
	{
		printf_session_usage();
	}
	else if (strncmp(argv[0], "info", 4) == 0)
	{

		if ((argc < 2) || strncmp(argv[1], "help", 4) == 0)
		{
				printf_session_usage();
		}
		else
		{
			Ipmi_Session_Request_Type session_request_type = 0;
			uint32_t                  id_or_handle = 0;

			if (strncmp(argv[1], "active", 6) == 0)
				session_request_type = IPMI_SESSION_REQUEST_CURRENT;
			else if (strncmp(argv[1], "all", 3) == 0)
				session_request_type = IPMI_SESSION_REQUEST_ALL;
			else if (strncmp(argv[1], "id", 2) == 0)
			{
				if (argc >= 3)
				{
					session_request_type = IPMI_SESSION_REQUEST_BY_ID;
					if (str2uint(argv[2], &id_or_handle) != 0) {
						lprintf(LOG_ERR, "HEX number expected, but '%s' given.",
								argv[2]);
						printf_session_usage();
						retval = -1;
					}
				}
				else
				{
					lprintf(LOG_ERR, "Missing id argument");
					printf_session_usage();
					retval = -1;
				}
			}
			else if (strncmp(argv[1], "handle", 6) == 0)
			{
				if (argc >= 3)
				{
					session_request_type = IPMI_SESSION_REQUEST_BY_HANDLE;
					if (str2uint(argv[2], &id_or_handle) != 0) {
						lprintf(LOG_ERR, "HEX number expected, but '%s' given.",
								argv[2]);
						printf_session_usage();
						retval = -1;
					}
				}
				else
				{
					lprintf(LOG_ERR, "Missing handle argument");
					printf_session_usage();
					retval = -1;
				}
			}
			else
			{
				lprintf(LOG_ERR, "Invalid SESSION info parameter: %s", argv[1]);
				printf_session_usage();
				retval = -1;
			}
			

			if (retval == 0)
				retval = ipmi_get_session_info(intf,
											   session_request_type,
											   id_or_handle);
		}
	}
	else
	{
		lprintf(LOG_ERR, "Invalid SESSION command: %s", argv[0]);
		printf_session_usage();
		retval = -1;
	}

	return retval;
}

Project: ipmitool
File: ipmi_session.c

printf_session_usage(void)
{
	lprintf(LOG_NOTICE, "Session Commands: info <active | all | id 0xnnnnnnnn | handle 0xnn>");
}

Project: ipmitool
File: ipmi_session.c

print_session_info_csv(const struct  get_session_info_rsp * session_info,
					   int data_len)
{
	char     buffer[18];
	uint16_t console_port_tmp;
	
	printf("%d", session_info->session_handle);
	printf(",%d", session_info->session_slot_count);
	printf(",%d", session_info->active_session_count);

	if (data_len == 3)
	{
		/* There is no session data here*/
		printf("\n");
		return;
	}

	printf(",%d", session_info->user_id);
	printf(",%s", val2str(session_info->privilege_level, ipmi_privlvl_vals));

	printf(",%s", session_info->auxiliary_data?
		   "IPMIv2/RMCP+" : "IPMIv1.5");

	printf(",0x%02x", session_info->channel_number);

	if (data_len == 18)
	{
		/* We have 802.3 LAN data */
		printf(",%s",
			   inet_ntop(AF_INET,
						 &(session_info->channel_data.lan_data.console_ip),
						 buffer,
						 16));

		printf(",%s", mac2str(
			session_info->channel_data.lan_data.console_mac));

		console_port_tmp = session_info->channel_data.lan_data.console_port;
		#if WORDS_BIGENDIAN
		console_port_tmp = BSWAP_16(console_port_tmp);
		#endif
		printf(",%d", console_port_tmp);
	}


	else if ((data_len == 12) || (data_len == 14))
	{
		/* Channel async serial/modem */
		printf(",%s",
			   val2str(session_info->channel_data.modem_data.session_channel_activity_type,
					   ipmi_channel_activity_type_vals));

		printf(",%d",
			   session_info->channel_data.modem_data.destination_selector);

		printf(",%s",
			   inet_ntop(AF_INET,
						 &(session_info->channel_data.modem_data.console_ip),
						 buffer,
						 16));

		if (data_len == 14)
		{
			/* Connection is PPP */
			console_port_tmp = session_info->channel_data.lan_data.console_port;
			#if WORDS_BIGENDIAN
			console_port_tmp = BSWAP_16(console_port_tmp);
			#endif
			printf(",%d", console_port_tmp);
		}
	}

	printf("\n");
}

Project: ipmitool
File: ipmi_session.c

ipmi_get_session_info(struct ipmi_intf         * intf,
					  Ipmi_Session_Request_Type  session_request_type,
					  uint32_t                   id_or_handle)
{
	int i, retval = 0;

	struct ipmi_rs * rsp;
	struct ipmi_rq req;
	uint8_t rqdata[5]; //  max length of the variable length request
	struct get_session_info_rsp   session_info;

	memset(&req, 0, sizeof(req));
	memset(&session_info, 0, sizeof(session_info));
	req.msg.netfn = IPMI_NETFN_APP;        // 0x06
	req.msg.cmd   = IPMI_GET_SESSION_INFO; // 0x3D
	req.msg.data = rqdata;

	switch (session_request_type)
	{
		
	case IPMI_SESSION_REQUEST_CURRENT:
	case IPMI_SESSION_REQUEST_BY_ID:	
	case IPMI_SESSION_REQUEST_BY_HANDLE:
		switch (session_request_type)
		{
		case IPMI_SESSION_REQUEST_CURRENT:
			rqdata[0]        = 0x00;
			req.msg.data_len = 1;
			break;
		case IPMI_SESSION_REQUEST_BY_ID:	
			rqdata[0]        = 0xFF;
			rqdata[1]        = id_or_handle         & 0x000000FF;
			rqdata[2]        = (id_or_handle >> 8)  & 0x000000FF;
			rqdata[3]        = (id_or_handle >> 16) & 0x000000FF;
			rqdata[4]        = (id_or_handle >> 24) & 0x000000FF;
			req.msg.data_len = 5;
			break;
		case IPMI_SESSION_REQUEST_BY_HANDLE:
			rqdata[0]        = 0xFE;
			rqdata[1]        = (uint8_t)id_or_handle;
			req.msg.data_len = 2;
			break;
		case IPMI_SESSION_REQUEST_ALL:
			break;
		}

		rsp = intf->sendrecv(intf, &req);
		if (!rsp)
		{
			lprintf(LOG_ERR, "Get Session Info command failed");
			retval = -1;
		}
		else if (rsp->ccode)
		{
			lprintf(LOG_ERR, "Get Session Info command failed: %s",
				val2str(rsp->ccode, completion_code_vals));
			retval = -1;
		}

		if (retval < 0)
		{
			if ((session_request_type == IPMI_SESSION_REQUEST_CURRENT) &&
			    (strncmp(intf->name, "lan", 3) != 0))
				lprintf(LOG_ERR, "It is likely that the channel in use "
					"does not support sessions");
		}
		else
		{
			memcpy(&session_info,  rsp->data,
			       __min(rsp->data_len, sizeof(session_info)));
			print_session_info(&session_info,
			                   __min(rsp->data_len, sizeof(session_info)));
		}
		break;
		
	case IPMI_SESSION_REQUEST_ALL:
		req.msg.data_len = 1;
		i = 1;
		do
		{
			rqdata[0] = i++;
			rsp = intf->sendrecv(intf, &req);
			
			if (!rsp)
			{
				lprintf(LOG_ERR, "Get Session Info command failed");
				retval = -1;
				break;
			}
			else if (rsp->ccode && rsp->ccode != 0xCC && rsp->ccode != 0xCB)
			{
				lprintf(LOG_ERR, "Get Session Info command failed: %s",
					val2str(rsp->ccode, completion_code_vals));
				retval = -1;
				break;
			}
			else if (rsp->data_len < 3)
			{
				retval = -1;
				break;
			}

			memcpy(&session_info,  rsp->data,
			       __min(rsp->data_len, sizeof(session_info)));
			print_session_info(&session_info,
			                   __min(rsp->data_len, sizeof(session_info)));
			
		} while (i <= session_info.session_slot_count);
		break;
	}

	return retval;
}

Project: ipmitool
File: ipmi_session.c

static void print_session_info(const struct  get_session_info_rsp * session_info,
							   int data_len)
{
	if (csv_output)
		print_session_info_csv(session_info, data_len);
	else
		print_session_info_verbose(session_info, data_len);
}

Project: ipmitool
File: ipmi_session.c

print_session_info_verbose(const struct  get_session_info_rsp * session_info,
						   int data_len)
{
	char     buffer[18];
	uint16_t console_port_tmp;
	
	printf("session handle                : %d\n", session_info->session_handle);
	printf("slot count                    : %d\n", session_info->session_slot_count);
	printf("active sessions               : %d\n", session_info->active_session_count);

	if (data_len == 3)
	{
		/* There is no session data here */
		printf("\n");
		return;
	}

	printf("user id                       : %d\n", session_info->user_id);
	printf("privilege level               : %s\n",
		   val2str(session_info->privilege_level, ipmi_privlvl_vals));
	
	printf("session type                  : %s\n", session_info->auxiliary_data?
		   "IPMIv2/RMCP+" : "IPMIv1.5");

	printf("channel number                : 0x%02x\n", session_info->channel_number);

	
	if (data_len == 18)
	{
		/* We have 802.3 LAN data */
		printf("console ip                    : %s\n",
			   inet_ntop(AF_INET,
						 &(session_info->channel_data.lan_data.console_ip),
						 buffer,
						 16));

		printf("console mac                   : %s\n", mac2str(
			session_info->channel_data.lan_data.console_mac));

		console_port_tmp = session_info->channel_data.lan_data.console_port;
		#if WORDS_BIGENDIAN
		console_port_tmp = BSWAP_16(console_port_tmp);
		#endif
		printf("console port                  : %d\n", console_port_tmp);
	}


	else if ((data_len == 12) || (data_len == 14))
	{
		/* Channel async serial/modem */
		printf("Session/Channel Activity Type : %s\n",
			   val2str(session_info->channel_data.modem_data.session_channel_activity_type,
					   ipmi_channel_activity_type_vals));

		printf("Destination selector          : %d\n",
			   session_info->channel_data.modem_data.destination_selector);

		printf("console ip                    : %s\n",
			   inet_ntop(AF_INET,
						 &(session_info->channel_data.modem_data.console_ip),
						 buffer,
						 16));

		if (data_len == 14)
		{
			/* Connection is PPP */
			console_port_tmp = session_info->channel_data.lan_data.console_port;
			#if WORDS_BIGENDIAN
			console_port_tmp = BSWAP_16(console_port_tmp);
			#endif
			printf("console port                  : %d\n", console_port_tmp);
		}
	}

	printf("\n");
}