1 #include <sys/socket.h>
2 #include <sys/un.h>
3 
4 #include "tmate.h"
5 
reset_and_enable_authorized_keys(void)6 static void reset_and_enable_authorized_keys(void)
7 {
8 	ssh_key *keys = tmate_session->authorized_keys;
9 	if (keys) {
10 		for (ssh_key *k = keys; *k; k++)
11 			ssh_key_free(*k);
12 		free(keys);
13 	}
14 
15 	keys = xreallocarray(NULL, sizeof(ssh_key), 1);
16 	keys[0] = NULL;
17 
18 	tmate_session->authorized_keys = keys;
19 }
20 
import_ssh_pubkey64(const char * _keystr)21 static ssh_key import_ssh_pubkey64(const char *_keystr)
22 {
23 	/* key is formatted as "type base64_key" */
24 
25 	char * const keystr = xstrdup(_keystr);
26 	char *s = keystr;
27 	ssh_key ret = NULL;
28 
29 	char *key_type = strsep(&s, " ");
30 	char *key_content = strsep(&s, " ");
31 
32 	if (!key_content)
33 		goto out;
34 
35 	enum ssh_keytypes_e type = ssh_key_type_from_name(key_type);
36 	if (type == SSH_KEYTYPE_UNKNOWN)
37 		goto out;
38 
39 	if (ssh_pki_import_pubkey_base64(key_content, type, &ret) != SSH_OK) {
40 		ret = NULL;
41 		goto out;
42 	}
43 out:
44 	free(keystr);
45 	return ret;
46 }
47 
get_num_authorized_keys(ssh_key * keys)48 int get_num_authorized_keys(ssh_key *keys)
49 {
50 	if (!keys)
51 		return 0;
52 
53 	int count = 0;
54 	for (ssh_key *k = keys; *k; k++)
55 		count++;
56 	return count;
57 }
58 
append_authorized_key(const char * keystr)59 static void append_authorized_key(const char *keystr)
60 {
61 	if (!tmate_session->authorized_keys)
62 		reset_and_enable_authorized_keys();
63 
64 	ssh_key pkey = import_ssh_pubkey64(keystr);
65 	if (!pkey)
66 		return;
67 
68 	ssh_key *keys = tmate_session->authorized_keys;
69 	int count = get_num_authorized_keys(keys);
70 	keys = xreallocarray(keys, sizeof(ssh_key), count+2);
71 	tmate_session->authorized_keys = keys;
72 
73 	keys[count++] = pkey;
74 	keys[count] = NULL;
75 }
76 
tmate_set(char * key,char * value)77 static void tmate_set(char *key, char *value)
78 {
79 	if (!strcmp(key, "authorized_keys"))
80 		append_authorized_key(value);
81 }
82 
tmate_hook_set_option_auth(const char * name,const char * val)83 void tmate_hook_set_option_auth(const char *name, const char *val)
84 {
85 	if (!strcmp(name, "tmate-authorized-keys")) {
86 		reset_and_enable_authorized_keys();
87 	} else if (!strcmp(name, "tmate-set")) {
88 		char *key_value = xstrdup(val);
89 		char *s = key_value;
90 
91 		char *key = strsep(&s, "=");
92 		char *value = s;
93 		if (value)
94 			tmate_set(key, value);
95 
96 		free(key_value);
97 	}
98 }
99 
tmate_allow_auth(const char * pubkey)100 bool tmate_allow_auth(const char *pubkey)
101 {
102 	/*
103 	 * Note that we don't accept connections on the tmux socket until we
104 	 * get the tmate ready message.
105 	 */
106 	if (!tmate_session->authorized_keys)
107 		return true;
108 
109 	if (!pubkey)
110 		return false;
111 
112 	ssh_key client_pkey = import_ssh_pubkey64(pubkey);
113 	if (!client_pkey)
114 		return false;
115 
116 	bool ret = false;
117 	for (ssh_key *k = tmate_session->authorized_keys; *k; k++) {
118 		if (!ssh_key_cmp(client_pkey, *k, SSH_KEY_CMP_PUBLIC)) {
119 			ret = true;
120 			break;
121 		}
122 	}
123 
124 	ssh_key_free(client_pkey);
125 
126 	return ret;
127 }
128 
write_all(int fd,const char * buf,size_t len)129 static int write_all(int fd, const char *buf, size_t len)
130 {
131 	for (size_t i = 0; i < len;)  {
132 		size_t ret = write(fd, buf+i, len-i);
133 		if (ret <= 0)
134 			return -1;
135 		i += ret;
136 	}
137 	return 0;
138 }
139 
read_all(int fd,char * buf,size_t len)140 static int read_all(int fd, char *buf, size_t len)
141 {
142 	for (size_t i = 0; i < len;)  {
143 		size_t ret = read(fd, buf+i, len-i);
144 		if (ret <= 0)
145 			return -1;
146 		i += ret;
147 	}
148 
149 	return 0;
150 }
151 
152 /*
153  * The following is executed in the context of the SSH server
154  */
would_tmate_session_allow_auth(const char * token,const char * pubkey)155 bool would_tmate_session_allow_auth(const char *token, const char *pubkey)
156 {
157 	/*
158 	 * The existance of this function is a bit unpleasant:
159 	 * In order to have the right SSH public key from the SSH client,
160 	 * we need to ask the tmate session for a match. Denying the key
161 	 * to the SSH client will make it cycle through its keys.
162 	 * We briefly connect to the session to get an answer.
163 	 *
164 	 * Note that the client will get reauthenticated later (see
165 	 * server-client.c when identifying the client).
166 	 */
167 	int sock_fd = -1;
168 	int ret = true;
169 
170 	if (tmate_validate_session_token(token) < 0)
171 		goto out;
172 
173 	char *sock_path = get_socket_path(token);
174 
175 	struct sockaddr_un sa;
176 	memset(&sa, 0, sizeof(sa));
177 	sa.sun_family = AF_UNIX;
178 	size_t size = strlcpy(sa.sun_path, sock_path, sizeof(sa.sun_path));
179 	free(sock_path);
180 	if (size >= sizeof sa.sun_path)
181 		goto out;
182 
183 	sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
184 	if (sock_fd < 0)
185 		goto out;
186 
187 	if (connect(sock_fd, (struct sockaddr *)&sa, sizeof sa) == -1)
188 		goto out;
189 
190 	struct imsg_hdr hdr = {
191 		.type = pubkey ? MSG_IDENTIFY_TMATE_AUTH_PUBKEY :
192 				 MSG_IDENTIFY_TMATE_AUTH_NONE,
193 		.len = IMSG_HEADER_SIZE + (pubkey ? strlen(pubkey)+1 : 0),
194 		.flags = 0,
195 		.peerid = PROTOCOL_VERSION,
196 		.pid = -1,
197 	};
198 
199 	if (write_all(sock_fd, (void*)&hdr, sizeof(hdr)) < 0)
200 		goto out;
201 
202 	if (pubkey) {
203 		if (write_all(sock_fd, pubkey, strlen(pubkey)+1) < 0)
204 			goto out;
205 	}
206 
207 	struct {
208 		struct imsg_hdr hdr;
209 		bool allow;
210 	} __packed recv_msg;
211 
212 	if (read_all(sock_fd, (void*)&recv_msg, sizeof(recv_msg)) < 0)
213 		goto out;
214 
215 	if (recv_msg.hdr.type == MSG_TMATE_AUTH_STATUS &&
216 	    recv_msg.hdr.len == sizeof(recv_msg))
217 		ret = recv_msg.allow;
218 
219 	tmate_debug("(preauth) allow=%d", ret);
220 
221 out:
222 	if (sock_fd != -1)
223 		close(sock_fd);
224 	return ret;
225 }
226