aboutsummaryrefslogtreecommitdiffstats
path: root/libaxl_send_handshake.c
blob: 99966fdc216d15e5004a46f816dda24ce383989e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* See LICENSE file for copyright and license details. */
#include "common.h"

static int
send_all(int fd, void *restrict data, size_t n, int flags, size_t *restrict progressp)
{
	char *bs = data;
	ssize_t r;

	while (n) {
		r = liberror_send(fd, bs, n, flags, "<display server>");
		if (r < 0)
			return -1;
		bs += r, n -= (size_t)r;
		*progressp += (size_t)r;
	}

	return 0;
}

int
libaxl_send_handshake(LIBAXL_CONTEXT *restrict ctx, const char *auth_name, size_t auth_name_len,
                      const char *auth_data, size_t auth_data_len, int flags)
{
	LIBAXL_CONNECTION *conn = ctx->conn;
	LIBAXL_CONTEXT *pending, **pendingp;
	size_t len, o = 0;
	char *buf;

	RLOCK_CONNECTION_SEND(conn);
	pending = conn->pending_out;
	conn->info.protocol_version_major = LIBAXL_PROTOCOL_VERSION_MAJOR;
	conn->info.protocol_version_minor = LIBAXL_PROTOCOL_VERSION_MINOR;
	conn->info.protocol_version       = LIBAXL_PROTOCOL_VERSION;
	conn->info.protocol_byte_order    = LIBAXL_MSB_FIRST; /* XXX Use LIBAXL_LSB_FIRST if preferable */
	RUNLOCK_CONNECTION_SEND(conn);
	if (pending) {
		liberror_save_backtrace(NULL);
		liberror_set_error(pending == ctx
		                   ? "Request from the previous call is pending to be flush"
		                   : "A request from another thread is pending to be flushed",
		                   "libaxl_send_handshake", "errno", EALREADY);
		errno = EALREADY;
		return LIBAXL_ERROR_SYSTEM;
	}

	if (auth_name_len > UINT16_MAX) {
		liberror_save_backtrace(NULL);
		liberror_set_error("Authorisation protocol name is too long", "libaxl_send_handshake", "errno", EINVAL);
		errno = EINVAL;
		return LIBAXL_ERROR_SYSTEM;
	}

	if (auth_data_len > UINT16_MAX) {
		liberror_save_backtrace(NULL);
		liberror_set_error("Authorisation data is too long", "libaxl_send_handshake", "errno", EINVAL);
		errno = EINVAL;
		return LIBAXL_ERROR_SYSTEM;
	}

	buf = ctx->out_buf;
	len = 12 + (auth_name_len + 3) / 4 * 4 + (auth_data_len + 3) / 4 * 4;
	if (len > ctx->out_buf_size) {
			buf = liberror_realloc(buf, len);
			if (!buf)
				return LIBAXL_ERROR_SYSTEM;
			ctx->out_buf = buf;
			ctx->out_buf_size = len;
	}

	buf[o++] = 'B'; /* XXX Use 'l' (LSB first) if preferable */
	buf[o++] = 0;
	*(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_VERSION_MAJOR);
	o += 2;
	*(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_VERSION_MINOR);
	o += 2;
	*(uint16_t *)&buf[o] = htons((uint16_t)auth_name_len);
	o += 2;
	*(uint16_t *)&buf[o] = htons((uint16_t)auth_data_len);
	o += 2;
	buf[o++] = 0;
	buf[o++] = 0;
	memcpy(&buf[o], auth_name, auth_name_len);
	for (o += auth_name_len; o % 4; o++)
		buf[o] = 0;
	memcpy(&buf[o], auth_data, auth_data_len);
	for (o += auth_data_len; o % 4; o++)
		buf[o] = 0;

	ctx->out_length = o;
	ctx->out_progress = 0;

	WLOCK_CONNECTION_SEND(conn);

	if (send_all(conn->fd, ctx->out_buf, ctx->out_length, flags, &ctx->out_progress)) {
		ctx->next_pending_out = NULL;
		pendingp = &conn->pending_out;
		while (*pendingp)
			pendingp = &(*pendingp)->next_pending_out;
		*pendingp = ctx;
		ctx->refcount += 1;

		WUNLOCK_CONNECTION_SEND(conn);

		liberror_save_backtrace(NULL);
		liberror_set_error("Handshake has been buffered and is ready to be sent",
		                   "libaxl_send_request", "errno", EINPROGRESS);
		errno = EINPROGRESS;
		return LIBAXL_ERROR_SYSTEM;
	}

	WUNLOCK_CONNECTION_SEND(conn);

	return 0;
}