xref: /netbsd-src/usr.bin/ftp/ssl.c (revision bafbd613d128806a1921666029a1af09a19e3f1c)
1*bafbd613Schristos /*	$NetBSD: ssl.c,v 1.20 2024/09/25 16:53:58 christos Exp $	*/
2f9336fd8Schristos 
3f9336fd8Schristos /*-
4f9336fd8Schristos  * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
5f9336fd8Schristos  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
6f9b7d234Swiz  * Copyright (c) 2015 Thomas Klausner <wiz@NetBSD.org>
7ddcd952bSmlelstv  * Copyright (c) 2023 Michael van Elst <mlelstv@NetBSD.org>
8f9336fd8Schristos  * All rights reserved.
9f9336fd8Schristos  *
10f9336fd8Schristos  * Redistribution and use in source and binary forms, with or without
11f9336fd8Schristos  * modification, are permitted provided that the following conditions
12f9336fd8Schristos  * are met:
13f9336fd8Schristos  * 1. Redistributions of source code must retain the above copyright
14f9336fd8Schristos  *    notice, this list of conditions and the following disclaimer
15f9336fd8Schristos  *    in this position and unchanged.
16f9336fd8Schristos  * 2. Redistributions in binary form must reproduce the above copyright
17f9336fd8Schristos  *    notice, this list of conditions and the following disclaimer in the
18f9336fd8Schristos  *    documentation and/or other materials provided with the distribution.
19f9336fd8Schristos  * 3. The name of the author may not be used to endorse or promote products
20f9336fd8Schristos  *    derived from this software without specific prior written permission
21f9336fd8Schristos  *
22f9336fd8Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23f9336fd8Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24f9336fd8Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25f9336fd8Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26f9336fd8Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27f9336fd8Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28f9336fd8Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29f9336fd8Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30f9336fd8Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31f9336fd8Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32f9336fd8Schristos  *
33f9336fd8Schristos  * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
34f9336fd8Schristos  */
35f9336fd8Schristos 
36f9336fd8Schristos #include <sys/cdefs.h>
37f9336fd8Schristos #ifndef lint
38*bafbd613Schristos __RCSID("$NetBSD: ssl.c,v 1.20 2024/09/25 16:53:58 christos Exp $");
39f9336fd8Schristos #endif
40f9336fd8Schristos 
4155c16b26Slukem #include <err.h>
42b6f94212Slukem #include <errno.h>
43b6f94212Slukem #include <fcntl.h>
44b6f94212Slukem #include <stdarg.h>
45b6f94212Slukem #include <stdio.h>
46b6f94212Slukem #include <stdlib.h>
47b6f94212Slukem #include <string.h>
48f9336fd8Schristos #include <time.h>
49f9336fd8Schristos #include <unistd.h>
50f9336fd8Schristos 
51f9336fd8Schristos #include <sys/param.h>
52f9336fd8Schristos #include <sys/uio.h>
53f9336fd8Schristos 
54f9336fd8Schristos #include <netinet/tcp.h>
55f9336fd8Schristos #include <netinet/in.h>
56b6f94212Slukem 
57b6f94212Slukem #ifdef WITH_SSL
58f9336fd8Schristos #include <openssl/crypto.h>
59f9336fd8Schristos #include <openssl/x509.h>
60f9336fd8Schristos #include <openssl/pem.h>
61f9336fd8Schristos #include <openssl/ssl.h>
62f9336fd8Schristos #include <openssl/err.h>
63b6f94212Slukem #endif
64f9336fd8Schristos 
65f9336fd8Schristos #include "ssl.h"
666db0a144Slukem #include "ftp_var.h"
67ddcd952bSmlelstv 
68f9336fd8Schristos extern int quit_time, verbose, ftp_debug;
69f9336fd8Schristos extern FILE *ttyout;
70f9336fd8Schristos 
71f9336fd8Schristos struct fetch_connect {
72f9336fd8Schristos 	int			 sd;		/* file/socket descriptor */
73f9336fd8Schristos 	char			*buf;		/* buffer */
74f9336fd8Schristos 	size_t			 bufsize;	/* buffer size */
75f9336fd8Schristos 	size_t			 bufpos;	/* position of buffer */
76f9336fd8Schristos 	size_t			 buflen;	/* length of buffer contents */
77f9336fd8Schristos 	struct {				/* data cached after an
78f9336fd8Schristos 						   interrupted read */
79f9336fd8Schristos 		char	*buf;
80f9336fd8Schristos 		size_t	 size;
81f9336fd8Schristos 		size_t	 pos;
82f9336fd8Schristos 		size_t	 len;
83f9336fd8Schristos 	} cache;
84f9336fd8Schristos 	int 			 issock;
85f9336fd8Schristos 	int			 iserr;
86f9336fd8Schristos 	int			 iseof;
87b6f94212Slukem #ifdef WITH_SSL
88f9336fd8Schristos 	SSL			*ssl;		/* SSL handle */
89b6f94212Slukem #endif
90f9336fd8Schristos };
91f9336fd8Schristos 
92f9336fd8Schristos /*
93f9336fd8Schristos  * Write a vector to a connection w/ timeout
94f9336fd8Schristos  * Note: can modify the iovec.
95f9336fd8Schristos  */
96f9336fd8Schristos static ssize_t
97f9336fd8Schristos fetch_writev(struct fetch_connect *conn, struct iovec *iov, int iovcnt)
98f9336fd8Schristos {
9955c16b26Slukem 	struct timeval timeout, now, delta;
100f9336fd8Schristos 	ssize_t len, total;
101e35c1a2bSchristos 	int fd = conn->sd;
10255c16b26Slukem 	int rv, timeout_secs;
10355c16b26Slukem 	struct pollfd pfd[1];
104f9336fd8Schristos 
10555c16b26Slukem 	pfd[0].fd = fd;
10655c16b26Slukem 	pfd[0].events = POLLOUT;
107f9336fd8Schristos 	gettimeofday(&timeout, NULL);
108f9336fd8Schristos 	timeout.tv_sec += quit_time;
109f9336fd8Schristos 
110f9336fd8Schristos 	total = 0;
111f9336fd8Schristos 	while (iovcnt > 0) {
11255c16b26Slukem 		if (quit_time > 0) {	/* enforce timeout */
11355c16b26Slukem 			do {
11455c16b26Slukem 				(void)gettimeofday(&now, NULL);
11555c16b26Slukem 				timersub(&timeout, &now, &delta);
116*bafbd613Schristos 				timeout_secs = (int)(delta.tv_sec * 1000
117*bafbd613Schristos 				    + delta.tv_usec / 1000);
11855c16b26Slukem 				if (timeout_secs < 0)
11955c16b26Slukem 					timeout_secs = 0;
12055c16b26Slukem 				rv = ftp_poll(pfd, 1, timeout_secs);
12155c16b26Slukem 					/* loop until poll !EINTR && !EAGAIN */
12255c16b26Slukem 			} while (rv == -1 && (errno == EINTR || errno == EAGAIN));
12355c16b26Slukem 			if (rv == -1)
124f9336fd8Schristos 				return -1;
12555c16b26Slukem 			if (rv == 0) {
12655c16b26Slukem 				errno = ETIMEDOUT;
127f9336fd8Schristos 				return -1;
128f9336fd8Schristos 			}
129f9336fd8Schristos 		}
130f9336fd8Schristos 		errno = 0;
131b6f94212Slukem #ifdef WITH_SSL
132f9336fd8Schristos 		if (conn->ssl != NULL)
133*bafbd613Schristos 			len = SSL_write(conn->ssl, iov->iov_base, (int)iov->iov_len);
134f9336fd8Schristos 		else
135b6f94212Slukem #endif
136e35c1a2bSchristos 			len = writev(fd, iov, iovcnt);
137f9336fd8Schristos 		if (len == 0) {
138f9336fd8Schristos 			/* we consider a short write a failure */
139f9336fd8Schristos 			/* XXX perhaps we shouldn't in the SSL case */
140f9336fd8Schristos 			errno = EPIPE;
141f9336fd8Schristos 			return -1;
142f9336fd8Schristos 		}
143f9336fd8Schristos 		if (len < 0) {
144e35c1a2bSchristos 			if (errno == EINTR || errno == EAGAIN)
145f9336fd8Schristos 				continue;
146f9336fd8Schristos 			return -1;
147f9336fd8Schristos 		}
148f9336fd8Schristos 		total += len;
149f9336fd8Schristos 		while (iovcnt > 0 && len >= (ssize_t)iov->iov_len) {
150f9336fd8Schristos 			len -= iov->iov_len;
151f9336fd8Schristos 			iov++;
152f9336fd8Schristos 			iovcnt--;
153f9336fd8Schristos 		}
154f9336fd8Schristos 		if (iovcnt > 0) {
155f9336fd8Schristos 			iov->iov_len -= len;
156f9336fd8Schristos 			iov->iov_base = (char *)iov->iov_base + len;
157f9336fd8Schristos 		}
158f9336fd8Schristos 	}
159f9336fd8Schristos 	return total;
160f9336fd8Schristos }
161f9336fd8Schristos 
162e35c1a2bSchristos static ssize_t
163e35c1a2bSchristos fetch_write(const void *str, size_t len, struct fetch_connect *conn)
164f9336fd8Schristos {
165f9336fd8Schristos 	struct iovec iov[1];
166f9336fd8Schristos 
167f9336fd8Schristos 	iov[0].iov_base = (char *)__UNCONST(str);
168f9336fd8Schristos 	iov[0].iov_len = len;
169f9336fd8Schristos 	return fetch_writev(conn, iov, 1);
170f9336fd8Schristos }
171f9336fd8Schristos 
172f9336fd8Schristos /*
173f9336fd8Schristos  * Send a formatted line; optionally echo to terminal
174f9336fd8Schristos  */
175f9336fd8Schristos int
176f9336fd8Schristos fetch_printf(struct fetch_connect *conn, const char *fmt, ...)
177f9336fd8Schristos {
178f9336fd8Schristos 	va_list ap;
179f9336fd8Schristos 	size_t len;
180f9336fd8Schristos 	char *msg;
181*bafbd613Schristos 	ssize_t r;
182f9336fd8Schristos 
183f9336fd8Schristos 	va_start(ap, fmt);
184f9336fd8Schristos 	len = vasprintf(&msg, fmt, ap);
185f9336fd8Schristos 	va_end(ap);
186f9336fd8Schristos 
187f9336fd8Schristos 	if (msg == NULL) {
188f9336fd8Schristos 		errno = ENOMEM;
189f9336fd8Schristos 		return -1;
190f9336fd8Schristos 	}
191f9336fd8Schristos 
192e35c1a2bSchristos 	r = fetch_write(msg, len, conn);
193f9336fd8Schristos 	free(msg);
194*bafbd613Schristos 	return (int)r;
195f9336fd8Schristos }
196f9336fd8Schristos 
197f9336fd8Schristos int
198f9336fd8Schristos fetch_fileno(struct fetch_connect *conn)
199f9336fd8Schristos {
200f9336fd8Schristos 
201f9336fd8Schristos 	return conn->sd;
202f9336fd8Schristos }
203f9336fd8Schristos 
204f9336fd8Schristos int
205f9336fd8Schristos fetch_error(struct fetch_connect *conn)
206f9336fd8Schristos {
207f9336fd8Schristos 
208f9336fd8Schristos 	return conn->iserr;
209f9336fd8Schristos }
210f9336fd8Schristos 
211f9336fd8Schristos static void
212f9336fd8Schristos fetch_clearerr(struct fetch_connect *conn)
213f9336fd8Schristos {
214f9336fd8Schristos 
215f9336fd8Schristos 	conn->iserr = 0;
216f9336fd8Schristos }
217f9336fd8Schristos 
218f9336fd8Schristos int
219f9336fd8Schristos fetch_flush(struct fetch_connect *conn)
220f9336fd8Schristos {
221f9336fd8Schristos 
222f9336fd8Schristos 	if (conn->issock) {
223e35c1a2bSchristos 		int fd = conn->sd;
224e35c1a2bSchristos 		int v;
225f9336fd8Schristos #ifdef TCP_NOPUSH
226f9336fd8Schristos 		v = 0;
227e35c1a2bSchristos 		setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
228f9336fd8Schristos #endif
229f9336fd8Schristos 		v = 1;
230e35c1a2bSchristos 		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
231f9336fd8Schristos 	}
232f9336fd8Schristos 	return 0;
233f9336fd8Schristos }
234f9336fd8Schristos 
235f9336fd8Schristos /*ARGSUSED*/
236f9336fd8Schristos struct fetch_connect *
237f9336fd8Schristos fetch_open(const char *fname, const char *fmode)
238f9336fd8Schristos {
239f9336fd8Schristos 	struct fetch_connect *conn;
240f9336fd8Schristos 	int fd;
241f9336fd8Schristos 
242f9336fd8Schristos 	fd = open(fname, O_RDONLY); /* XXX: fmode */
243f9336fd8Schristos 	if (fd < 0)
244f9336fd8Schristos 		return NULL;
245f9336fd8Schristos 
246f9336fd8Schristos 	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
247f9336fd8Schristos 		close(fd);
248f9336fd8Schristos 		return NULL;
249f9336fd8Schristos 	}
250f9336fd8Schristos 
251f9336fd8Schristos 	conn->sd = fd;
252f9336fd8Schristos 	conn->issock = 0;
253f9336fd8Schristos 	return conn;
254f9336fd8Schristos }
255f9336fd8Schristos 
256f9336fd8Schristos /*ARGSUSED*/
257f9336fd8Schristos struct fetch_connect *
258f9336fd8Schristos fetch_fdopen(int sd, const char *fmode)
259f9336fd8Schristos {
260f9336fd8Schristos 	struct fetch_connect *conn;
261ccdf6b91Schristos #if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH)
262f9336fd8Schristos 	int opt = 1;
263ccdf6b91Schristos #endif
264f9336fd8Schristos 
265f9336fd8Schristos 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
266f9336fd8Schristos 		return NULL;
267f9336fd8Schristos 
268f9336fd8Schristos 	conn->sd = sd;
269f9336fd8Schristos 	conn->issock = 1;
270f9336fd8Schristos 	fcntl(sd, F_SETFD, FD_CLOEXEC);
271ccdf6b91Schristos #ifdef SO_NOSIGPIPE
272f9336fd8Schristos 	setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
273ccdf6b91Schristos #endif
274f9336fd8Schristos #ifdef TCP_NOPUSH
275f9336fd8Schristos 	setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
276f9336fd8Schristos #endif
277f9336fd8Schristos 	return conn;
278f9336fd8Schristos }
279f9336fd8Schristos 
280f9336fd8Schristos int
281f9336fd8Schristos fetch_close(struct fetch_connect *conn)
282f9336fd8Schristos {
283e35c1a2bSchristos 	if (conn == NULL)
284e35c1a2bSchristos 		return 0;
285f9336fd8Schristos 
286f9336fd8Schristos 	fetch_flush(conn);
287b6f94212Slukem #ifdef WITH_SSL
288f9336fd8Schristos 	SSL_free(conn->ssl);
289b6f94212Slukem #endif
290e35c1a2bSchristos 	close(conn->sd);
291f9336fd8Schristos 	free(conn->cache.buf);
292f9336fd8Schristos 	free(conn->buf);
293f9336fd8Schristos 	free(conn);
294e35c1a2bSchristos 	return 0;
295f9336fd8Schristos }
296f9336fd8Schristos 
297e35c1a2bSchristos #define FETCH_WRITE_WAIT	-3
298f9336fd8Schristos #define FETCH_READ_WAIT		-2
299f9336fd8Schristos #define FETCH_READ_ERROR	-1
300f9336fd8Schristos 
301b6f94212Slukem #ifdef WITH_SSL
302f9336fd8Schristos static ssize_t
303f9336fd8Schristos fetch_ssl_read(SSL *ssl, void *buf, size_t len)
304f9336fd8Schristos {
305*bafbd613Schristos 	int rlen;
306*bafbd613Schristos 	rlen = SSL_read(ssl, buf, (int)len);
307e35c1a2bSchristos 	if (rlen >= 0)
308e35c1a2bSchristos 		return rlen;
309e35c1a2bSchristos 
310e35c1a2bSchristos 	switch (SSL_get_error(ssl, rlen)) {
311e35c1a2bSchristos 	case SSL_ERROR_WANT_READ:
312f9336fd8Schristos 		return FETCH_READ_WAIT;
313e35c1a2bSchristos 	case SSL_ERROR_WANT_WRITE:
314e35c1a2bSchristos 		return FETCH_WRITE_WAIT;
315e35c1a2bSchristos 	default:
316f9336fd8Schristos 		ERR_print_errors_fp(ttyout);
317f9336fd8Schristos 		return FETCH_READ_ERROR;
318f9336fd8Schristos 	}
319f9336fd8Schristos }
320b6f94212Slukem #endif /* WITH_SSL */
321f9336fd8Schristos 
322f9336fd8Schristos static ssize_t
323f9336fd8Schristos fetch_nonssl_read(int sd, void *buf, size_t len)
324f9336fd8Schristos {
325f9336fd8Schristos 	ssize_t rlen;
326f9336fd8Schristos 
327f9336fd8Schristos 	rlen = read(sd, buf, len);
328e35c1a2bSchristos 	if (rlen == -1) {
32955c16b26Slukem 		if (errno == EINTR || errno == EAGAIN)
330f9336fd8Schristos 			return FETCH_READ_WAIT;
331f9336fd8Schristos 		return FETCH_READ_ERROR;
332f9336fd8Schristos 	}
333f9336fd8Schristos 	return rlen;
334f9336fd8Schristos }
335f9336fd8Schristos 
336f9336fd8Schristos /*
337f9336fd8Schristos  * Cache some data that was read from a socket but cannot be immediately
338f9336fd8Schristos  * returned because of an interrupted system call.
339f9336fd8Schristos  */
340f9336fd8Schristos static int
341f9336fd8Schristos fetch_cache_data(struct fetch_connect *conn, char *src, size_t nbytes)
342f9336fd8Schristos {
343f9336fd8Schristos 
344f9336fd8Schristos 	if (conn->cache.size < nbytes) {
345f9336fd8Schristos 		char *tmp = realloc(conn->cache.buf, nbytes);
346f9336fd8Schristos 		if (tmp == NULL)
347f9336fd8Schristos 			return -1;
348f9336fd8Schristos 
349f9336fd8Schristos 		conn->cache.buf = tmp;
350f9336fd8Schristos 		conn->cache.size = nbytes;
351f9336fd8Schristos 	}
352f9336fd8Schristos 
353f9336fd8Schristos 	memcpy(conn->cache.buf, src, nbytes);
354f9336fd8Schristos 	conn->cache.len = nbytes;
355f9336fd8Schristos 	conn->cache.pos = 0;
356f9336fd8Schristos 	return 0;
357f9336fd8Schristos }
358f9336fd8Schristos 
359e35c1a2bSchristos static int
360e35c1a2bSchristos fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout)
361e35c1a2bSchristos {
362e35c1a2bSchristos 	struct timeval now, delta;
363e35c1a2bSchristos 	int fd = conn->sd;
36455c16b26Slukem 	int rv, timeout_secs;
36555c16b26Slukem 	struct pollfd pfd[1];
366e35c1a2bSchristos 
36755c16b26Slukem 	pfd[0].fd = fd;
36855c16b26Slukem 	if (rlen == FETCH_READ_WAIT) {
36955c16b26Slukem 		pfd[0].events = POLLIN;
37055c16b26Slukem 	} else if (rlen == FETCH_WRITE_WAIT) {
37155c16b26Slukem 		pfd[0].events = POLLOUT;
37255c16b26Slukem 	} else {
37355c16b26Slukem 		pfd[0].events = 0;
37455c16b26Slukem 	}
37555c16b26Slukem 
37655c16b26Slukem 	do {
377e35c1a2bSchristos 		if (quit_time > 0) {
378e35c1a2bSchristos 			gettimeofday(&now, NULL);
37955c16b26Slukem 			timersub(timeout, &now, &delta);
380*bafbd613Schristos 			timeout_secs = (int)(delta.tv_sec * 1000
381*bafbd613Schristos 			    + delta.tv_usec / 1000);
38255c16b26Slukem 			if (timeout_secs < 0)
38355c16b26Slukem 				timeout_secs = 0;
38455c16b26Slukem 		} else {
38555c16b26Slukem 			timeout_secs = INFTIM;
38655c16b26Slukem 		}
38755c16b26Slukem 		errno = 0;
38855c16b26Slukem 		rv = ftp_poll(pfd, 1, timeout_secs);
38955c16b26Slukem 				/* loop until poll !EINTR && !EAGAIN */
39055c16b26Slukem 	} while (rv == -1 && (errno == EINTR || errno == EAGAIN));
39155c16b26Slukem 	if (rv == 0) {		/* poll timeout */
392920389c1Slukem 		fprintf(ttyout, "\r\n%s: transfer aborted"
393920389c1Slukem 		    " because stalled for %lu sec.\r\n",
394920389c1Slukem 		    getprogname(), (unsigned long)quit_time);
395920389c1Slukem 		errno = ETIMEDOUT;
396e35c1a2bSchristos 		conn->iserr = ETIMEDOUT;
397e35c1a2bSchristos 		return -1;
398e35c1a2bSchristos 	}
39955c16b26Slukem 	if (rv == -1) {		/* poll error */
400e35c1a2bSchristos 		conn->iserr = errno;
401e35c1a2bSchristos 		return -1;
402e35c1a2bSchristos 	}
403e35c1a2bSchristos 	return 0;
404e35c1a2bSchristos }
405e35c1a2bSchristos 
406a5b9754eSchristos size_t
407f9336fd8Schristos fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
408f9336fd8Schristos {
409f9336fd8Schristos 	ssize_t rlen, total;
410f9336fd8Schristos 	size_t len;
411f9336fd8Schristos 	char *start, *buf;
412e35c1a2bSchristos 	struct timeval timeout;
413f9336fd8Schristos 
414f9336fd8Schristos 	if (quit_time > 0) {
415f9336fd8Schristos 		gettimeofday(&timeout, NULL);
416f9336fd8Schristos 		timeout.tv_sec += quit_time;
417f9336fd8Schristos 	}
418f9336fd8Schristos 
419f9336fd8Schristos 	total = 0;
420f9336fd8Schristos 	start = buf = ptr;
421f9336fd8Schristos 	len = size * nmemb;
422f9336fd8Schristos 
423f9336fd8Schristos 	if (conn->cache.len > 0) {
424f9336fd8Schristos 		/*
425f9336fd8Schristos 		 * The last invocation of fetch_read was interrupted by a
426f9336fd8Schristos 		 * signal after some data had been read from the socket. Copy
427f9336fd8Schristos 		 * the cached data into the supplied buffer before trying to
428f9336fd8Schristos 		 * read from the socket again.
429f9336fd8Schristos 		 */
430f9336fd8Schristos 		total = (conn->cache.len < len) ? conn->cache.len : len;
431f9336fd8Schristos 		memcpy(buf, conn->cache.buf, total);
432f9336fd8Schristos 
433f9336fd8Schristos 		conn->cache.len -= total;
434f9336fd8Schristos 		conn->cache.pos += total;
435f9336fd8Schristos 		len -= total;
436f9336fd8Schristos 		buf += total;
437f9336fd8Schristos 	}
438f9336fd8Schristos 
439f9336fd8Schristos 	while (len > 0) {
440f9336fd8Schristos 		/*
441f9336fd8Schristos 		 * The socket is non-blocking.  Instead of the canonical
44255c16b26Slukem 		 * poll() -> read(), we do the following:
443f9336fd8Schristos 		 *
444f9336fd8Schristos 		 * 1) call read() or SSL_read().
445f9336fd8Schristos 		 * 2) if an error occurred, return -1.
446f9336fd8Schristos 		 * 3) if we received data but we still expect more,
447f9336fd8Schristos 		 *    update our counters and loop.
448f9336fd8Schristos 		 * 4) if read() or SSL_read() signaled EOF, return.
449f9336fd8Schristos 		 * 5) if we did not receive any data but we're not at EOF,
45055c16b26Slukem 		 *    call poll().
451f9336fd8Schristos 		 *
452f9336fd8Schristos 		 * In the SSL case, this is necessary because if we
453f9336fd8Schristos 		 * receive a close notification, we have to call
454f9336fd8Schristos 		 * SSL_read() one additional time after we've read
455f9336fd8Schristos 		 * everything we received.
456f9336fd8Schristos 		 *
457f9336fd8Schristos 		 * In the non-SSL case, it may improve performance (very
458f9336fd8Schristos 		 * slightly) when reading small amounts of data.
459f9336fd8Schristos 		 */
460b6f94212Slukem #ifdef WITH_SSL
461f9336fd8Schristos 		if (conn->ssl != NULL)
462f9336fd8Schristos 			rlen = fetch_ssl_read(conn->ssl, buf, len);
463f9336fd8Schristos 		else
464b6f94212Slukem #endif
465f9336fd8Schristos 			rlen = fetch_nonssl_read(conn->sd, buf, len);
466e35c1a2bSchristos 		switch (rlen) {
467e35c1a2bSchristos 		case 0:
468a5b9754eSchristos 			conn->iseof = 1;
469e35c1a2bSchristos 			return total;
470e35c1a2bSchristos 		case FETCH_READ_ERROR:
471a5b9754eSchristos 			conn->iserr = errno;
47255c16b26Slukem 			if (errno == EINTR || errno == EAGAIN)
473f9336fd8Schristos 				fetch_cache_data(conn, start, total);
474a5b9754eSchristos 			return 0;
475e35c1a2bSchristos 		case FETCH_READ_WAIT:
476e35c1a2bSchristos 		case FETCH_WRITE_WAIT:
477e35c1a2bSchristos 			if (fetch_wait(conn, rlen, &timeout) == -1)
478a5b9754eSchristos 				return 0;
479e35c1a2bSchristos 			break;
480e35c1a2bSchristos 		default:
481e35c1a2bSchristos 			len -= rlen;
482e35c1a2bSchristos 			buf += rlen;
483e35c1a2bSchristos 			total += rlen;
484e35c1a2bSchristos 			break;
485f9336fd8Schristos 		}
486f9336fd8Schristos 	}
487f9336fd8Schristos 	return total;
488f9336fd8Schristos }
489f9336fd8Schristos 
490f9336fd8Schristos #define MIN_BUF_SIZE 1024
491f9336fd8Schristos 
492f9336fd8Schristos /*
493f9336fd8Schristos  * Read a line of text from a connection w/ timeout
494f9336fd8Schristos  */
495f9336fd8Schristos char *
496f9336fd8Schristos fetch_getln(char *str, int size, struct fetch_connect *conn)
497f9336fd8Schristos {
498f9336fd8Schristos 	size_t tmpsize;
499a5b9754eSchristos 	size_t len;
500f9336fd8Schristos 	char c;
501f9336fd8Schristos 
502f9336fd8Schristos 	if (conn->buf == NULL) {
503f9336fd8Schristos 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
504f9336fd8Schristos 			errno = ENOMEM;
505f9336fd8Schristos 			conn->iserr = 1;
506f9336fd8Schristos 			return NULL;
507f9336fd8Schristos 		}
508f9336fd8Schristos 		conn->bufsize = MIN_BUF_SIZE;
509f9336fd8Schristos 	}
510f9336fd8Schristos 
511f9336fd8Schristos 	if (conn->iserr || conn->iseof)
512f9336fd8Schristos 		return NULL;
513f9336fd8Schristos 
514f9336fd8Schristos 	if (conn->buflen - conn->bufpos > 0)
515f9336fd8Schristos 		goto done;
516f9336fd8Schristos 
517f9336fd8Schristos 	conn->buf[0] = '\0';
518f9336fd8Schristos 	conn->bufpos = 0;
519f9336fd8Schristos 	conn->buflen = 0;
520f9336fd8Schristos 	do {
521f9336fd8Schristos 		len = fetch_read(&c, sizeof(c), 1, conn);
522f9336fd8Schristos 		if (len == 0) {
523a5b9754eSchristos 			if (conn->iserr)
524a5b9754eSchristos 				return NULL;
525a5b9754eSchristos 			if (conn->iseof)
526f9336fd8Schristos 				break;
527a5b9754eSchristos 			abort();
528f9336fd8Schristos 		}
529f9336fd8Schristos 		conn->buf[conn->buflen++] = c;
530f9336fd8Schristos 		if (conn->buflen == conn->bufsize) {
531f9336fd8Schristos 			char *tmp = conn->buf;
532f9336fd8Schristos 			tmpsize = conn->bufsize * 2 + 1;
533f9336fd8Schristos 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
534f9336fd8Schristos 				errno = ENOMEM;
535f9336fd8Schristos 				conn->iserr = 1;
536f9336fd8Schristos 				return NULL;
537f9336fd8Schristos 			}
538f9336fd8Schristos 			conn->buf = tmp;
539f9336fd8Schristos 			conn->bufsize = tmpsize;
540f9336fd8Schristos 		}
541f9336fd8Schristos 	} while (c != '\n');
542f9336fd8Schristos 
543f9336fd8Schristos 	if (conn->buflen == 0)
544f9336fd8Schristos 		return NULL;
545f9336fd8Schristos  done:
546f9336fd8Schristos 	tmpsize = MIN(size - 1, (int)(conn->buflen - conn->bufpos));
547f9336fd8Schristos 	memcpy(str, conn->buf + conn->bufpos, tmpsize);
548f9336fd8Schristos 	str[tmpsize] = '\0';
549f9336fd8Schristos 	conn->bufpos += tmpsize;
550f9336fd8Schristos 	return str;
551f9336fd8Schristos }
552f9336fd8Schristos 
553f9336fd8Schristos int
554f9336fd8Schristos fetch_getline(struct fetch_connect *conn, char *buf, size_t buflen,
555f9336fd8Schristos     const char **errormsg)
556f9336fd8Schristos {
557f9336fd8Schristos 	size_t len;
558f9336fd8Schristos 	int rv;
559f9336fd8Schristos 
560*bafbd613Schristos 	if (fetch_getln(buf, (int)buflen, conn) == NULL) {
561f9336fd8Schristos 		if (conn->iseof) {	/* EOF */
562f9336fd8Schristos 			rv = -2;
563f9336fd8Schristos 			if (errormsg)
564f9336fd8Schristos 				*errormsg = "\nEOF received";
565f9336fd8Schristos 		} else {		/* error */
566f9336fd8Schristos 			rv = -1;
567f9336fd8Schristos 			if (errormsg)
568f9336fd8Schristos 				*errormsg = "Error encountered";
569f9336fd8Schristos 		}
570f9336fd8Schristos 		fetch_clearerr(conn);
571f9336fd8Schristos 		return rv;
572f9336fd8Schristos 	}
573f9336fd8Schristos 	len = strlen(buf);
574f9336fd8Schristos 	if (buf[len - 1] == '\n') {	/* clear any trailing newline */
575f9336fd8Schristos 		buf[--len] = '\0';
576f9336fd8Schristos 	} else if (len == buflen - 1) {	/* line too long */
577*bafbd613Schristos 		for (;;) {
578f9336fd8Schristos 			char c;
579a5b9754eSchristos 			size_t rlen = fetch_read(&c, sizeof(c), 1, conn);
580a5b9754eSchristos 			if (rlen == 0 || c == '\n')
581f9336fd8Schristos 				break;
582f9336fd8Schristos 		}
583f9336fd8Schristos 		if (errormsg)
5846d430af8Slukem 			*errormsg = "Input line is too long (specify -b > 16384)";
585f9336fd8Schristos 		fetch_clearerr(conn);
586f9336fd8Schristos 		return -3;
587f9336fd8Schristos 	}
588f9336fd8Schristos 	if (errormsg)
589f9336fd8Schristos 		*errormsg = NULL;
590*bafbd613Schristos 	return (int)len;
591f9336fd8Schristos }
592f9336fd8Schristos 
593b6f94212Slukem #ifdef WITH_SSL
59455c16b26Slukem /*
59555c16b26Slukem  * Start the SSL/TLS negotiation.
59655c16b26Slukem  * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
59755c16b26Slukem  * these will not be reverted on connection failure.
59855c16b26Slukem  * Returns pointer to allocated SSL structure on success,
59955c16b26Slukem  * or NULL upon failure.
60055c16b26Slukem  */
601f9336fd8Schristos void *
602f9b7d234Swiz fetch_start_ssl(int sock, const char *servername)
603f9336fd8Schristos {
60455c16b26Slukem 	SSL *ssl = NULL;
60555c16b26Slukem 	SSL_CTX *ctx = NULL;
60642e6ad3aSchristos 	X509_VERIFY_PARAM *param;
60755c16b26Slukem 	int ret, ssl_err, flags, rv, timeout_secs;
608ddcd952bSmlelstv 	int verify = !ftp_truthy("sslnoverify", getoptionvalue("sslnoverify"), 0);
60955c16b26Slukem 	struct timeval timeout, now, delta;
61055c16b26Slukem 	struct pollfd pfd[1];
611f9336fd8Schristos 
612f9336fd8Schristos 	/* Init the SSL library and context */
613f9336fd8Schristos 	if (!SSL_library_init()){
61455c16b26Slukem 		warnx("SSL library init failed");
61555c16b26Slukem 		goto cleanup_start_ssl;
616f9336fd8Schristos 	}
617f9336fd8Schristos 
618f9336fd8Schristos 	SSL_load_error_strings();
619f9336fd8Schristos 
620f9336fd8Schristos 	ctx = SSL_CTX_new(SSLv23_client_method());
621f9336fd8Schristos 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
62242e6ad3aSchristos 	if (verify) {
62342e6ad3aSchristos 		SSL_CTX_set_default_verify_paths(ctx);
62442e6ad3aSchristos 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
62542e6ad3aSchristos 	}
626f9336fd8Schristos 
627f9336fd8Schristos 	ssl = SSL_new(ctx);
628f9336fd8Schristos 	if (ssl == NULL){
62955c16b26Slukem 		warnx("SSL context creation failed");
63055c16b26Slukem 		goto cleanup_start_ssl;
631f9336fd8Schristos 	}
63242e6ad3aSchristos 
63342e6ad3aSchristos 	if (verify) {
63442e6ad3aSchristos 		param = SSL_get0_param(ssl);
63542e6ad3aSchristos 		if (!X509_VERIFY_PARAM_set1_host(param, servername,
63642e6ad3aSchristos 		    strlen(servername))) {
63755c16b26Slukem 			warnx("SSL verification setup failed");
63855c16b26Slukem 			goto cleanup_start_ssl;
63942e6ad3aSchristos 		}
64042e6ad3aSchristos 
64142e6ad3aSchristos 		/* Enable peer verification, (using the default callback) */
64242e6ad3aSchristos 		SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
64342e6ad3aSchristos 	}
6443778483fSchristos #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
6453778483fSchristos 	SSL_set_options(ssl, SSL_OP_IGNORE_UNEXPECTED_EOF);
6463778483fSchristos #endif
64742e6ad3aSchristos 
64855c16b26Slukem 						/* save current socket flags */
64955c16b26Slukem 	if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
65055c16b26Slukem 		warn("Can't %s socket flags for SSL connect to `%s'",
65155c16b26Slukem 		    "save", servername);
65255c16b26Slukem 		goto cleanup_start_ssl;
65355c16b26Slukem 	}
65455c16b26Slukem 						/* set non-blocking connect */
65555c16b26Slukem 	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
65655c16b26Slukem 		warn("Can't set socket non-blocking for SSL connect to `%s'",
65755c16b26Slukem 		    servername);
65855c16b26Slukem 		goto cleanup_start_ssl;
65955c16b26Slukem 	}
66055c16b26Slukem 
66155c16b26Slukem 	/* NOTE: we now must restore socket flags on successful connection */
66255c16b26Slukem 
66355c16b26Slukem 	(void)gettimeofday(&timeout, NULL);	/* setup SSL_connect() timeout */
66455c16b26Slukem 	timeout.tv_sec += (quit_time > 0) ? quit_time: 60;
66555c16b26Slukem 						/* without -q, default to 60s */
66655c16b26Slukem 
667f9336fd8Schristos 	SSL_set_fd(ssl, sock);
668f66e764cSjoerg 	if (!SSL_set_tlsext_host_name(ssl, __UNCONST(servername))) {
66955c16b26Slukem 		warnx("SSL hostname setting failed");
67055c16b26Slukem 		goto cleanup_start_ssl;
671f9b7d234Swiz 	}
67255c16b26Slukem 	pfd[0].fd = sock;
67355c16b26Slukem 	pfd[0].events = 0;
67455c16b26Slukem 	while ((ret = SSL_connect(ssl)) <= 0) {
675f9336fd8Schristos 		ssl_err = SSL_get_error(ssl, ret);
67655c16b26Slukem 		DPRINTF("%s: SSL_connect() ret=%d ssl_err=%d\n",
67755c16b26Slukem 		    __func__, ret, ssl_err);
67855c16b26Slukem 		if (ret == 0) { /* unsuccessful handshake */
679f9336fd8Schristos 			ERR_print_errors_fp(ttyout);
68055c16b26Slukem 			goto cleanup_start_ssl;
681f9336fd8Schristos 		}
68255c16b26Slukem 		if (ssl_err == SSL_ERROR_WANT_READ) {
68355c16b26Slukem 			pfd[0].events = POLLIN;
68455c16b26Slukem 		} else if (ssl_err == SSL_ERROR_WANT_WRITE) {
68555c16b26Slukem 			pfd[0].events = POLLOUT;
68655c16b26Slukem 		} else {
68755c16b26Slukem 			ERR_print_errors_fp(ttyout);
68855c16b26Slukem 			goto cleanup_start_ssl;
68955c16b26Slukem 		}
69055c16b26Slukem 		(void)gettimeofday(&now, NULL);
69155c16b26Slukem 		timersub(&timeout, &now, &delta);
692*bafbd613Schristos 		timeout_secs = (int)(delta.tv_sec * 1000
693*bafbd613Schristos 		    + delta.tv_usec / 1000);
69455c16b26Slukem 		if (timeout_secs < 0)
69555c16b26Slukem 			timeout_secs = 0;
69655c16b26Slukem 		rv = ftp_poll(pfd, 1, timeout_secs);
69755c16b26Slukem 		if (rv == 0) {		/* poll for SSL_connect() timed out */
69855c16b26Slukem 			fprintf(ttyout, "Timeout establishing SSL connection to `%s'\n",
69955c16b26Slukem 			    servername);
70055c16b26Slukem 			goto cleanup_start_ssl;
70155c16b26Slukem 		} else if (rv == -1 && errno != EINTR && errno != EAGAIN) {
70255c16b26Slukem 			warn("Error polling for SSL connect to `%s'", servername);
70355c16b26Slukem 			goto cleanup_start_ssl;
70455c16b26Slukem 		}
70555c16b26Slukem 	}
70655c16b26Slukem 
70755c16b26Slukem 	if (fcntl(sock, F_SETFL, flags) == -1) {
70855c16b26Slukem 						/* restore socket flags */
70955c16b26Slukem 		warn("Can't %s socket flags for SSL connect to `%s'",
71055c16b26Slukem 		    "restore", servername);
71155c16b26Slukem 		goto cleanup_start_ssl;
712f9336fd8Schristos 	}
713f9336fd8Schristos 
714f9336fd8Schristos 	if (ftp_debug && verbose) {
715f9336fd8Schristos 		X509 *cert;
716f9336fd8Schristos 		X509_NAME *name;
717f9336fd8Schristos 		char *str;
718f9336fd8Schristos 
719f9336fd8Schristos 		fprintf(ttyout, "SSL connection established using %s\n",
720f9336fd8Schristos 		    SSL_get_cipher(ssl));
721f9336fd8Schristos 		cert = SSL_get_peer_certificate(ssl);
722f9336fd8Schristos 		name = X509_get_subject_name(cert);
723f9336fd8Schristos 		str = X509_NAME_oneline(name, 0, 0);
724f9336fd8Schristos 		fprintf(ttyout, "Certificate subject: %s\n", str);
725f9336fd8Schristos 		free(str);
726f9336fd8Schristos 		name = X509_get_issuer_name(cert);
727f9336fd8Schristos 		str = X509_NAME_oneline(name, 0, 0);
728f9336fd8Schristos 		fprintf(ttyout, "Certificate issuer: %s\n", str);
729f9336fd8Schristos 		free(str);
730f9336fd8Schristos 	}
731f9336fd8Schristos 
732f9336fd8Schristos 	return ssl;
73355c16b26Slukem 
73455c16b26Slukem  cleanup_start_ssl:
73555c16b26Slukem 	if (ssl)
73655c16b26Slukem 		SSL_free(ssl);
73755c16b26Slukem 	if (ctx)
73855c16b26Slukem 		SSL_CTX_free(ctx);
73955c16b26Slukem 	return NULL;
740f9336fd8Schristos }
741b6f94212Slukem #endif /* WITH_SSL */
742f9336fd8Schristos 
743f9336fd8Schristos 
744f9336fd8Schristos void
745f9336fd8Schristos fetch_set_ssl(struct fetch_connect *conn, void *ssl)
746f9336fd8Schristos {
747b6f94212Slukem #ifdef WITH_SSL
748f9336fd8Schristos 	conn->ssl = ssl;
749b6f94212Slukem #endif
750f9336fd8Schristos }
751