/* See LICENSE file for copyright and license details. */ #include "common.h" struct response { union { struct { uint8_t status; uint8_t length_of_reason; /* Only used in Failed, and not in Authenticate */ uint16_t protocol_major_version; /* Not in Authenticate */ uint16_t protocol_minor_version; /* Not in Authenticate */ uint16_t length_of_addition_data; char additional_data[]; }; struct { uint8_t __status__1; uint8_t __length_of_reason__1; uint16_t __protocol_major_version__1; uint16_t __protocol_minor_version__1; uint16_t __length_of_addition_data__1; char reason[]; }; struct { uint8_t __status__2; uint8_t __length_of_reason__2; uint16_t __protocol_major_version__2; uint16_t __protocol_minor_version__2; uint16_t __length_of_addition_data__2; uint32_t release_number; uint32_t resource_id_base; uint32_t resource_id_mask; uint32_t motion_buffer_size; uint16_t length_of_vendor; uint16_t maximum_request_length; uint8_t number_of_roots; uint8_t number_of_pixmap_formats; uint8_t image_byte_order; uint8_t bitmap_format_bit_order; uint8_t bitmap_format_scanline_unit; uint8_t bitmap_format_scanline_pad; uint8_t min_keycode; uint8_t max_keycode; uint32_t __unused; char vendor[]; }; }; }; int libaxl_receive_handshake(LIBAXL_CONTEXT *restrict ctx, int *restrict majorp, int *restrict minorp, char **restrict reasonp, int flags) { LIBAXL_CONNECTION *conn = ctx->conn; char *restrict inbuf, *new; size_t i, j, k, n, t, len, vendor_len, out_off, in_off; ssize_t r; int read_stage = 0, saved_errno, status; struct response *resp; uint32_t xid_base, xid_mask; struct libaxl_screen *screen; struct libaxl_depth *depth; #ifdef MSG_TRUNC int flag_trunc; #endif #ifdef MSG_TRUNC flags ^= flag_trunc = flags & MSG_TRUNC; #endif WLOCK_CONNECTION_RECV(conn); inbuf = conn->in_buf; n = offsetof(struct response, additional_data); continue_read: if (conn->in_buf_size < n) { inbuf = liberror_realloc(inbuf, n); if (!inbuf) { WUNLOCK_CONNECTION_RECV(conn); return LIBAXL_ERROR_SYSTEM; } conn->in_buf = inbuf; conn->in_buf_size = n; } while (conn->in_progress < n) { r = recv(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags); if (r <= 0) { liberror_save_backtrace(NULL); if (!r) { liberror_set_error("The connection to the display server has been closed", "libaxl_receive_handshake", "libaxl", LIBAXL_ERROR_CONNECTION_CLOSED); WUNLOCK_CONNECTION_RECV(conn); return LIBAXL_ERROR_CONNECTION_CLOSED; } liberror_recv_failed(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags, ""); WUNLOCK_CONNECTION_RECV(conn); return LIBAXL_ERROR_SYSTEM; } conn->in_progress += (size_t)r; } if (read_stage == 0) { n += (size_t)ntohs(((struct response *)inbuf)->length_of_addition_data) * 4; read_stage = 1; goto continue_read; } conn->in_progress = 0; #ifdef MSG_TRUNC if (flag_trunc) { WUNLOCK_CONNECTION_RECV(conn); return 0; } #endif t = conn->in_buf_size; conn->in_buf_size = ctx->in_buf_size; ctx->in_buf_size = t; conn->in_buf = ctx->in_buf; ctx->in_buf = inbuf; WUNLOCK_CONNECTION_RECV(conn); resp = (struct response *)inbuf; if (majorp) *majorp = (int)ntohs(resp->protocol_major_version); if (minorp) *minorp = (int)ntohs(resp->protocol_minor_version); if (reasonp) *reasonp = NULL; status = (int)resp->status; switch (resp->status) { case LIBAXL_HANDSHAKE_FAILED: len = (size_t)ntohs(resp->length_of_reason); if (n < len + offsetof(struct response, reason)) goto invalid; goto return_reason; case LIBAXL_HANDSHAKE_AUTHENTICATE: len = n - offsetof(struct response, reason); return_reason: if (reasonp) { *reasonp = liberror_malloc(len + 1); if (!*reasonp) return LIBAXL_ERROR_SYSTEM; memcpy(*reasonp, resp->reason, len); (*reasonp)[len] = '\0'; } break; case LIBAXL_HANDSHAKE_SUCCESS: xid_base = ntohl(resp->resource_id_base); xid_mask = ntohl(resp->resource_id_mask); if (xid_base & xid_mask || (xid_base | xid_mask) >> 29) goto invalid; conn->xid_base = xid_base; for (conn->xid_shift = 0; !(xid_mask & 1); conn->xid_shift += 1) xid_mask >>= 1; conn->xid_max = xid_mask; conn->info.vendor_release = ntohl(resp->release_number); conn->info.min_keycode = resp->min_keycode; conn->info.max_keycode = resp->max_keycode; conn->info.image_byte_order = resp->image_byte_order; conn->info.motion_buffer_size = ntohl(resp->motion_buffer_size); conn->info.maximum_request_length = resp->maximum_request_length = ntohs(resp->maximum_request_length); conn->info.bitmap_format_bit_order = resp->bitmap_format_bit_order; conn->info.bitmap_format_scanline_unit = resp->bitmap_format_scanline_unit; conn->info.bitmap_format_scanline_pad = resp->bitmap_format_scanline_pad; /* These restricts are less restrictive than the protocol specifices, * in case the they are modified later on */ if (!xid_mask || conn->xid_max + 1 < UINT16_C(1) << 18 || (conn->xid_max & (conn->xid_max + 1)) || resp->min_keycode < 8 || resp->image_byte_order > 1 || resp->maximum_request_length < 4096 || resp->bitmap_format_bit_order > 1 || resp->bitmap_format_scanline_unit % 8 || resp->bitmap_format_scanline_pad % 8 || resp->bitmap_format_scanline_unit > resp->bitmap_format_scanline_pad) goto invalid; vendor_len = ntohs(resp->length_of_vendor); conn->info.nscreens = resp->number_of_roots; conn->info.nformats = resp->number_of_pixmap_formats; memmove(inbuf, resp->vendor, out_off = in_off = vendor_len); inbuf[out_off++] = '\0'; out_off += (4 - out_off % 4) % 4; in_off += (4 - in_off % 4) % 4; in_off += offsetof(struct response, vendor); t = out_off; memmove(&inbuf[out_off], &inbuf[in_off], n - in_off); out_off += n - in_off; saved_errno = errno; new = realloc(inbuf, out_off); errno = saved_errno; if (new) inbuf = new; conn->info_buf = inbuf; conn->info.vendor = inbuf; conn->info.formats = (void *)&inbuf[t]; conn->info.screens = screen = (void *)&inbuf[t + conn->info.nformats * 8]; if ((size_t)conn->info.default_screen_number < conn->info.nscreens) { conn->info.default_screen = conn->info.screens; for (i = 0; i < (size_t)conn->info.default_screen_number; i++) conn->info.default_screen = libaxl_next_screen(conn->info.default_screen); } else { conn->info.default_screen = NULL; } for (i = 0; i < (size_t)conn->info.nscreens; i++) { screen->root = ntohl(screen->root); screen->default_colormap = ntohl(screen->default_colormap); screen->white_pixel = ntohl(screen->white_pixel); screen->black_pixel = ntohl(screen->black_pixel); screen->current_input_masks = ntohl(screen->current_input_masks); screen->width_in_pixels = ntohs(screen->width_in_pixels); screen->height_in_pixels = ntohs(screen->height_in_pixels); screen->width_in_millimeters = ntohs(screen->width_in_millimeters); screen->height_in_millimeters = ntohs(screen->height_in_millimeters); screen->min_installed_maps = ntohs(screen->min_installed_maps); screen->max_installed_maps = ntohs(screen->max_installed_maps); screen->root_visual = ntohs(screen->root_visual); depth = screen->allowed_depths; for (j = 0; j < (size_t)screen->number_of_allowed_depths; j++) { depth->number_of_visuals = ntohs(depth->number_of_visuals); for (k = 0; k < (size_t)depth->number_of_visuals; k++) { depth->visuals[k].visual_id = ntohl(depth->visuals[k].visual_id); depth->visuals[k].colormap_entries = ntohs(depth->visuals[k].colormap_entries); depth->visuals[k].red_mask = ntohl(depth->visuals[k].red_mask); depth->visuals[k].green_mask = ntohl(depth->visuals[k].green_mask); depth->visuals[k].blue_mask = ntohl(depth->visuals[k].blue_mask); } depth = (void *)(uintptr_t)&depth->visuals[depth->number_of_visuals]; } screen = (void *)(uintptr_t)depth; } ctx->in_buf_size = 0; ctx->in_buf = NULL; break; default: invalid: liberror_set_error("Invalid handshake response received from the display server", "libaxl_receive_handshake", "libaxl", LIBAXL_ERROR_INVALID_HANDSHAKE_RESPONSE); errno = EBADMSG; return LIBAXL_ERROR_INVALID_HANDSHAKE_RESPONSE; } return status; }