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