Home | History | Annotate | Line # | Download | only in ftp
ssl.c revision 1.10
      1 /*	$NetBSD: ssl.c,v 1.10 2021/06/03 10:23:33 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998-2004 Dag-Erling Codan Smrgrav
      5  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg (at) NetBSD.org>
      6  * Copyright (c) 2015 Thomas Klausner <wiz (at) NetBSD.org>
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer
     14  *    in this position and unchanged.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  *
     32  * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #ifndef lint
     37 __RCSID("$NetBSD: ssl.c,v 1.10 2021/06/03 10:23:33 lukem Exp $");
     38 #endif
     39 
     40 #include <errno.h>
     41 #include <fcntl.h>
     42 #include <stdarg.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <string.h>
     46 #include <time.h>
     47 #include <unistd.h>
     48 
     49 #include <sys/param.h>
     50 #include <sys/select.h>
     51 #include <sys/uio.h>
     52 
     53 #include <netinet/tcp.h>
     54 #include <netinet/in.h>
     55 
     56 #ifdef WITH_SSL
     57 #include <openssl/crypto.h>
     58 #include <openssl/x509.h>
     59 #include <openssl/pem.h>
     60 #include <openssl/ssl.h>
     61 #include <openssl/err.h>
     62 #endif
     63 
     64 #include "ssl.h"
     65 
     66 extern int quit_time, verbose, ftp_debug;
     67 extern FILE *ttyout;
     68 
     69 struct fetch_connect {
     70 	int			 sd;		/* file/socket descriptor */
     71 	char			*buf;		/* buffer */
     72 	size_t			 bufsize;	/* buffer size */
     73 	size_t			 bufpos;	/* position of buffer */
     74 	size_t			 buflen;	/* length of buffer contents */
     75 	struct {				/* data cached after an
     76 						   interrupted read */
     77 		char	*buf;
     78 		size_t	 size;
     79 		size_t	 pos;
     80 		size_t	 len;
     81 	} cache;
     82 	int 			 issock;
     83 	int			 iserr;
     84 	int			 iseof;
     85 #ifdef WITH_SSL
     86 	SSL			*ssl;		/* SSL handle */
     87 #endif
     88 };
     89 
     90 /*
     91  * Write a vector to a connection w/ timeout
     92  * Note: can modify the iovec.
     93  */
     94 static ssize_t
     95 fetch_writev(struct fetch_connect *conn, struct iovec *iov, int iovcnt)
     96 {
     97 	struct timeval now, timeout, delta;
     98 	fd_set writefds;
     99 	ssize_t len, total;
    100 	int fd = conn->sd;
    101 	int r;
    102 
    103 	if (quit_time > 0) {
    104 		FD_ZERO(&writefds);
    105 		gettimeofday(&timeout, NULL);
    106 		timeout.tv_sec += quit_time;
    107 	}
    108 
    109 	total = 0;
    110 	while (iovcnt > 0) {
    111 		while (quit_time > 0 && !FD_ISSET(fd, &writefds)) {
    112 			FD_SET(fd, &writefds);
    113 			gettimeofday(&now, NULL);
    114 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
    115 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
    116 			if (delta.tv_usec < 0) {
    117 				delta.tv_usec += 1000000;
    118 				delta.tv_sec--;
    119 			}
    120 			if (delta.tv_sec < 0) {
    121 				errno = ETIMEDOUT;
    122 				return -1;
    123 			}
    124 			errno = 0;
    125 			r = select(fd + 1, NULL, &writefds, NULL, &delta);
    126 			if (r == -1) {
    127 				if (errno == EINTR)
    128 					continue;
    129 				return -1;
    130 			}
    131 		}
    132 		errno = 0;
    133 #ifdef WITH_SSL
    134 		if (conn->ssl != NULL)
    135 			len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len);
    136 		else
    137 #endif
    138 			len = writev(fd, iov, iovcnt);
    139 		if (len == 0) {
    140 			/* we consider a short write a failure */
    141 			/* XXX perhaps we shouldn't in the SSL case */
    142 			errno = EPIPE;
    143 			return -1;
    144 		}
    145 		if (len < 0) {
    146 			if (errno == EINTR || errno == EAGAIN)
    147 				continue;
    148 			return -1;
    149 		}
    150 		total += len;
    151 		while (iovcnt > 0 && len >= (ssize_t)iov->iov_len) {
    152 			len -= iov->iov_len;
    153 			iov++;
    154 			iovcnt--;
    155 		}
    156 		if (iovcnt > 0) {
    157 			iov->iov_len -= len;
    158 			iov->iov_base = (char *)iov->iov_base + len;
    159 		}
    160 	}
    161 	return total;
    162 }
    163 
    164 static ssize_t
    165 fetch_write(const void *str, size_t len, struct fetch_connect *conn)
    166 {
    167 	struct iovec iov[1];
    168 
    169 	iov[0].iov_base = (char *)__UNCONST(str);
    170 	iov[0].iov_len = len;
    171 	return fetch_writev(conn, iov, 1);
    172 }
    173 
    174 /*
    175  * Send a formatted line; optionally echo to terminal
    176  */
    177 int
    178 fetch_printf(struct fetch_connect *conn, const char *fmt, ...)
    179 {
    180 	va_list ap;
    181 	size_t len;
    182 	char *msg;
    183 	int r;
    184 
    185 	va_start(ap, fmt);
    186 	len = vasprintf(&msg, fmt, ap);
    187 	va_end(ap);
    188 
    189 	if (msg == NULL) {
    190 		errno = ENOMEM;
    191 		return -1;
    192 	}
    193 
    194 	r = fetch_write(msg, len, conn);
    195 	free(msg);
    196 	return r;
    197 }
    198 
    199 int
    200 fetch_fileno(struct fetch_connect *conn)
    201 {
    202 
    203 	return conn->sd;
    204 }
    205 
    206 int
    207 fetch_error(struct fetch_connect *conn)
    208 {
    209 
    210 	return conn->iserr;
    211 }
    212 
    213 static void
    214 fetch_clearerr(struct fetch_connect *conn)
    215 {
    216 
    217 	conn->iserr = 0;
    218 }
    219 
    220 int
    221 fetch_flush(struct fetch_connect *conn)
    222 {
    223 
    224 	if (conn->issock) {
    225 		int fd = conn->sd;
    226 		int v;
    227 #ifdef TCP_NOPUSH
    228 		v = 0;
    229 		setsockopt(fd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v));
    230 #endif
    231 		v = 1;
    232 		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
    233 	}
    234 	return 0;
    235 }
    236 
    237 /*ARGSUSED*/
    238 struct fetch_connect *
    239 fetch_open(const char *fname, const char *fmode)
    240 {
    241 	struct fetch_connect *conn;
    242 	int fd;
    243 
    244 	fd = open(fname, O_RDONLY); /* XXX: fmode */
    245 	if (fd < 0)
    246 		return NULL;
    247 
    248 	if ((conn = calloc(1, sizeof(*conn))) == NULL) {
    249 		close(fd);
    250 		return NULL;
    251 	}
    252 
    253 	conn->sd = fd;
    254 	conn->issock = 0;
    255 	return conn;
    256 }
    257 
    258 /*ARGSUSED*/
    259 struct fetch_connect *
    260 fetch_fdopen(int sd, const char *fmode)
    261 {
    262 	struct fetch_connect *conn;
    263 #if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH)
    264 	int opt = 1;
    265 #endif
    266 
    267 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
    268 		return NULL;
    269 
    270 	conn->sd = sd;
    271 	conn->issock = 1;
    272 	fcntl(sd, F_SETFD, FD_CLOEXEC);
    273 #ifdef SO_NOSIGPIPE
    274 	setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
    275 #endif
    276 #ifdef TCP_NOPUSH
    277 	setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt));
    278 #endif
    279 	return conn;
    280 }
    281 
    282 int
    283 fetch_close(struct fetch_connect *conn)
    284 {
    285 	if (conn == NULL)
    286 		return 0;
    287 
    288 	fetch_flush(conn);
    289 #ifdef WITH_SSL
    290 	SSL_free(conn->ssl);
    291 #endif
    292 	close(conn->sd);
    293 	free(conn->cache.buf);
    294 	free(conn->buf);
    295 	free(conn);
    296 	return 0;
    297 }
    298 
    299 #define FETCH_WRITE_WAIT	-3
    300 #define FETCH_READ_WAIT		-2
    301 #define FETCH_READ_ERROR	-1
    302 
    303 #ifdef WITH_SSL
    304 static ssize_t
    305 fetch_ssl_read(SSL *ssl, void *buf, size_t len)
    306 {
    307 	ssize_t rlen;
    308 	rlen = SSL_read(ssl, buf, len);
    309 	if (rlen >= 0)
    310 		return rlen;
    311 
    312 	switch (SSL_get_error(ssl, rlen)) {
    313 	case SSL_ERROR_WANT_READ:
    314 		return FETCH_READ_WAIT;
    315 	case SSL_ERROR_WANT_WRITE:
    316 		return FETCH_WRITE_WAIT;
    317 	default:
    318 		ERR_print_errors_fp(ttyout);
    319 		return FETCH_READ_ERROR;
    320 	}
    321 }
    322 #endif /* WITH_SSL */
    323 
    324 static ssize_t
    325 fetch_nonssl_read(int sd, void *buf, size_t len)
    326 {
    327 	ssize_t rlen;
    328 
    329 	rlen = read(sd, buf, len);
    330 	if (rlen == -1) {
    331 		if (errno == EAGAIN || errno == EINTR)
    332 			return FETCH_READ_WAIT;
    333 		return FETCH_READ_ERROR;
    334 	}
    335 	return rlen;
    336 }
    337 
    338 /*
    339  * Cache some data that was read from a socket but cannot be immediately
    340  * returned because of an interrupted system call.
    341  */
    342 static int
    343 fetch_cache_data(struct fetch_connect *conn, char *src, size_t nbytes)
    344 {
    345 
    346 	if (conn->cache.size < nbytes) {
    347 		char *tmp = realloc(conn->cache.buf, nbytes);
    348 		if (tmp == NULL)
    349 			return -1;
    350 
    351 		conn->cache.buf = tmp;
    352 		conn->cache.size = nbytes;
    353 	}
    354 
    355 	memcpy(conn->cache.buf, src, nbytes);
    356 	conn->cache.len = nbytes;
    357 	conn->cache.pos = 0;
    358 	return 0;
    359 }
    360 
    361 static int
    362 fetch_wait(struct fetch_connect *conn, ssize_t rlen, struct timeval *timeout)
    363 {
    364 	struct timeval now, delta;
    365 	int fd = conn->sd;
    366 	fd_set fds;
    367 
    368 	FD_ZERO(&fds);
    369 	while (!FD_ISSET(fd, &fds)) {
    370 		FD_SET(fd, &fds);
    371 		if (quit_time > 0) {
    372 			gettimeofday(&now, NULL);
    373 			if (!timercmp(timeout, &now, >)) {
    374 				fprintf(ttyout, "\r\n%s: transfer aborted"
    375 				    " because stalled for %lu sec.\r\n",
    376 				    getprogname(), (unsigned long)quit_time);
    377 				errno = ETIMEDOUT;
    378 				conn->iserr = ETIMEDOUT;
    379 				return -1;
    380 			}
    381 			timersub(timeout, &now, &delta);
    382 		}
    383 		errno = 0;
    384 		if (select(fd + 1,
    385 			rlen == FETCH_READ_WAIT ? &fds : NULL,
    386 			rlen == FETCH_WRITE_WAIT ? &fds : NULL,
    387 			NULL, quit_time > 0 ? &delta : NULL) < 0) {
    388 			if (errno == EINTR)
    389 				continue;
    390 			conn->iserr = errno;
    391 			return -1;
    392 		}
    393 	}
    394 	return 0;
    395 }
    396 
    397 size_t
    398 fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn)
    399 {
    400 	ssize_t rlen, total;
    401 	size_t len;
    402 	char *start, *buf;
    403 	struct timeval timeout;
    404 
    405 	if (quit_time > 0) {
    406 		gettimeofday(&timeout, NULL);
    407 		timeout.tv_sec += quit_time;
    408 	}
    409 
    410 	total = 0;
    411 	start = buf = ptr;
    412 	len = size * nmemb;
    413 
    414 	if (conn->cache.len > 0) {
    415 		/*
    416 		 * The last invocation of fetch_read was interrupted by a
    417 		 * signal after some data had been read from the socket. Copy
    418 		 * the cached data into the supplied buffer before trying to
    419 		 * read from the socket again.
    420 		 */
    421 		total = (conn->cache.len < len) ? conn->cache.len : len;
    422 		memcpy(buf, conn->cache.buf, total);
    423 
    424 		conn->cache.len -= total;
    425 		conn->cache.pos += total;
    426 		len -= total;
    427 		buf += total;
    428 	}
    429 
    430 	while (len > 0) {
    431 		/*
    432 		 * The socket is non-blocking.  Instead of the canonical
    433 		 * select() -> read(), we do the following:
    434 		 *
    435 		 * 1) call read() or SSL_read().
    436 		 * 2) if an error occurred, return -1.
    437 		 * 3) if we received data but we still expect more,
    438 		 *    update our counters and loop.
    439 		 * 4) if read() or SSL_read() signaled EOF, return.
    440 		 * 5) if we did not receive any data but we're not at EOF,
    441 		 *    call select().
    442 		 *
    443 		 * In the SSL case, this is necessary because if we
    444 		 * receive a close notification, we have to call
    445 		 * SSL_read() one additional time after we've read
    446 		 * everything we received.
    447 		 *
    448 		 * In the non-SSL case, it may improve performance (very
    449 		 * slightly) when reading small amounts of data.
    450 		 */
    451 #ifdef WITH_SSL
    452 		if (conn->ssl != NULL)
    453 			rlen = fetch_ssl_read(conn->ssl, buf, len);
    454 		else
    455 #endif
    456 			rlen = fetch_nonssl_read(conn->sd, buf, len);
    457 		switch (rlen) {
    458 		case 0:
    459 			conn->iseof = 1;
    460 			return total;
    461 		case FETCH_READ_ERROR:
    462 			conn->iserr = errno;
    463 			if (errno == EINTR)
    464 				fetch_cache_data(conn, start, total);
    465 			return 0;
    466 		case FETCH_READ_WAIT:
    467 		case FETCH_WRITE_WAIT:
    468 			if (fetch_wait(conn, rlen, &timeout) == -1)
    469 				return 0;
    470 			break;
    471 		default:
    472 			len -= rlen;
    473 			buf += rlen;
    474 			total += rlen;
    475 			break;
    476 		}
    477 	}
    478 	return total;
    479 }
    480 
    481 #define MIN_BUF_SIZE 1024
    482 
    483 /*
    484  * Read a line of text from a connection w/ timeout
    485  */
    486 char *
    487 fetch_getln(char *str, int size, struct fetch_connect *conn)
    488 {
    489 	size_t tmpsize;
    490 	size_t len;
    491 	char c;
    492 
    493 	if (conn->buf == NULL) {
    494 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
    495 			errno = ENOMEM;
    496 			conn->iserr = 1;
    497 			return NULL;
    498 		}
    499 		conn->bufsize = MIN_BUF_SIZE;
    500 	}
    501 
    502 	if (conn->iserr || conn->iseof)
    503 		return NULL;
    504 
    505 	if (conn->buflen - conn->bufpos > 0)
    506 		goto done;
    507 
    508 	conn->buf[0] = '\0';
    509 	conn->bufpos = 0;
    510 	conn->buflen = 0;
    511 	do {
    512 		len = fetch_read(&c, sizeof(c), 1, conn);
    513 		if (len == 0) {
    514 			if (conn->iserr)
    515 				return NULL;
    516 			if (conn->iseof)
    517 				break;
    518 			abort();
    519 		}
    520 		conn->buf[conn->buflen++] = c;
    521 		if (conn->buflen == conn->bufsize) {
    522 			char *tmp = conn->buf;
    523 			tmpsize = conn->bufsize * 2 + 1;
    524 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
    525 				errno = ENOMEM;
    526 				conn->iserr = 1;
    527 				return NULL;
    528 			}
    529 			conn->buf = tmp;
    530 			conn->bufsize = tmpsize;
    531 		}
    532 	} while (c != '\n');
    533 
    534 	if (conn->buflen == 0)
    535 		return NULL;
    536  done:
    537 	tmpsize = MIN(size - 1, (int)(conn->buflen - conn->bufpos));
    538 	memcpy(str, conn->buf + conn->bufpos, tmpsize);
    539 	str[tmpsize] = '\0';
    540 	conn->bufpos += tmpsize;
    541 	return str;
    542 }
    543 
    544 int
    545 fetch_getline(struct fetch_connect *conn, char *buf, size_t buflen,
    546     const char **errormsg)
    547 {
    548 	size_t len;
    549 	int rv;
    550 
    551 	if (fetch_getln(buf, buflen, conn) == NULL) {
    552 		if (conn->iseof) {	/* EOF */
    553 			rv = -2;
    554 			if (errormsg)
    555 				*errormsg = "\nEOF received";
    556 		} else {		/* error */
    557 			rv = -1;
    558 			if (errormsg)
    559 				*errormsg = "Error encountered";
    560 		}
    561 		fetch_clearerr(conn);
    562 		return rv;
    563 	}
    564 	len = strlen(buf);
    565 	if (buf[len - 1] == '\n') {	/* clear any trailing newline */
    566 		buf[--len] = '\0';
    567 	} else if (len == buflen - 1) {	/* line too long */
    568 		while (1) {
    569 			char c;
    570 			size_t rlen = fetch_read(&c, sizeof(c), 1, conn);
    571 			if (rlen == 0 || c == '\n')
    572 				break;
    573 		}
    574 		if (errormsg)
    575 			*errormsg = "Input line is too long";
    576 		fetch_clearerr(conn);
    577 		return -3;
    578 	}
    579 	if (errormsg)
    580 		*errormsg = NULL;
    581 	return len;
    582 }
    583 
    584 #ifdef WITH_SSL
    585 void *
    586 fetch_start_ssl(int sock, const char *servername)
    587 {
    588 	SSL *ssl;
    589 	SSL_CTX *ctx;
    590 	int ret, ssl_err;
    591 
    592 	/* Init the SSL library and context */
    593 	if (!SSL_library_init()){
    594 		fprintf(ttyout, "SSL library init failed\n");
    595 		return NULL;
    596 	}
    597 
    598 	SSL_load_error_strings();
    599 
    600 	ctx = SSL_CTX_new(SSLv23_client_method());
    601 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
    602 
    603 	ssl = SSL_new(ctx);
    604 	if (ssl == NULL){
    605 		fprintf(ttyout, "SSL context creation failed\n");
    606 		SSL_CTX_free(ctx);
    607 		return NULL;
    608 	}
    609 	SSL_set_fd(ssl, sock);
    610 	if (!SSL_set_tlsext_host_name(ssl, __UNCONST(servername))) {
    611 		fprintf(ttyout, "SSL hostname setting failed\n");
    612 		SSL_CTX_free(ctx);
    613 		return NULL;
    614 	}
    615 	while ((ret = SSL_connect(ssl)) == -1) {
    616 		ssl_err = SSL_get_error(ssl, ret);
    617 		if (ssl_err != SSL_ERROR_WANT_READ &&
    618 		    ssl_err != SSL_ERROR_WANT_WRITE) {
    619 			ERR_print_errors_fp(ttyout);
    620 			SSL_free(ssl);
    621 			return NULL;
    622 		}
    623 	}
    624 
    625 	if (ftp_debug && verbose) {
    626 		X509 *cert;
    627 		X509_NAME *name;
    628 		char *str;
    629 
    630 		fprintf(ttyout, "SSL connection established using %s\n",
    631 		    SSL_get_cipher(ssl));
    632 		cert = SSL_get_peer_certificate(ssl);
    633 		name = X509_get_subject_name(cert);
    634 		str = X509_NAME_oneline(name, 0, 0);
    635 		fprintf(ttyout, "Certificate subject: %s\n", str);
    636 		free(str);
    637 		name = X509_get_issuer_name(cert);
    638 		str = X509_NAME_oneline(name, 0, 0);
    639 		fprintf(ttyout, "Certificate issuer: %s\n", str);
    640 		free(str);
    641 	}
    642 
    643 	return ssl;
    644 }
    645 #endif /* WITH_SSL */
    646 
    647 
    648 void
    649 fetch_set_ssl(struct fetch_connect *conn, void *ssl)
    650 {
    651 #ifdef WITH_SSL
    652 	conn->ssl = ssl;
    653 #endif
    654 }
    655