1 /*
2 * ProFTPD - mod_sftp agent
3 * Copyright (c) 2012-2020 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "mod_sftp.h"
26 #include "agent.h"
27 #include "msg.h"
28
29 const char *trace_channel = "ssh2";
30
31 /* These values from https://tools.ietf.org/html/draft-miller-ssh-agent-04
32 */
33 #define SFTP_SSH_AGENT_FAILURE 5
34 #define SFTP_SSH_AGENT_SUCCESS 6
35
36 #define SFTP_SSH_AGENT_REQ_IDS 11
37 #define SFTP_SSH_AGENT_RESP_IDS 12
38
39 #define SFTP_SSH_AGENT_REQ_SIGN_DATA 13
40 #define SFTP_SSH_AGENT_RESP_SIGN_DATA 14
41
42 #define SFTP_SSH_AGENT_EXTENDED_FAILURE 30
43
44 /* Error code for ssh.com's ssh-agent2 process. */
45 #define SFTP_SSHCOM_AGENT_FAILURE 102
46
47 /* For RFC 8332 support. */
48 #define SFTP_SSH_AGENT_SIGN_RSA_SHA256 2
49 #define SFTP_SSH_AGENT_SIGN_RSA_SHA512 4
50
51 /* Size of the buffer we use to talk to the agent. */
52 #define AGENT_REQUEST_MSGSZ 1024
53
54 /* Max size of the agent reply that we will handle. */
55 #define AGENT_REPLY_MAXSZ (256 * 1024)
56
57 /* Max number of identities/keys we're willing to handle at one time. */
58 #define AGENT_MAX_KEYS 1024
59
60 /* In sftp_keys_get_hostkey(), when dealing with the key data returned
61 * from the agent, use get_pkey_from_data() to create the EVP_PKEY. Keep
62 * the key_data around, for signing requests to send to the agent.
63 */
64
agent_failure(char resp_status)65 static int agent_failure(char resp_status) {
66 int failed = FALSE;
67
68 switch (resp_status) {
69 case SFTP_SSH_AGENT_FAILURE:
70 failed = TRUE;
71 break;
72
73 case SFTP_SSH_AGENT_EXTENDED_FAILURE:
74 failed = TRUE;
75 break;
76
77 case SFTP_SSHCOM_AGENT_FAILURE:
78 failed = TRUE;
79 break;
80 }
81
82 return failed;
83 }
84
agent_request(pool * p,int fd,const char * path,unsigned char * req,uint32_t reqlen,uint32_t * resplen)85 static unsigned char *agent_request(pool *p, int fd, const char *path,
86 unsigned char *req, uint32_t reqlen, uint32_t *resplen) {
87 unsigned char msg[AGENT_REQUEST_MSGSZ], *buf, *ptr;
88 uint32_t bufsz, buflen;
89 size_t write_len;
90 int res;
91
92 bufsz = buflen = sizeof(msg);
93 buf = ptr = msg;
94
95 sftp_msg_write_int(&buf, &buflen, reqlen);
96
97 /* Send the message length to the agent. */
98
99 write_len = bufsz - buflen;
100 res = write(fd, ptr, write_len);
101 if (res < 0) {
102 int xerrno = errno;
103
104 pr_trace_msg(trace_channel, 3,
105 "error sending request length to SSH agent at '%s': %s", path,
106 strerror(xerrno));
107
108 errno = xerrno;
109 return NULL;
110 }
111
112 /* Handle short writes. */
113 if ((size_t) res != write_len) {
114 pr_trace_msg(trace_channel, 3,
115 "short write (%d of %lu bytes sent) when talking to SSH agent at '%s'",
116 res, (unsigned long) (write_len), path);
117 errno = EIO;
118 return NULL;
119 }
120
121 /* Send the message payload to the agent. */
122
123 res = write(fd, req, reqlen);
124 if (res < 0) {
125 int xerrno = errno;
126
127 pr_trace_msg(trace_channel, 3,
128 "error sending request payload to SSH agent at '%s': %s", path,
129 strerror(xerrno));
130
131 errno = xerrno;
132 return NULL;
133 }
134
135 /* Handle short writes. */
136 if ((uint32_t) res != reqlen) {
137 pr_trace_msg(trace_channel, 3,
138 "short write (%d of %lu bytes sent) when talking to SSH agent at '%s'",
139 res, (unsigned long) reqlen, path);
140 errno = EIO;
141 return NULL;
142 }
143
144 /* Wait for a response from the server. */
145 /* XXX This needs a timeout, prevent a blocked/bad agent from stalling
146 * the server. Maybe just set an internal timer?
147 */
148
149 res = read(fd, msg, sizeof(uint32_t));
150 if (res < 0) {
151 int xerrno = errno;
152
153 pr_trace_msg(trace_channel, 3,
154 "error reading response length from SSH agent at '%s': %s", path,
155 strerror(xerrno));
156
157 errno = xerrno;
158 return NULL;
159 }
160
161 /* Sanity check the returned length; we could be dealing with a buggy
162 * client (or something else is injecting data into the Unix domain socket).
163 * Best be conservative: if we get a response length of more than 256KB,
164 * it's too big. (What about very long lists of keys, and/or large keys?)
165 */
166 if (res > AGENT_REPLY_MAXSZ) {
167 pr_trace_msg(trace_channel, 1,
168 "response length (%d) from SSH agent at '%s' exceeds maximum (%lu), "
169 "ignoring", res, path, (unsigned long) AGENT_REPLY_MAXSZ);
170 errno = EIO;
171 return NULL;
172 }
173
174 buf = msg;
175 buflen = res;
176
177 *resplen = sftp_msg_read_int(p, &buf, &buflen);
178
179 bufsz = buflen = *resplen;
180 buf = ptr = palloc(p, bufsz);
181
182 buflen = 0;
183 while (buflen != *resplen) {
184 pr_signals_handle();
185
186 res = read(fd, buf + buflen, bufsz - buflen);
187 if (res < 0) {
188 int xerrno = errno;
189
190 pr_trace_msg(trace_channel, 3,
191 "error reading %d bytes of response payload from SSH agent at '%s': %s",
192 (bufsz - buflen), path, strerror(xerrno));
193
194 errno = xerrno;
195 return NULL;
196 }
197
198 /* XXX Handle short reads? */
199 buflen += res;
200 }
201
202 return ptr;
203 }
204
agent_connect(const char * path)205 static int agent_connect(const char *path) {
206 int fd, len, res, xerrno;
207 struct sockaddr_un sock;
208
209 memset(&sock, 0, sizeof(sock));
210 sock.sun_family = AF_UNIX;
211 sstrncpy(sock.sun_path, path, sizeof(sock.sun_path));
212 len = sizeof(sock);
213
214 fd = socket(AF_UNIX, SOCK_STREAM, 0);
215 if (fd < 0) {
216 xerrno = errno;
217
218 pr_trace_msg(trace_channel, 3, "error opening Unix domain socket: %s",
219 strerror(xerrno));
220
221 errno = xerrno;
222 return -1;
223 }
224
225 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
226 pr_trace_msg(trace_channel, 3,
227 "error setting CLOEXEC on fd %d for talking to SSH agent: %s",
228 fd, strerror(errno));
229 }
230
231 PRIVS_ROOT
232 res = connect(fd, (struct sockaddr *) &sock, len);
233 xerrno = errno;
234 PRIVS_RELINQUISH
235
236 if (res < 0) {
237 pr_trace_msg(trace_channel, 2, "error connecting to SSH agent at '%s': %s",
238 path, strerror(xerrno));
239
240 (void) close(fd);
241 errno = xerrno;
242 return -1;
243 }
244
245 return fd;
246 }
247
sftp_agent_get_keys(pool * p,const char * agent_path,array_header * key_list)248 int sftp_agent_get_keys(pool *p, const char *agent_path,
249 array_header *key_list) {
250 register unsigned int i;
251 int fd;
252 unsigned char *buf, *req, *resp;
253 uint32_t buflen, key_count, reqlen, reqsz, resplen;
254 char resp_status;
255
256 fd = agent_connect(agent_path);
257 if (fd < 0) {
258 return -1;
259 }
260
261 /* Write out the request for the identities (i.e. the public keys). */
262
263 reqsz = buflen = 64;
264 req = buf = palloc(p, reqsz);
265
266 sftp_msg_write_byte(&buf, &buflen, SFTP_SSH_AGENT_REQ_IDS);
267
268 reqlen = reqsz - buflen;
269 resp = agent_request(p, fd, agent_path, req, reqlen, &resplen);
270 if (resp == NULL) {
271 int xerrno = errno;
272
273 (void) close(fd);
274 errno = xerrno;
275 return -1;
276 }
277
278 (void) close(fd);
279
280 /* Read the response from the agent. */
281
282 resp_status = sftp_msg_read_byte(p, &resp, &resplen);
283 if (agent_failure(resp_status) == TRUE) {
284 pr_trace_msg(trace_channel, 5,
285 "SSH agent at '%s' indicated failure (%d) for identities request",
286 agent_path, resp_status);
287 errno = EPERM;
288 return -1;
289 }
290
291 if (resp_status != SFTP_SSH_AGENT_RESP_IDS) {
292 pr_trace_msg(trace_channel, 5,
293 "unknown response type %d from SSH agent at '%s'", resp_status,
294 agent_path);
295 errno = EACCES;
296 return -1;
297 }
298
299 key_count = sftp_msg_read_int(p, &resp, &resplen);
300 if (key_count > AGENT_MAX_KEYS) {
301 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
302 "SSH agent at '%s' returned too many keys (%lu, max %lu)", agent_path,
303 (unsigned long) key_count, (unsigned long) AGENT_MAX_KEYS);
304 errno = EPERM;
305 return -1;
306 }
307
308 for (i = 0; i < key_count; i++) {
309 unsigned char *key_data;
310 uint32_t key_datalen;
311 char *key_comment;
312 struct agent_key *key;
313
314 key_datalen = sftp_msg_read_int(p, &resp, &resplen);
315 key_data = sftp_msg_read_data(p, &resp, &resplen, key_datalen);
316 key_comment = sftp_msg_read_string(p, &resp, &resplen);
317 if (key_comment != NULL) {
318 pr_trace_msg(trace_channel, 9,
319 "SSH agent at '%s' provided comment '%s' for key #%u", agent_path,
320 key_comment, (i + 1));
321 }
322
323 key = pcalloc(p, sizeof(struct agent_key));
324
325 key->key_data = key_data;
326 key->key_datalen = key_datalen;
327 key->agent_path = pstrdup(p, agent_path);
328
329 *((struct agent_key **) push_array(key_list)) = key;
330 }
331
332 pr_trace_msg(trace_channel, 9, "SSH agent at '%s' provided %lu %s",
333 agent_path, (unsigned long) key_count, key_count != 1 ? "keys" : "key");
334 return 0;
335 }
336
sftp_agent_sign_data(pool * p,const char * agent_path,const unsigned char * key_data,uint32_t key_datalen,const unsigned char * data,uint32_t datalen,uint32_t * sig_datalen,int flags)337 const unsigned char *sftp_agent_sign_data(pool *p, const char *agent_path,
338 const unsigned char *key_data, uint32_t key_datalen,
339 const unsigned char *data, uint32_t datalen, uint32_t *sig_datalen,
340 int flags) {
341 int fd;
342 unsigned char *buf, *req, *resp, *sig_data;
343 uint32_t buflen, sig_flags, reqlen, reqsz, resplen;
344 char resp_status;
345
346 fd = agent_connect(agent_path);
347 if (fd < 0) {
348 return NULL;
349 }
350
351 /* XXX When to set flags to OLD_SIGNATURE? */
352 sig_flags = 0;
353
354 if (flags & SFTP_AGENT_SIGN_FL_USE_RSA_SHA256) {
355 sig_flags |= SFTP_SSH_AGENT_SIGN_RSA_SHA256;
356 }
357
358 if (flags & SFTP_AGENT_SIGN_FL_USE_RSA_SHA512) {
359 sig_flags |= SFTP_SSH_AGENT_SIGN_RSA_SHA512;
360 }
361
362 /* Write out the request for signing the given data. */
363 reqsz = buflen = 1 + key_datalen + 4 + datalen + 4 + 4;
364 req = buf = palloc(p, reqsz);
365
366 sftp_msg_write_byte(&buf, &buflen, SFTP_SSH_AGENT_REQ_SIGN_DATA);
367 sftp_msg_write_data(&buf, &buflen, key_data, key_datalen, TRUE);
368 sftp_msg_write_data(&buf, &buflen, data, datalen, TRUE);
369 sftp_msg_write_int(&buf, &buflen, sig_flags);
370
371 reqlen = reqsz - buflen;
372 resp = agent_request(p, fd, agent_path, req, reqlen, &resplen);
373 if (resp == NULL) {
374 int xerrno = errno;
375
376 (void) close(fd);
377 errno = xerrno;
378 return NULL;
379 }
380
381 (void) close(fd);
382
383 /* Read the response from the agent. */
384
385 resp_status = sftp_msg_read_byte(p, &resp, &resplen);
386 if (agent_failure(resp_status) == TRUE) {
387 pr_trace_msg(trace_channel, 5,
388 "SSH agent at '%s' indicated failure (%d) for data signing request",
389 agent_path, resp_status);
390 errno = EPERM;
391 return NULL;
392 }
393
394 if (resp_status != SFTP_SSH_AGENT_RESP_SIGN_DATA) {
395 pr_trace_msg(trace_channel, 5,
396 "unknown response type %d from SSH agent at '%s'", resp_status,
397 agent_path);
398 errno = EACCES;
399 return NULL;
400 }
401
402 *sig_datalen = sftp_msg_read_int(p, &resp, &resplen);
403 sig_data = sftp_msg_read_data(p, &resp, &resplen, *sig_datalen);
404
405 return sig_data;
406 }
407