xref: /freebsd/contrib/sendmail/src/sfsasl.c (revision 552d4955b823634ed151c69903f0b9d33e76e78e)
106f25ae9SGregory Neil Shapiro /*
2e3793f76SGregory Neil Shapiro  * Copyright (c) 1999-2006, 2008 Sendmail, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
406f25ae9SGregory Neil Shapiro  *
506f25ae9SGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
606f25ae9SGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
706f25ae9SGregory Neil Shapiro  * the sendmail distribution.
806f25ae9SGregory Neil Shapiro  *
906f25ae9SGregory Neil Shapiro  */
1006f25ae9SGregory Neil Shapiro 
1140266059SGregory Neil Shapiro #include <sm/gen.h>
12*552d4955SGregory Neil Shapiro SM_RCSID("@(#)$Id: sfsasl.c,v 8.120 2013/03/15 17:49:12 guenther Exp $")
1306f25ae9SGregory Neil Shapiro #include <stdlib.h>
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15af9557fdSGregory Neil Shapiro #include <sm/time.h>
1640266059SGregory Neil Shapiro #include <errno.h>
17b6bacd31SGregory Neil Shapiro 
18b6bacd31SGregory Neil Shapiro /* allow to disable error handling code just in case... */
19b6bacd31SGregory Neil Shapiro #ifndef DEAL_WITH_ERROR_SSL
20b6bacd31SGregory Neil Shapiro # define DEAL_WITH_ERROR_SSL	1
21b6bacd31SGregory Neil Shapiro #endif /* ! DEAL_WITH_ERROR_SSL */
22b6bacd31SGregory Neil Shapiro 
2340266059SGregory Neil Shapiro #if SASL
2406f25ae9SGregory Neil Shapiro # include "sfsasl.h"
2506f25ae9SGregory Neil Shapiro 
2640266059SGregory Neil Shapiro /* Structure used by the "sasl" file type */
2740266059SGregory Neil Shapiro struct sasl_obj
2840266059SGregory Neil Shapiro {
2940266059SGregory Neil Shapiro 	SM_FILE_T *fp;
3040266059SGregory Neil Shapiro 	sasl_conn_t *conn;
3140266059SGregory Neil Shapiro };
3240266059SGregory Neil Shapiro 
3340266059SGregory Neil Shapiro struct sasl_info
3440266059SGregory Neil Shapiro {
3540266059SGregory Neil Shapiro 	SM_FILE_T *fp;
3640266059SGregory Neil Shapiro 	sasl_conn_t *conn;
3740266059SGregory Neil Shapiro };
3840266059SGregory Neil Shapiro 
3940266059SGregory Neil Shapiro /*
4040266059SGregory Neil Shapiro **  SASL_GETINFO - returns requested information about a "sasl" file
4140266059SGregory Neil Shapiro **		  descriptor.
4240266059SGregory Neil Shapiro **
4340266059SGregory Neil Shapiro **	Parameters:
4440266059SGregory Neil Shapiro **		fp -- the file descriptor
4540266059SGregory Neil Shapiro **		what -- the type of information requested
4640266059SGregory Neil Shapiro **		valp -- the thang to return the information in
4740266059SGregory Neil Shapiro **
4840266059SGregory Neil Shapiro **	Returns:
4940266059SGregory Neil Shapiro **		-1 for unknown requests
5040266059SGregory Neil Shapiro **		>=0 on success with valp filled in (if possible).
5140266059SGregory Neil Shapiro */
5240266059SGregory Neil Shapiro 
5340266059SGregory Neil Shapiro static int sasl_getinfo __P((SM_FILE_T *, int, void *));
5440266059SGregory Neil Shapiro 
5540266059SGregory Neil Shapiro static int
5640266059SGregory Neil Shapiro sasl_getinfo(fp, what, valp)
5740266059SGregory Neil Shapiro 	SM_FILE_T *fp;
5840266059SGregory Neil Shapiro 	int what;
5940266059SGregory Neil Shapiro 	void *valp;
6040266059SGregory Neil Shapiro {
6140266059SGregory Neil Shapiro 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
6240266059SGregory Neil Shapiro 
6340266059SGregory Neil Shapiro 	switch (what)
6440266059SGregory Neil Shapiro 	{
6540266059SGregory Neil Shapiro 	  case SM_IO_WHAT_FD:
6640266059SGregory Neil Shapiro 		if (so->fp == NULL)
6740266059SGregory Neil Shapiro 			return -1;
6840266059SGregory Neil Shapiro 		return so->fp->f_file; /* for stdio fileno() compatability */
6940266059SGregory Neil Shapiro 
7040266059SGregory Neil Shapiro 	  case SM_IO_IS_READABLE:
7140266059SGregory Neil Shapiro 		if (so->fp == NULL)
7240266059SGregory Neil Shapiro 			return 0;
7340266059SGregory Neil Shapiro 
7440266059SGregory Neil Shapiro 		/* get info from underlying file */
7540266059SGregory Neil Shapiro 		return sm_io_getinfo(so->fp, what, valp);
7640266059SGregory Neil Shapiro 
7740266059SGregory Neil Shapiro 	  default:
7840266059SGregory Neil Shapiro 		return -1;
7940266059SGregory Neil Shapiro 	}
8040266059SGregory Neil Shapiro }
8140266059SGregory Neil Shapiro 
8240266059SGregory Neil Shapiro /*
8340266059SGregory Neil Shapiro **  SASL_OPEN -- creates the sasl specific information for opening a
8440266059SGregory Neil Shapiro **		file of the sasl type.
8540266059SGregory Neil Shapiro **
8640266059SGregory Neil Shapiro **	Parameters:
8740266059SGregory Neil Shapiro **		fp -- the file pointer associated with the new open
8840266059SGregory Neil Shapiro **		info -- contains the sasl connection information pointer and
8940266059SGregory Neil Shapiro **			the original SM_FILE_T that holds the open
9040266059SGregory Neil Shapiro **		flags -- ignored
9140266059SGregory Neil Shapiro **		rpool -- ignored
9240266059SGregory Neil Shapiro **
9340266059SGregory Neil Shapiro **	Returns:
9440266059SGregory Neil Shapiro **		0 on success
9540266059SGregory Neil Shapiro */
9640266059SGregory Neil Shapiro 
9740266059SGregory Neil Shapiro static int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
9840266059SGregory Neil Shapiro 
9940266059SGregory Neil Shapiro /* ARGSUSED2 */
10040266059SGregory Neil Shapiro static int
10140266059SGregory Neil Shapiro sasl_open(fp, info, flags, rpool)
10240266059SGregory Neil Shapiro 	SM_FILE_T *fp;
10340266059SGregory Neil Shapiro 	const void *info;
10440266059SGregory Neil Shapiro 	int flags;
10540266059SGregory Neil Shapiro 	const void *rpool;
10640266059SGregory Neil Shapiro {
10740266059SGregory Neil Shapiro 	struct sasl_obj *so;
10840266059SGregory Neil Shapiro 	struct sasl_info *si = (struct sasl_info *) info;
10940266059SGregory Neil Shapiro 
11040266059SGregory Neil Shapiro 	so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
111a7ec597cSGregory Neil Shapiro 	if (so == NULL)
112a7ec597cSGregory Neil Shapiro 	{
113a7ec597cSGregory Neil Shapiro 		errno = ENOMEM;
114a7ec597cSGregory Neil Shapiro 		return -1;
115a7ec597cSGregory Neil Shapiro 	}
11640266059SGregory Neil Shapiro 	so->fp = si->fp;
11740266059SGregory Neil Shapiro 	so->conn = si->conn;
11840266059SGregory Neil Shapiro 
11940266059SGregory Neil Shapiro 	/*
12040266059SGregory Neil Shapiro 	**  The underlying 'fp' is set to SM_IO_NOW so that the entire
12140266059SGregory Neil Shapiro 	**  encoded string is written in one chunk. Otherwise there is
12240266059SGregory Neil Shapiro 	**  the possibility that it may appear illegal, bogus or
12340266059SGregory Neil Shapiro 	**  mangled to the other side of the connection.
12440266059SGregory Neil Shapiro 	**  We will read or write through 'fp' since it is the opaque
12540266059SGregory Neil Shapiro 	**  connection for the communications. We need to treat it this
12640266059SGregory Neil Shapiro 	**  way in case the encoded string is to be sent down a TLS
12740266059SGregory Neil Shapiro 	**  connection rather than, say, sm_io's stdio.
12840266059SGregory Neil Shapiro 	*/
12940266059SGregory Neil Shapiro 
13040266059SGregory Neil Shapiro 	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
13140266059SGregory Neil Shapiro 	fp->f_cookie = so;
13240266059SGregory Neil Shapiro 	return 0;
13340266059SGregory Neil Shapiro }
13440266059SGregory Neil Shapiro 
13540266059SGregory Neil Shapiro /*
13640266059SGregory Neil Shapiro **  SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
13740266059SGregory Neil Shapiro **
13840266059SGregory Neil Shapiro **	Parameters:
13940266059SGregory Neil Shapiro **		fp -- the file pointer to close
14040266059SGregory Neil Shapiro **
14140266059SGregory Neil Shapiro **	Returns:
14240266059SGregory Neil Shapiro **		0 on success
14340266059SGregory Neil Shapiro */
14440266059SGregory Neil Shapiro 
14540266059SGregory Neil Shapiro static int sasl_close __P((SM_FILE_T *));
14640266059SGregory Neil Shapiro 
14740266059SGregory Neil Shapiro static int
14840266059SGregory Neil Shapiro sasl_close(fp)
14940266059SGregory Neil Shapiro 	SM_FILE_T *fp;
15040266059SGregory Neil Shapiro {
15140266059SGregory Neil Shapiro 	struct sasl_obj *so;
15240266059SGregory Neil Shapiro 
15340266059SGregory Neil Shapiro 	so = (struct sasl_obj *) fp->f_cookie;
154a7ec597cSGregory Neil Shapiro 	if (so == NULL)
155a7ec597cSGregory Neil Shapiro 		return 0;
15640266059SGregory Neil Shapiro 	if (so->fp != NULL)
15740266059SGregory Neil Shapiro 	{
15840266059SGregory Neil Shapiro 		sm_io_close(so->fp, SM_TIME_DEFAULT);
15940266059SGregory Neil Shapiro 		so->fp = NULL;
16040266059SGregory Neil Shapiro 	}
16140266059SGregory Neil Shapiro 	sm_free(so);
16240266059SGregory Neil Shapiro 	so = NULL;
16340266059SGregory Neil Shapiro 	return 0;
16440266059SGregory Neil Shapiro }
16540266059SGregory Neil Shapiro 
166193538b7SGregory Neil Shapiro /* how to deallocate a buffer allocated by SASL */
16740266059SGregory Neil Shapiro extern void	sm_sasl_free __P((void *));
16840266059SGregory Neil Shapiro #  define SASL_DEALLOC(b)	sm_sasl_free(b)
16940266059SGregory Neil Shapiro 
17040266059SGregory Neil Shapiro /*
17140266059SGregory Neil Shapiro **  SASL_READ -- read encrypted information and decrypt it for the caller
17240266059SGregory Neil Shapiro **
17340266059SGregory Neil Shapiro **	Parameters:
17440266059SGregory Neil Shapiro **		fp -- the file pointer
17540266059SGregory Neil Shapiro **		buf -- the location to place the decrypted information
17640266059SGregory Neil Shapiro **		size -- the number of bytes to read after decryption
17740266059SGregory Neil Shapiro **
17840266059SGregory Neil Shapiro **	Results:
17940266059SGregory Neil Shapiro **		-1 on error
18040266059SGregory Neil Shapiro **		otherwise the number of bytes read
18140266059SGregory Neil Shapiro */
18240266059SGregory Neil Shapiro 
18340266059SGregory Neil Shapiro static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
184193538b7SGregory Neil Shapiro 
18506f25ae9SGregory Neil Shapiro static ssize_t
18640266059SGregory Neil Shapiro sasl_read(fp, buf, size)
18740266059SGregory Neil Shapiro 	SM_FILE_T *fp;
18840266059SGregory Neil Shapiro 	char *buf;
18906f25ae9SGregory Neil Shapiro 	size_t size;
19006f25ae9SGregory Neil Shapiro {
19140266059SGregory Neil Shapiro 	int result;
19240266059SGregory Neil Shapiro 	ssize_t len;
19394c01205SGregory Neil Shapiro # if SASL >= 20000
19413bd1963SGregory Neil Shapiro 	static const char *outbuf = NULL;
19594c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
196193538b7SGregory Neil Shapiro 	static char *outbuf = NULL;
19794c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
198193538b7SGregory Neil Shapiro 	static unsigned int outlen = 0;
199193538b7SGregory Neil Shapiro 	static unsigned int offset = 0;
20040266059SGregory Neil Shapiro 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
20106f25ae9SGregory Neil Shapiro 
202193538b7SGregory Neil Shapiro 	/*
203193538b7SGregory Neil Shapiro 	**  sasl_decode() may require more data than a single read() returns.
204193538b7SGregory Neil Shapiro 	**  Hence we have to put a loop around the decoding.
205193538b7SGregory Neil Shapiro 	**  This also requires that we may have to split up the returned
206193538b7SGregory Neil Shapiro 	**  data since it might be larger than the allowed size.
207193538b7SGregory Neil Shapiro 	**  Therefore we use a static pointer and return portions of it
208193538b7SGregory Neil Shapiro 	**  if necessary.
209a7ec597cSGregory Neil Shapiro 	**  XXX Note: This function is not thread-safe nor can it be used
210a7ec597cSGregory Neil Shapiro 	**  on more than one file. A correct implementation would store
211a7ec597cSGregory Neil Shapiro 	**  this data in fp->f_cookie.
212193538b7SGregory Neil Shapiro 	*/
21306f25ae9SGregory Neil Shapiro 
21413bd1963SGregory Neil Shapiro # if SASL >= 20000
21513bd1963SGregory Neil Shapiro 	while (outlen == 0)
21613bd1963SGregory Neil Shapiro # else /* SASL >= 20000 */
217193538b7SGregory Neil Shapiro 	while (outbuf == NULL && outlen == 0)
21813bd1963SGregory Neil Shapiro # endif /* SASL >= 20000 */
219193538b7SGregory Neil Shapiro 	{
22040266059SGregory Neil Shapiro 		len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
22106f25ae9SGregory Neil Shapiro 		if (len <= 0)
22206f25ae9SGregory Neil Shapiro 			return len;
22340266059SGregory Neil Shapiro 		result = sasl_decode(so->conn, buf,
22440266059SGregory Neil Shapiro 				     (unsigned int) len, &outbuf, &outlen);
22506f25ae9SGregory Neil Shapiro 		if (result != SASL_OK)
22606f25ae9SGregory Neil Shapiro 		{
2274e4196cbSGregory Neil Shapiro 			if (LogLevel > 7)
2284e4196cbSGregory Neil Shapiro 				sm_syslog(LOG_WARNING, NOQID,
2294e4196cbSGregory Neil Shapiro 					"AUTH: sasl_decode error=%d", result);
230193538b7SGregory Neil Shapiro 			outbuf = NULL;
231193538b7SGregory Neil Shapiro 			offset = 0;
232193538b7SGregory Neil Shapiro 			outlen = 0;
23306f25ae9SGregory Neil Shapiro 			return -1;
23406f25ae9SGregory Neil Shapiro 		}
235193538b7SGregory Neil Shapiro 	}
23606f25ae9SGregory Neil Shapiro 
23740266059SGregory Neil Shapiro 	if (outbuf == NULL)
23806f25ae9SGregory Neil Shapiro 	{
23940266059SGregory Neil Shapiro 		/* be paranoid: outbuf == NULL but outlen != 0 */
24040266059SGregory Neil Shapiro 		syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
24140266059SGregory Neil Shapiro 		/* NOTREACHED */
24240266059SGregory Neil Shapiro 	}
243193538b7SGregory Neil Shapiro 	if (outlen - offset > size)
244193538b7SGregory Neil Shapiro 	{
245193538b7SGregory Neil Shapiro 		/* return another part of the buffer */
24640266059SGregory Neil Shapiro 		(void) memcpy(buf, outbuf + offset, size);
247193538b7SGregory Neil Shapiro 		offset += size;
24840266059SGregory Neil Shapiro 		len = size;
24906f25ae9SGregory Neil Shapiro 	}
250193538b7SGregory Neil Shapiro 	else
251193538b7SGregory Neil Shapiro 	{
252193538b7SGregory Neil Shapiro 		/* return the rest of the buffer */
25340266059SGregory Neil Shapiro 		len = outlen - offset;
25440266059SGregory Neil Shapiro 		(void) memcpy(buf, outbuf + offset, (size_t) len);
25594c01205SGregory Neil Shapiro # if SASL < 20000
256193538b7SGregory Neil Shapiro 		SASL_DEALLOC(outbuf);
25794c01205SGregory Neil Shapiro # endif /* SASL < 20000 */
258193538b7SGregory Neil Shapiro 		outbuf = NULL;
259193538b7SGregory Neil Shapiro 		offset = 0;
260193538b7SGregory Neil Shapiro 		outlen = 0;
261193538b7SGregory Neil Shapiro 	}
26240266059SGregory Neil Shapiro 	return len;
26306f25ae9SGregory Neil Shapiro }
26406f25ae9SGregory Neil Shapiro 
26540266059SGregory Neil Shapiro /*
26640266059SGregory Neil Shapiro **  SASL_WRITE -- write information out after encrypting it
26740266059SGregory Neil Shapiro **
26840266059SGregory Neil Shapiro **	Parameters:
26940266059SGregory Neil Shapiro **		fp -- the file pointer
27040266059SGregory Neil Shapiro **		buf -- holds the data to be encrypted and written
27140266059SGregory Neil Shapiro **		size -- the number of bytes to have encrypted and written
27240266059SGregory Neil Shapiro **
27340266059SGregory Neil Shapiro **	Returns:
27440266059SGregory Neil Shapiro **		-1 on error
27540266059SGregory Neil Shapiro **		otherwise number of bytes written
27640266059SGregory Neil Shapiro */
27740266059SGregory Neil Shapiro 
27840266059SGregory Neil Shapiro static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
27940266059SGregory Neil Shapiro 
28006f25ae9SGregory Neil Shapiro static ssize_t
28140266059SGregory Neil Shapiro sasl_write(fp, buf, size)
28240266059SGregory Neil Shapiro 	SM_FILE_T *fp;
28340266059SGregory Neil Shapiro 	const char *buf;
28406f25ae9SGregory Neil Shapiro 	size_t size;
28506f25ae9SGregory Neil Shapiro {
28606f25ae9SGregory Neil Shapiro 	int result;
28794c01205SGregory Neil Shapiro # if SASL >= 20000
28894c01205SGregory Neil Shapiro 	const char *outbuf;
28994c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
29006f25ae9SGregory Neil Shapiro 	char *outbuf;
29194c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
292b6bacd31SGregory Neil Shapiro 	unsigned int outlen, *maxencode;
29340266059SGregory Neil Shapiro 	size_t ret = 0, total = 0;
29440266059SGregory Neil Shapiro 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
29506f25ae9SGregory Neil Shapiro 
296b6bacd31SGregory Neil Shapiro 	/*
297b6bacd31SGregory Neil Shapiro 	**  Fetch the maximum input buffer size for sasl_encode().
298b6bacd31SGregory Neil Shapiro 	**  This can be less than the size set in attemptauth()
2999bd497b8SGregory Neil Shapiro 	**  due to a negotiation with the other side, e.g.,
300b6bacd31SGregory Neil Shapiro 	**  Cyrus IMAP lmtp program sets maxbuf=4096,
301b6bacd31SGregory Neil Shapiro 	**  digestmd5 substracts 25 and hence we'll get 4071
302b6bacd31SGregory Neil Shapiro 	**  instead of 8192 (MAXOUTLEN).
303b6bacd31SGregory Neil Shapiro 	**  Hack (for now): simply reduce the size, callers are (must be)
304b6bacd31SGregory Neil Shapiro 	**  able to deal with that and invoke sasl_write() again with
305b6bacd31SGregory Neil Shapiro 	**  the rest of the data.
306b6bacd31SGregory Neil Shapiro 	**  Note: it would be better to store this value in the context
307b6bacd31SGregory Neil Shapiro 	**  after the negotiation.
308b6bacd31SGregory Neil Shapiro 	*/
309b6bacd31SGregory Neil Shapiro 
310b6bacd31SGregory Neil Shapiro 	result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
311b6bacd31SGregory Neil Shapiro 				(const void **) &maxencode);
312b6bacd31SGregory Neil Shapiro 	if (result == SASL_OK && size > *maxencode && *maxencode > 0)
313b6bacd31SGregory Neil Shapiro 		size = *maxencode;
314b6bacd31SGregory Neil Shapiro 
31540266059SGregory Neil Shapiro 	result = sasl_encode(so->conn, buf,
31640266059SGregory Neil Shapiro 			     (unsigned int) size, &outbuf, &outlen);
31706f25ae9SGregory Neil Shapiro 
31806f25ae9SGregory Neil Shapiro 	if (result != SASL_OK)
3194e4196cbSGregory Neil Shapiro 	{
3204e4196cbSGregory Neil Shapiro 		if (LogLevel > 7)
3214e4196cbSGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
3224e4196cbSGregory Neil Shapiro 				"AUTH: sasl_encode error=%d", result);
32306f25ae9SGregory Neil Shapiro 		return -1;
3244e4196cbSGregory Neil Shapiro 	}
32506f25ae9SGregory Neil Shapiro 
32606f25ae9SGregory Neil Shapiro 	if (outbuf != NULL)
32706f25ae9SGregory Neil Shapiro 	{
32840266059SGregory Neil Shapiro 		while (outlen > 0)
32940266059SGregory Neil Shapiro 		{
330af9557fdSGregory Neil Shapiro 			errno = 0;
331605302a5SGregory Neil Shapiro 			/* XXX result == 0? */
33240266059SGregory Neil Shapiro 			ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
33340266059SGregory Neil Shapiro 					  &outbuf[total], outlen);
334a7ec597cSGregory Neil Shapiro 			if (ret <= 0)
335a7ec597cSGregory Neil Shapiro 				return ret;
33640266059SGregory Neil Shapiro 			outlen -= ret;
33740266059SGregory Neil Shapiro 			total += ret;
33840266059SGregory Neil Shapiro 		}
33994c01205SGregory Neil Shapiro # if SASL < 20000
340193538b7SGregory Neil Shapiro 		SASL_DEALLOC(outbuf);
34194c01205SGregory Neil Shapiro # endif /* SASL < 20000 */
34206f25ae9SGregory Neil Shapiro 	}
34306f25ae9SGregory Neil Shapiro 	return size;
34406f25ae9SGregory Neil Shapiro }
34506f25ae9SGregory Neil Shapiro 
34640266059SGregory Neil Shapiro /*
34740266059SGregory Neil Shapiro **  SFDCSASL -- create sasl file type and open in and out file pointers
34840266059SGregory Neil Shapiro **	       for sendmail to read from and write to.
34940266059SGregory Neil Shapiro **
35040266059SGregory Neil Shapiro **	Parameters:
35140266059SGregory Neil Shapiro **		fin -- the sm_io file encrypted data to be read from
352af9557fdSGregory Neil Shapiro **		fout -- the sm_io file encrypted data to be written to
35340266059SGregory Neil Shapiro **		conn -- the sasl connection pointer
354af9557fdSGregory Neil Shapiro **		tmo -- timeout
35540266059SGregory Neil Shapiro **
35640266059SGregory Neil Shapiro **	Returns:
35740266059SGregory Neil Shapiro **		-1 on error
35840266059SGregory Neil Shapiro **		0 on success
35940266059SGregory Neil Shapiro **
36040266059SGregory Neil Shapiro **	Side effects:
36140266059SGregory Neil Shapiro **		The arguments "fin" and "fout" are replaced with the new
36240266059SGregory Neil Shapiro **		SM_FILE_T pointers.
36340266059SGregory Neil Shapiro */
36440266059SGregory Neil Shapiro 
36506f25ae9SGregory Neil Shapiro int
366af9557fdSGregory Neil Shapiro sfdcsasl(fin, fout, conn, tmo)
36740266059SGregory Neil Shapiro 	SM_FILE_T **fin;
36840266059SGregory Neil Shapiro 	SM_FILE_T **fout;
36906f25ae9SGregory Neil Shapiro 	sasl_conn_t *conn;
370af9557fdSGregory Neil Shapiro 	int tmo;
37106f25ae9SGregory Neil Shapiro {
37240266059SGregory Neil Shapiro 	SM_FILE_T *newin, *newout;
37340266059SGregory Neil Shapiro 	SM_FILE_T  SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
37440266059SGregory Neil Shapiro 		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
375af9557fdSGregory Neil Shapiro 		SM_TIME_DEFAULT);
37640266059SGregory Neil Shapiro 	struct sasl_info info;
37706f25ae9SGregory Neil Shapiro 
37806f25ae9SGregory Neil Shapiro 	if (conn == NULL)
37906f25ae9SGregory Neil Shapiro 	{
38006f25ae9SGregory Neil Shapiro 		/* no need to do anything */
38106f25ae9SGregory Neil Shapiro 		return 0;
38206f25ae9SGregory Neil Shapiro 	}
38306f25ae9SGregory Neil Shapiro 
38440266059SGregory Neil Shapiro 	SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
38540266059SGregory Neil Shapiro 		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
386af9557fdSGregory Neil Shapiro 		SM_TIME_DEFAULT);
38740266059SGregory Neil Shapiro 	info.fp = *fin;
38840266059SGregory Neil Shapiro 	info.conn = conn;
389e92d3f3fSGregory Neil Shapiro 	newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
390e92d3f3fSGregory Neil Shapiro 			SM_IO_RDONLY_B, NULL);
39106f25ae9SGregory Neil Shapiro 
39240266059SGregory Neil Shapiro 	if (newin == NULL)
39340266059SGregory Neil Shapiro 		return -1;
39406f25ae9SGregory Neil Shapiro 
39540266059SGregory Neil Shapiro 	info.fp = *fout;
39640266059SGregory Neil Shapiro 	info.conn = conn;
397e92d3f3fSGregory Neil Shapiro 	newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
398e92d3f3fSGregory Neil Shapiro 			SM_IO_WRONLY_B, NULL);
39906f25ae9SGregory Neil Shapiro 
40040266059SGregory Neil Shapiro 	if (newout == NULL)
40106f25ae9SGregory Neil Shapiro 	{
40240266059SGregory Neil Shapiro 		(void) sm_io_close(newin, SM_TIME_DEFAULT);
40306f25ae9SGregory Neil Shapiro 		return -1;
40406f25ae9SGregory Neil Shapiro 	}
40540266059SGregory Neil Shapiro 	sm_io_automode(newin, newout);
40640266059SGregory Neil Shapiro 
407af9557fdSGregory Neil Shapiro 	sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
408af9557fdSGregory Neil Shapiro 	sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
409af9557fdSGregory Neil Shapiro 
41040266059SGregory Neil Shapiro 	*fin = newin;
41140266059SGregory Neil Shapiro 	*fout = newout;
41206f25ae9SGregory Neil Shapiro 	return 0;
41306f25ae9SGregory Neil Shapiro }
41440266059SGregory Neil Shapiro #endif /* SASL */
41506f25ae9SGregory Neil Shapiro 
41640266059SGregory Neil Shapiro #if STARTTLS
41706f25ae9SGregory Neil Shapiro # include "sfsasl.h"
41806f25ae9SGregory Neil Shapiro #  include <openssl/err.h>
41906f25ae9SGregory Neil Shapiro 
42040266059SGregory Neil Shapiro /* Structure used by the "tls" file type */
42140266059SGregory Neil Shapiro struct tls_obj
42240266059SGregory Neil Shapiro {
42340266059SGregory Neil Shapiro 	SM_FILE_T *fp;
42440266059SGregory Neil Shapiro 	SSL *con;
42540266059SGregory Neil Shapiro };
42640266059SGregory Neil Shapiro 
42740266059SGregory Neil Shapiro struct tls_info
42840266059SGregory Neil Shapiro {
42940266059SGregory Neil Shapiro 	SM_FILE_T *fp;
43040266059SGregory Neil Shapiro 	SSL *con;
43140266059SGregory Neil Shapiro };
43240266059SGregory Neil Shapiro 
43340266059SGregory Neil Shapiro /*
43440266059SGregory Neil Shapiro **  TLS_GETINFO - returns requested information about a "tls" file
43540266059SGregory Neil Shapiro **		 descriptor.
43640266059SGregory Neil Shapiro **
43740266059SGregory Neil Shapiro **	Parameters:
43840266059SGregory Neil Shapiro **		fp -- the file descriptor
43940266059SGregory Neil Shapiro **		what -- the type of information requested
44040266059SGregory Neil Shapiro **		valp -- the thang to return the information in (unused)
44140266059SGregory Neil Shapiro **
44240266059SGregory Neil Shapiro **	Returns:
44340266059SGregory Neil Shapiro **		-1 for unknown requests
44440266059SGregory Neil Shapiro **		>=0 on success with valp filled in (if possible).
44540266059SGregory Neil Shapiro */
44640266059SGregory Neil Shapiro 
44740266059SGregory Neil Shapiro static int tls_getinfo __P((SM_FILE_T *, int, void *));
44840266059SGregory Neil Shapiro 
44940266059SGregory Neil Shapiro /* ARGSUSED2 */
45013058a91SGregory Neil Shapiro static int
45140266059SGregory Neil Shapiro tls_getinfo(fp, what, valp)
45240266059SGregory Neil Shapiro 	SM_FILE_T *fp;
45340266059SGregory Neil Shapiro 	int what;
45440266059SGregory Neil Shapiro 	void *valp;
45540266059SGregory Neil Shapiro {
45640266059SGregory Neil Shapiro 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
45740266059SGregory Neil Shapiro 
45840266059SGregory Neil Shapiro 	switch (what)
45940266059SGregory Neil Shapiro 	{
46040266059SGregory Neil Shapiro 	  case SM_IO_WHAT_FD:
46140266059SGregory Neil Shapiro 		if (so->fp == NULL)
46240266059SGregory Neil Shapiro 			return -1;
46340266059SGregory Neil Shapiro 		return so->fp->f_file; /* for stdio fileno() compatability */
46440266059SGregory Neil Shapiro 
46540266059SGregory Neil Shapiro 	  case SM_IO_IS_READABLE:
46640266059SGregory Neil Shapiro 		return SSL_pending(so->con) > 0;
46740266059SGregory Neil Shapiro 
46840266059SGregory Neil Shapiro 	  default:
46940266059SGregory Neil Shapiro 		return -1;
47040266059SGregory Neil Shapiro 	}
47140266059SGregory Neil Shapiro }
47240266059SGregory Neil Shapiro 
47340266059SGregory Neil Shapiro /*
47440266059SGregory Neil Shapiro **  TLS_OPEN -- creates the tls specific information for opening a
47540266059SGregory Neil Shapiro **	       file of the tls type.
47640266059SGregory Neil Shapiro **
47740266059SGregory Neil Shapiro **	Parameters:
47840266059SGregory Neil Shapiro **		fp -- the file pointer associated with the new open
47940266059SGregory Neil Shapiro **		info -- the sm_io file pointer holding the open and the
48040266059SGregory Neil Shapiro **			TLS encryption connection to be read from or written to
48140266059SGregory Neil Shapiro **		flags -- ignored
48240266059SGregory Neil Shapiro **		rpool -- ignored
48340266059SGregory Neil Shapiro **
48440266059SGregory Neil Shapiro **	Returns:
48540266059SGregory Neil Shapiro **		0 on success
48640266059SGregory Neil Shapiro */
48740266059SGregory Neil Shapiro 
48840266059SGregory Neil Shapiro static int tls_open __P((SM_FILE_T *, const void *, int, const void *));
48940266059SGregory Neil Shapiro 
49040266059SGregory Neil Shapiro /* ARGSUSED2 */
49140266059SGregory Neil Shapiro static int
49240266059SGregory Neil Shapiro tls_open(fp, info, flags, rpool)
49340266059SGregory Neil Shapiro 	SM_FILE_T *fp;
49440266059SGregory Neil Shapiro 	const void *info;
49540266059SGregory Neil Shapiro 	int flags;
49640266059SGregory Neil Shapiro 	const void *rpool;
49740266059SGregory Neil Shapiro {
49840266059SGregory Neil Shapiro 	struct tls_obj *so;
49940266059SGregory Neil Shapiro 	struct tls_info *ti = (struct tls_info *) info;
50040266059SGregory Neil Shapiro 
50140266059SGregory Neil Shapiro 	so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
502a7ec597cSGregory Neil Shapiro 	if (so == NULL)
503a7ec597cSGregory Neil Shapiro 	{
504a7ec597cSGregory Neil Shapiro 		errno = ENOMEM;
505a7ec597cSGregory Neil Shapiro 		return -1;
506a7ec597cSGregory Neil Shapiro 	}
50740266059SGregory Neil Shapiro 	so->fp = ti->fp;
50840266059SGregory Neil Shapiro 	so->con = ti->con;
50940266059SGregory Neil Shapiro 
51040266059SGregory Neil Shapiro 	/*
51140266059SGregory Neil Shapiro 	**  We try to get the "raw" file descriptor that TLS uses to
51240266059SGregory Neil Shapiro 	**  do the actual read/write with. This is to allow us control
51340266059SGregory Neil Shapiro 	**  over the file descriptor being a blocking or non-blocking type.
51440266059SGregory Neil Shapiro 	**  Under the covers TLS handles the change and this allows us
51540266059SGregory Neil Shapiro 	**  to do timeouts with sm_io.
51640266059SGregory Neil Shapiro 	*/
51740266059SGregory Neil Shapiro 
51840266059SGregory Neil Shapiro 	fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
51940266059SGregory Neil Shapiro 	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
52040266059SGregory Neil Shapiro 	fp->f_cookie = so;
52140266059SGregory Neil Shapiro 	return 0;
52240266059SGregory Neil Shapiro }
52340266059SGregory Neil Shapiro 
52440266059SGregory Neil Shapiro /*
52540266059SGregory Neil Shapiro **  TLS_CLOSE -- close the tls specific parts of the tls file pointer
52640266059SGregory Neil Shapiro **
52740266059SGregory Neil Shapiro **	Parameters:
52840266059SGregory Neil Shapiro **		fp -- the file pointer to close
52940266059SGregory Neil Shapiro **
53040266059SGregory Neil Shapiro **	Returns:
53140266059SGregory Neil Shapiro **		0 on success
53240266059SGregory Neil Shapiro */
53340266059SGregory Neil Shapiro 
53440266059SGregory Neil Shapiro static int tls_close __P((SM_FILE_T *));
53540266059SGregory Neil Shapiro 
53640266059SGregory Neil Shapiro static int
53740266059SGregory Neil Shapiro tls_close(fp)
53840266059SGregory Neil Shapiro 	SM_FILE_T *fp;
53940266059SGregory Neil Shapiro {
54040266059SGregory Neil Shapiro 	struct tls_obj *so;
54140266059SGregory Neil Shapiro 
54240266059SGregory Neil Shapiro 	so = (struct tls_obj *) fp->f_cookie;
543a7ec597cSGregory Neil Shapiro 	if (so == NULL)
544a7ec597cSGregory Neil Shapiro 		return 0;
54540266059SGregory Neil Shapiro 	if (so->fp != NULL)
54640266059SGregory Neil Shapiro 	{
54740266059SGregory Neil Shapiro 		sm_io_close(so->fp, SM_TIME_DEFAULT);
54840266059SGregory Neil Shapiro 		so->fp = NULL;
54940266059SGregory Neil Shapiro 	}
55040266059SGregory Neil Shapiro 	sm_free(so);
55140266059SGregory Neil Shapiro 	so = NULL;
55240266059SGregory Neil Shapiro 	return 0;
55340266059SGregory Neil Shapiro }
55440266059SGregory Neil Shapiro 
55540266059SGregory Neil Shapiro /* maximum number of retries for TLS related I/O due to handshakes */
55640266059SGregory Neil Shapiro # define MAX_TLS_IOS	4
55740266059SGregory Neil Shapiro 
55840266059SGregory Neil Shapiro /*
5594e4196cbSGregory Neil Shapiro **  TLS_RETRY -- check whether a failed SSL operation can be retried
5604e4196cbSGregory Neil Shapiro **
5614e4196cbSGregory Neil Shapiro **	Parameters:
5624e4196cbSGregory Neil Shapiro **		ssl -- TLS structure
5634e4196cbSGregory Neil Shapiro **		rfd -- read fd
5644e4196cbSGregory Neil Shapiro **		wfd -- write fd
5654e4196cbSGregory Neil Shapiro **		tlsstart -- start time of TLS operation
5664e4196cbSGregory Neil Shapiro **		timeout -- timeout for TLS operation
5674e4196cbSGregory Neil Shapiro **		err -- SSL error
5684e4196cbSGregory Neil Shapiro **		where -- description of operation
5694e4196cbSGregory Neil Shapiro **
5704e4196cbSGregory Neil Shapiro **	Results:
5714e4196cbSGregory Neil Shapiro **		>0 on success
5724e4196cbSGregory Neil Shapiro **		0 on timeout
5734e4196cbSGregory Neil Shapiro **		<0 on error
5744e4196cbSGregory Neil Shapiro */
5754e4196cbSGregory Neil Shapiro 
5764e4196cbSGregory Neil Shapiro int
5774e4196cbSGregory Neil Shapiro tls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
5784e4196cbSGregory Neil Shapiro 	SSL *ssl;
5794e4196cbSGregory Neil Shapiro 	int rfd;
5804e4196cbSGregory Neil Shapiro 	int wfd;
5814e4196cbSGregory Neil Shapiro 	time_t tlsstart;
5824e4196cbSGregory Neil Shapiro 	int timeout;
5834e4196cbSGregory Neil Shapiro 	int err;
5844e4196cbSGregory Neil Shapiro 	const char *where;
5854e4196cbSGregory Neil Shapiro {
5864e4196cbSGregory Neil Shapiro 	int ret;
5874e4196cbSGregory Neil Shapiro 	time_t left;
5884e4196cbSGregory Neil Shapiro 	time_t now = curtime();
5894e4196cbSGregory Neil Shapiro 	struct timeval tv;
5904e4196cbSGregory Neil Shapiro 
5914e4196cbSGregory Neil Shapiro 	ret = -1;
5924e4196cbSGregory Neil Shapiro 
5934e4196cbSGregory Neil Shapiro 	/*
5944e4196cbSGregory Neil Shapiro 	**  For SSL_ERROR_WANT_{READ,WRITE}:
5954e4196cbSGregory Neil Shapiro 	**  There is not a complete SSL record available yet
5964e4196cbSGregory Neil Shapiro 	**  or there is only a partial SSL record removed from
5974e4196cbSGregory Neil Shapiro 	**  the network (socket) buffer into the SSL buffer.
5984e4196cbSGregory Neil Shapiro 	**  The SSL_connect will only succeed when a full
5994e4196cbSGregory Neil Shapiro 	**  SSL record is available (assuming a "real" error
6004e4196cbSGregory Neil Shapiro 	**  doesn't happen). To handle when a "real" error
6014e4196cbSGregory Neil Shapiro 	**  does happen the select is set for exceptions too.
6024e4196cbSGregory Neil Shapiro 	**  The connection may be re-negotiated during this time
6034e4196cbSGregory Neil Shapiro 	**  so both read and write "want errors" need to be handled.
6044e4196cbSGregory Neil Shapiro 	**  A select() exception loops back so that a proper SSL
6054e4196cbSGregory Neil Shapiro 	**  error message can be gotten.
6064e4196cbSGregory Neil Shapiro 	*/
6074e4196cbSGregory Neil Shapiro 
6084e4196cbSGregory Neil Shapiro 	left = timeout - (now - tlsstart);
6094e4196cbSGregory Neil Shapiro 	if (left <= 0)
6104e4196cbSGregory Neil Shapiro 		return 0;	/* timeout */
6114e4196cbSGregory Neil Shapiro 	tv.tv_sec = left;
6124e4196cbSGregory Neil Shapiro 	tv.tv_usec = 0;
6134e4196cbSGregory Neil Shapiro 
6144e4196cbSGregory Neil Shapiro 	if (LogLevel > 14)
6154e4196cbSGregory Neil Shapiro 	{
6164e4196cbSGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID,
6174e4196cbSGregory Neil Shapiro 			  "STARTTLS=%s, info: fds=%d/%d, err=%d",
6184e4196cbSGregory Neil Shapiro 			  where, rfd, wfd, err);
6194e4196cbSGregory Neil Shapiro 	}
6204e4196cbSGregory Neil Shapiro 
6214e4196cbSGregory Neil Shapiro 	if (FD_SETSIZE > 0 &&
6224e4196cbSGregory Neil Shapiro 	    ((err == SSL_ERROR_WANT_READ && rfd >= FD_SETSIZE) ||
6234e4196cbSGregory Neil Shapiro 	     (err == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
6244e4196cbSGregory Neil Shapiro 	{
6254e4196cbSGregory Neil Shapiro 		if (LogLevel > 5)
6264e4196cbSGregory Neil Shapiro 		{
6274e4196cbSGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
6284e4196cbSGregory Neil Shapiro 				  "STARTTLS=%s, error: fd %d/%d too large",
6294e4196cbSGregory Neil Shapiro 				  where, rfd, wfd);
6304e4196cbSGregory Neil Shapiro 		if (LogLevel > 8)
631*552d4955SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, where);
6324e4196cbSGregory Neil Shapiro 		}
6334e4196cbSGregory Neil Shapiro 		errno = EINVAL;
6344e4196cbSGregory Neil Shapiro 	}
6354e4196cbSGregory Neil Shapiro 	else if (err == SSL_ERROR_WANT_READ)
6364e4196cbSGregory Neil Shapiro 	{
6374e4196cbSGregory Neil Shapiro 		fd_set ssl_maskr, ssl_maskx;
638*552d4955SGregory Neil Shapiro 		int save_errno = errno;
6394e4196cbSGregory Neil Shapiro 
6404e4196cbSGregory Neil Shapiro 		FD_ZERO(&ssl_maskr);
6414e4196cbSGregory Neil Shapiro 		FD_SET(rfd, &ssl_maskr);
6424e4196cbSGregory Neil Shapiro 		FD_ZERO(&ssl_maskx);
6434e4196cbSGregory Neil Shapiro 		FD_SET(rfd, &ssl_maskx);
6444e4196cbSGregory Neil Shapiro 		do
6454e4196cbSGregory Neil Shapiro 		{
6464e4196cbSGregory Neil Shapiro 			ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
6474e4196cbSGregory Neil Shapiro 					&tv);
6484e4196cbSGregory Neil Shapiro 		} while (ret < 0 && errno == EINTR);
6494e4196cbSGregory Neil Shapiro 		if (ret < 0 && errno > 0)
6504e4196cbSGregory Neil Shapiro 			ret = -errno;
651*552d4955SGregory Neil Shapiro 		errno = save_errno;
6524e4196cbSGregory Neil Shapiro 	}
6534e4196cbSGregory Neil Shapiro 	else if (err == SSL_ERROR_WANT_WRITE)
6544e4196cbSGregory Neil Shapiro 	{
6554e4196cbSGregory Neil Shapiro 		fd_set ssl_maskw, ssl_maskx;
656*552d4955SGregory Neil Shapiro 		int save_errno = errno;
6574e4196cbSGregory Neil Shapiro 
6584e4196cbSGregory Neil Shapiro 		FD_ZERO(&ssl_maskw);
6594e4196cbSGregory Neil Shapiro 		FD_SET(wfd, &ssl_maskw);
6604e4196cbSGregory Neil Shapiro 		FD_ZERO(&ssl_maskx);
6614e4196cbSGregory Neil Shapiro 		FD_SET(rfd, &ssl_maskx);
6624e4196cbSGregory Neil Shapiro 		do
6634e4196cbSGregory Neil Shapiro 		{
6644e4196cbSGregory Neil Shapiro 			ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
6654e4196cbSGregory Neil Shapiro 					&tv);
6664e4196cbSGregory Neil Shapiro 		} while (ret < 0 && errno == EINTR);
6674e4196cbSGregory Neil Shapiro 		if (ret < 0 && errno > 0)
6684e4196cbSGregory Neil Shapiro 			ret = -errno;
669*552d4955SGregory Neil Shapiro 		errno = save_errno;
6704e4196cbSGregory Neil Shapiro 	}
6714e4196cbSGregory Neil Shapiro 	return ret;
6724e4196cbSGregory Neil Shapiro }
6734e4196cbSGregory Neil Shapiro 
6744e4196cbSGregory Neil Shapiro /* errno to force refill() etc to stop (see IS_IO_ERROR()) */
6754e4196cbSGregory Neil Shapiro #ifdef ETIMEDOUT
6764e4196cbSGregory Neil Shapiro # define SM_ERR_TIMEOUT	ETIMEDOUT
6774e4196cbSGregory Neil Shapiro #else /* ETIMEDOUT */
6784e4196cbSGregory Neil Shapiro # define SM_ERR_TIMEOUT	EIO
6794e4196cbSGregory Neil Shapiro #endif /* ETIMEDOUT */
6804e4196cbSGregory Neil Shapiro 
6814e4196cbSGregory Neil Shapiro /*
682e3793f76SGregory Neil Shapiro **  SET_TLS_RD_TMO -- read secured information for the caller
683e3793f76SGregory Neil Shapiro **
684e3793f76SGregory Neil Shapiro **	Parameters:
685e3793f76SGregory Neil Shapiro **		rd_tmo -- read timeout
686e3793f76SGregory Neil Shapiro **
687e3793f76SGregory Neil Shapiro **	Results:
688e3793f76SGregory Neil Shapiro **		none
689e3793f76SGregory Neil Shapiro **	This is a hack: there is no way to pass it in
690e3793f76SGregory Neil Shapiro */
691e3793f76SGregory Neil Shapiro 
692e3793f76SGregory Neil Shapiro static int tls_rd_tmo = -1;
693e3793f76SGregory Neil Shapiro 
694e3793f76SGregory Neil Shapiro void
695e3793f76SGregory Neil Shapiro set_tls_rd_tmo(rd_tmo)
696e3793f76SGregory Neil Shapiro 	int rd_tmo;
697e3793f76SGregory Neil Shapiro {
698e3793f76SGregory Neil Shapiro 	tls_rd_tmo = rd_tmo;
699e3793f76SGregory Neil Shapiro }
700e3793f76SGregory Neil Shapiro 
701e3793f76SGregory Neil Shapiro /*
70240266059SGregory Neil Shapiro **  TLS_READ -- read secured information for the caller
70340266059SGregory Neil Shapiro **
70440266059SGregory Neil Shapiro **	Parameters:
70540266059SGregory Neil Shapiro **		fp -- the file pointer
70640266059SGregory Neil Shapiro **		buf -- the location to place the data
70740266059SGregory Neil Shapiro **		size -- the number of bytes to read from connection
70840266059SGregory Neil Shapiro **
70940266059SGregory Neil Shapiro **	Results:
71040266059SGregory Neil Shapiro **		-1 on error
71140266059SGregory Neil Shapiro **		otherwise the number of bytes read
71240266059SGregory Neil Shapiro */
71340266059SGregory Neil Shapiro 
71440266059SGregory Neil Shapiro static ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
71540266059SGregory Neil Shapiro 
71640266059SGregory Neil Shapiro static ssize_t
71740266059SGregory Neil Shapiro tls_read(fp, buf, size)
71840266059SGregory Neil Shapiro 	SM_FILE_T *fp;
71913058a91SGregory Neil Shapiro 	char *buf;
72040266059SGregory Neil Shapiro 	size_t size;
72106f25ae9SGregory Neil Shapiro {
7224e4196cbSGregory Neil Shapiro 	int r, rfd, wfd, try, ssl_err;
72340266059SGregory Neil Shapiro 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
7244e4196cbSGregory Neil Shapiro 	time_t tlsstart;
72506f25ae9SGregory Neil Shapiro 	char *err;
72606f25ae9SGregory Neil Shapiro 
7274e4196cbSGregory Neil Shapiro 	try = 99;
7284e4196cbSGregory Neil Shapiro 	err = NULL;
7294e4196cbSGregory Neil Shapiro 	tlsstart = curtime();
7304e4196cbSGregory Neil Shapiro 
7314e4196cbSGregory Neil Shapiro   retry:
73240266059SGregory Neil Shapiro 	r = SSL_read(so->con, (char *) buf, size);
73340266059SGregory Neil Shapiro 
73440266059SGregory Neil Shapiro 	if (r > 0)
73540266059SGregory Neil Shapiro 		return r;
73640266059SGregory Neil Shapiro 
73706f25ae9SGregory Neil Shapiro 	err = NULL;
7384e4196cbSGregory Neil Shapiro 	switch (ssl_err = SSL_get_error(so->con, r))
73906f25ae9SGregory Neil Shapiro 	{
74006f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_NONE:
74140266059SGregory Neil Shapiro 	  case SSL_ERROR_ZERO_RETURN:
74206f25ae9SGregory Neil Shapiro 		break;
74306f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_WRITE:
74440266059SGregory Neil Shapiro 		err = "read W BLOCK";
7454e4196cbSGregory Neil Shapiro 		/* FALLTHROUGH */
74606f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_READ:
7474e4196cbSGregory Neil Shapiro 		if (err == NULL)
74840266059SGregory Neil Shapiro 			err = "read R BLOCK";
7494e4196cbSGregory Neil Shapiro 		rfd = SSL_get_rfd(so->con);
7504e4196cbSGregory Neil Shapiro 		wfd = SSL_get_wfd(so->con);
7514e4196cbSGregory Neil Shapiro 		try = tls_retry(so->con, rfd, wfd, tlsstart,
752e3793f76SGregory Neil Shapiro 				(tls_rd_tmo < 0) ? TimeOuts.to_datablock
753e3793f76SGregory Neil Shapiro 						 : tls_rd_tmo,
754e3793f76SGregory Neil Shapiro 				ssl_err, "read");
7554e4196cbSGregory Neil Shapiro 		if (try > 0)
7564e4196cbSGregory Neil Shapiro 			goto retry;
7574e4196cbSGregory Neil Shapiro 		errno = SM_ERR_TIMEOUT;
75806f25ae9SGregory Neil Shapiro 		break;
7594e4196cbSGregory Neil Shapiro 
76006f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_X509_LOOKUP:
76106f25ae9SGregory Neil Shapiro 		err = "write X BLOCK";
76206f25ae9SGregory Neil Shapiro 		break;
76306f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_SYSCALL:
76440266059SGregory Neil Shapiro 		if (r == 0 && errno == 0) /* out of protocol EOF found */
76540266059SGregory Neil Shapiro 			break;
76606f25ae9SGregory Neil Shapiro 		err = "syscall error";
76706f25ae9SGregory Neil Shapiro /*
76806f25ae9SGregory Neil Shapiro 		get_last_socket_error());
76906f25ae9SGregory Neil Shapiro */
77006f25ae9SGregory Neil Shapiro 		break;
77106f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_SSL:
772b6bacd31SGregory Neil Shapiro #if DEAL_WITH_ERROR_SSL
773959366dcSGregory Neil Shapiro 		if (r == 0 && errno == 0) /* out of protocol EOF found */
774959366dcSGregory Neil Shapiro 			break;
775b6bacd31SGregory Neil Shapiro #endif /* DEAL_WITH_ERROR_SSL */
77606f25ae9SGregory Neil Shapiro 		err = "generic SSL error";
777*552d4955SGregory Neil Shapiro 
77840266059SGregory Neil Shapiro 		if (LogLevel > 9)
779*552d4955SGregory Neil Shapiro 		{
780*552d4955SGregory Neil Shapiro 			int pri;
781*552d4955SGregory Neil Shapiro 
782*552d4955SGregory Neil Shapiro 			if (errno == EAGAIN && try > 0)
783*552d4955SGregory Neil Shapiro 				pri = LOG_DEBUG;
784*552d4955SGregory Neil Shapiro 			else
785*552d4955SGregory Neil Shapiro 				pri = LOG_WARNING;
786*552d4955SGregory Neil Shapiro 			tlslogerr(pri, "read");
787*552d4955SGregory Neil Shapiro 		}
788959366dcSGregory Neil Shapiro 
789b6bacd31SGregory Neil Shapiro #if DEAL_WITH_ERROR_SSL
790959366dcSGregory Neil Shapiro 		/* avoid repeated calls? */
791959366dcSGregory Neil Shapiro 		if (r == 0)
792959366dcSGregory Neil Shapiro 			r = -1;
793b6bacd31SGregory Neil Shapiro #endif /* DEAL_WITH_ERROR_SSL */
79406f25ae9SGregory Neil Shapiro 		break;
79506f25ae9SGregory Neil Shapiro 	}
79606f25ae9SGregory Neil Shapiro 	if (err != NULL)
79740266059SGregory Neil Shapiro 	{
798605302a5SGregory Neil Shapiro 		int save_errno;
799605302a5SGregory Neil Shapiro 
800605302a5SGregory Neil Shapiro 		save_errno = (errno == 0) ? EIO : errno;
8014e4196cbSGregory Neil Shapiro 		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
8024e4196cbSGregory Neil Shapiro 		{
8034e4196cbSGregory Neil Shapiro 			if (LogLevel > 7)
804a7ec597cSGregory Neil Shapiro 				sm_syslog(LOG_WARNING, NOQID,
8054e4196cbSGregory Neil Shapiro 					  "STARTTLS: read error=timeout");
8064e4196cbSGregory Neil Shapiro 		}
8074e4196cbSGregory Neil Shapiro 		else if (LogLevel > 8)
808*552d4955SGregory Neil Shapiro 		{
809*552d4955SGregory Neil Shapiro 			int pri;
810*552d4955SGregory Neil Shapiro 
811*552d4955SGregory Neil Shapiro 			if (save_errno == EAGAIN && try > 0)
812*552d4955SGregory Neil Shapiro 				pri = LOG_DEBUG;
813*552d4955SGregory Neil Shapiro 			else
814*552d4955SGregory Neil Shapiro 				pri = LOG_WARNING;
815*552d4955SGregory Neil Shapiro 			sm_syslog(pri, NOQID,
8164e4196cbSGregory Neil Shapiro 				  "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
817a7ec597cSGregory Neil Shapiro 				  err, r, errno,
8184e4196cbSGregory Neil Shapiro 				  ERR_error_string(ERR_get_error(), NULL), try,
8194e4196cbSGregory Neil Shapiro 				  ssl_err);
820*552d4955SGregory Neil Shapiro 		}
821a7ec597cSGregory Neil Shapiro 		else if (LogLevel > 7)
82240266059SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
8234e4196cbSGregory Neil Shapiro 				  "STARTTLS: read error=%s (%d), retry=%d, ssl_err=%d",
8244e4196cbSGregory Neil Shapiro 				  err, r, errno, try, ssl_err);
825605302a5SGregory Neil Shapiro 		errno = save_errno;
82606f25ae9SGregory Neil Shapiro 	}
82706f25ae9SGregory Neil Shapiro 	return r;
82806f25ae9SGregory Neil Shapiro }
82906f25ae9SGregory Neil Shapiro 
83040266059SGregory Neil Shapiro /*
83140266059SGregory Neil Shapiro **  TLS_WRITE -- write information out through secure connection
83240266059SGregory Neil Shapiro **
83340266059SGregory Neil Shapiro **	Parameters:
83440266059SGregory Neil Shapiro **		fp -- the file pointer
83540266059SGregory Neil Shapiro **		buf -- holds the data to be securely written
83640266059SGregory Neil Shapiro **		size -- the number of bytes to write
83740266059SGregory Neil Shapiro **
83840266059SGregory Neil Shapiro **	Returns:
83940266059SGregory Neil Shapiro **		-1 on error
84040266059SGregory Neil Shapiro **		otherwise number of bytes written
84140266059SGregory Neil Shapiro */
84240266059SGregory Neil Shapiro 
84340266059SGregory Neil Shapiro static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
84440266059SGregory Neil Shapiro 
84513058a91SGregory Neil Shapiro static ssize_t
84640266059SGregory Neil Shapiro tls_write(fp, buf, size)
84740266059SGregory Neil Shapiro 	SM_FILE_T *fp;
84813058a91SGregory Neil Shapiro 	const char *buf;
84940266059SGregory Neil Shapiro 	size_t size;
85006f25ae9SGregory Neil Shapiro {
8514e4196cbSGregory Neil Shapiro 	int r, rfd, wfd, try, ssl_err;
85240266059SGregory Neil Shapiro 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
8534e4196cbSGregory Neil Shapiro 	time_t tlsstart;
85406f25ae9SGregory Neil Shapiro 	char *err;
85506f25ae9SGregory Neil Shapiro 
8564e4196cbSGregory Neil Shapiro 	try = 99;
8574e4196cbSGregory Neil Shapiro 	err = NULL;
8584e4196cbSGregory Neil Shapiro 	tlsstart = curtime();
8594e4196cbSGregory Neil Shapiro 
8604e4196cbSGregory Neil Shapiro   retry:
86140266059SGregory Neil Shapiro 	r = SSL_write(so->con, (char *) buf, size);
86240266059SGregory Neil Shapiro 
86340266059SGregory Neil Shapiro 	if (r > 0)
86440266059SGregory Neil Shapiro 		return r;
86506f25ae9SGregory Neil Shapiro 	err = NULL;
8664e4196cbSGregory Neil Shapiro 	switch (ssl_err = SSL_get_error(so->con, r))
86706f25ae9SGregory Neil Shapiro 	{
86806f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_NONE:
86940266059SGregory Neil Shapiro 	  case SSL_ERROR_ZERO_RETURN:
87006f25ae9SGregory Neil Shapiro 		break;
87106f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_WRITE:
8724e4196cbSGregory Neil Shapiro 		err = "read W BLOCK";
8734e4196cbSGregory Neil Shapiro 		/* FALLTHROUGH */
87406f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_READ:
8754e4196cbSGregory Neil Shapiro 		if (err == NULL)
8764e4196cbSGregory Neil Shapiro 			err = "read R BLOCK";
8774e4196cbSGregory Neil Shapiro 		rfd = SSL_get_rfd(so->con);
8784e4196cbSGregory Neil Shapiro 		wfd = SSL_get_wfd(so->con);
8794e4196cbSGregory Neil Shapiro 		try = tls_retry(so->con, rfd, wfd, tlsstart,
8804e4196cbSGregory Neil Shapiro 				DATA_PROGRESS_TIMEOUT, ssl_err, "write");
8814e4196cbSGregory Neil Shapiro 		if (try > 0)
8824e4196cbSGregory Neil Shapiro 			goto retry;
8834e4196cbSGregory Neil Shapiro 		errno = SM_ERR_TIMEOUT;
88406f25ae9SGregory Neil Shapiro 		break;
88506f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_WANT_X509_LOOKUP:
88606f25ae9SGregory Neil Shapiro 		err = "write X BLOCK";
88706f25ae9SGregory Neil Shapiro 		break;
88806f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_SYSCALL:
88940266059SGregory Neil Shapiro 		if (r == 0 && errno == 0) /* out of protocol EOF found */
89040266059SGregory Neil Shapiro 			break;
89106f25ae9SGregory Neil Shapiro 		err = "syscall error";
89206f25ae9SGregory Neil Shapiro /*
89306f25ae9SGregory Neil Shapiro 		get_last_socket_error());
89406f25ae9SGregory Neil Shapiro */
89506f25ae9SGregory Neil Shapiro 		break;
89606f25ae9SGregory Neil Shapiro 	  case SSL_ERROR_SSL:
89706f25ae9SGregory Neil Shapiro 		err = "generic SSL error";
89806f25ae9SGregory Neil Shapiro /*
89906f25ae9SGregory Neil Shapiro 		ERR_GET_REASON(ERR_peek_error()));
90006f25ae9SGregory Neil Shapiro */
90140266059SGregory Neil Shapiro 		if (LogLevel > 9)
902*552d4955SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, "write");
903959366dcSGregory Neil Shapiro 
904b6bacd31SGregory Neil Shapiro #if DEAL_WITH_ERROR_SSL
905959366dcSGregory Neil Shapiro 		/* avoid repeated calls? */
906959366dcSGregory Neil Shapiro 		if (r == 0)
907959366dcSGregory Neil Shapiro 			r = -1;
908b6bacd31SGregory Neil Shapiro #endif /* DEAL_WITH_ERROR_SSL */
90906f25ae9SGregory Neil Shapiro 		break;
91006f25ae9SGregory Neil Shapiro 	}
91106f25ae9SGregory Neil Shapiro 	if (err != NULL)
91240266059SGregory Neil Shapiro 	{
913605302a5SGregory Neil Shapiro 		int save_errno;
914605302a5SGregory Neil Shapiro 
915605302a5SGregory Neil Shapiro 		save_errno = (errno == 0) ? EIO : errno;
9164e4196cbSGregory Neil Shapiro 		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
9174e4196cbSGregory Neil Shapiro 		{
9184e4196cbSGregory Neil Shapiro 			if (LogLevel > 7)
919a7ec597cSGregory Neil Shapiro 				sm_syslog(LOG_WARNING, NOQID,
9204e4196cbSGregory Neil Shapiro 					  "STARTTLS: write error=timeout");
9214e4196cbSGregory Neil Shapiro 		}
9224e4196cbSGregory Neil Shapiro 		else if (LogLevel > 8)
9234e4196cbSGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
9244e4196cbSGregory Neil Shapiro 				  "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
925a7ec597cSGregory Neil Shapiro 				  err, r, errno,
9264e4196cbSGregory Neil Shapiro 				  ERR_error_string(ERR_get_error(), NULL), try,
9274e4196cbSGregory Neil Shapiro 				  ssl_err);
928a7ec597cSGregory Neil Shapiro 		else if (LogLevel > 7)
92940266059SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
9304e4196cbSGregory Neil Shapiro 				  "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
9314e4196cbSGregory Neil Shapiro 				  err, r, errno, try, ssl_err);
932605302a5SGregory Neil Shapiro 		errno = save_errno;
93306f25ae9SGregory Neil Shapiro 	}
93406f25ae9SGregory Neil Shapiro 	return r;
93506f25ae9SGregory Neil Shapiro }
93606f25ae9SGregory Neil Shapiro 
93740266059SGregory Neil Shapiro /*
93840266059SGregory Neil Shapiro **  SFDCTLS -- create tls file type and open in and out file pointers
93940266059SGregory Neil Shapiro **	      for sendmail to read from and write to.
94040266059SGregory Neil Shapiro **
94140266059SGregory Neil Shapiro **	Parameters:
94240266059SGregory Neil Shapiro **		fin -- data input source being replaced
94340266059SGregory Neil Shapiro **		fout -- data output source being replaced
944a7ec597cSGregory Neil Shapiro **		con -- the tls connection pointer
94540266059SGregory Neil Shapiro **
94640266059SGregory Neil Shapiro **	Returns:
94740266059SGregory Neil Shapiro **		-1 on error
94840266059SGregory Neil Shapiro **		0 on success
94940266059SGregory Neil Shapiro **
95040266059SGregory Neil Shapiro **	Side effects:
95140266059SGregory Neil Shapiro **		The arguments "fin" and "fout" are replaced with the new
95240266059SGregory Neil Shapiro **		SM_FILE_T pointers.
95340266059SGregory Neil Shapiro **		The original "fin" and "fout" are preserved in the tls file
95440266059SGregory Neil Shapiro **		type but are not actually used because of the design of TLS.
95540266059SGregory Neil Shapiro */
95606f25ae9SGregory Neil Shapiro 
95706f25ae9SGregory Neil Shapiro int
95806f25ae9SGregory Neil Shapiro sfdctls(fin, fout, con)
95940266059SGregory Neil Shapiro 	SM_FILE_T **fin;
96040266059SGregory Neil Shapiro 	SM_FILE_T **fout;
96106f25ae9SGregory Neil Shapiro 	SSL *con;
96206f25ae9SGregory Neil Shapiro {
96340266059SGregory Neil Shapiro 	SM_FILE_T *tlsin, *tlsout;
96440266059SGregory Neil Shapiro 	SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
96540266059SGregory Neil Shapiro 		tls_read, tls_write, NULL, tls_getinfo, NULL,
96640266059SGregory Neil Shapiro 		SM_TIME_FOREVER);
96740266059SGregory Neil Shapiro 	struct tls_info info;
96806f25ae9SGregory Neil Shapiro 
96940266059SGregory Neil Shapiro 	SM_ASSERT(con != NULL);
97006f25ae9SGregory Neil Shapiro 
97140266059SGregory Neil Shapiro 	SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
97240266059SGregory Neil Shapiro 		tls_read, tls_write, NULL, tls_getinfo, NULL,
97340266059SGregory Neil Shapiro 		SM_TIME_FOREVER);
97440266059SGregory Neil Shapiro 	info.fp = *fin;
97540266059SGregory Neil Shapiro 	info.con = con;
976e92d3f3fSGregory Neil Shapiro 	tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
97740266059SGregory Neil Shapiro 			   NULL);
97840266059SGregory Neil Shapiro 	if (tlsin == NULL)
97940266059SGregory Neil Shapiro 		return -1;
98006f25ae9SGregory Neil Shapiro 
98140266059SGregory Neil Shapiro 	info.fp = *fout;
982e92d3f3fSGregory Neil Shapiro 	tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
98340266059SGregory Neil Shapiro 			    NULL);
98440266059SGregory Neil Shapiro 	if (tlsout == NULL)
98542e5d165SGregory Neil Shapiro 	{
98640266059SGregory Neil Shapiro 		(void) sm_io_close(tlsin, SM_TIME_DEFAULT);
98742e5d165SGregory Neil Shapiro 		return -1;
98842e5d165SGregory Neil Shapiro 	}
98940266059SGregory Neil Shapiro 	sm_io_automode(tlsin, tlsout);
99006f25ae9SGregory Neil Shapiro 
99140266059SGregory Neil Shapiro 	*fin = tlsin;
99240266059SGregory Neil Shapiro 	*fout = tlsout;
99306f25ae9SGregory Neil Shapiro 	return 0;
99406f25ae9SGregory Neil Shapiro }
99540266059SGregory Neil Shapiro #endif /* STARTTLS */
996