164562Sgshapiro/*
2261194Sgshapiro * Copyright (c) 1999-2006, 2008 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12266527SgshapiroSM_RCSID("@(#)$Id: sfsasl.c,v 8.121 2013-11-22 20:51:56 ca Exp $")
1364562Sgshapiro#include <stdlib.h>
1464562Sgshapiro#include <sendmail.h>
15159609Sgshapiro#include <sm/time.h>
16285229Sgshapiro#include <sm/fdset.h>
1790792Sgshapiro#include <errno.h>
18141858Sgshapiro
19141858Sgshapiro/* allow to disable error handling code just in case... */
20141858Sgshapiro#ifndef DEAL_WITH_ERROR_SSL
21141858Sgshapiro# define DEAL_WITH_ERROR_SSL	1
22363466Sgshapiro#endif
23141858Sgshapiro
2490792Sgshapiro#if SASL
2590792Sgshapiro# include "sfsasl.h"
2664562Sgshapiro
2790792Sgshapiro/* Structure used by the "sasl" file type */
2890792Sgshapirostruct sasl_obj
2990792Sgshapiro{
3090792Sgshapiro	SM_FILE_T *fp;
3190792Sgshapiro	sasl_conn_t *conn;
3290792Sgshapiro};
3390792Sgshapiro
3490792Sgshapirostruct sasl_info
3590792Sgshapiro{
3690792Sgshapiro	SM_FILE_T *fp;
3790792Sgshapiro	sasl_conn_t *conn;
3890792Sgshapiro};
3990792Sgshapiro
4064562Sgshapiro/*
4190792Sgshapiro**  SASL_GETINFO - returns requested information about a "sasl" file
4290792Sgshapiro**		  descriptor.
4390792Sgshapiro**
4490792Sgshapiro**	Parameters:
4590792Sgshapiro**		fp -- the file descriptor
4690792Sgshapiro**		what -- the type of information requested
4790792Sgshapiro**		valp -- the thang to return the information in
4890792Sgshapiro**
4990792Sgshapiro**	Returns:
5090792Sgshapiro**		-1 for unknown requests
5190792Sgshapiro**		>=0 on success with valp filled in (if possible).
5264562Sgshapiro*/
5364562Sgshapiro
5490792Sgshapirostatic int sasl_getinfo __P((SM_FILE_T *, int, void *));
5564562Sgshapiro
5690792Sgshapirostatic int
5790792Sgshapirosasl_getinfo(fp, what, valp)
5890792Sgshapiro	SM_FILE_T *fp;
5990792Sgshapiro	int what;
6090792Sgshapiro	void *valp;
6190792Sgshapiro{
6290792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
6390792Sgshapiro
6490792Sgshapiro	switch (what)
6590792Sgshapiro	{
6690792Sgshapiro	  case SM_IO_WHAT_FD:
6790792Sgshapiro		if (so->fp == NULL)
6890792Sgshapiro			return -1;
69363466Sgshapiro		return so->fp->f_file; /* for stdio fileno() compatibility */
7090792Sgshapiro
7190792Sgshapiro	  case SM_IO_IS_READABLE:
7290792Sgshapiro		if (so->fp == NULL)
7390792Sgshapiro			return 0;
7490792Sgshapiro
7590792Sgshapiro		/* get info from underlying file */
7690792Sgshapiro		return sm_io_getinfo(so->fp, what, valp);
7790792Sgshapiro
7890792Sgshapiro	  default:
7990792Sgshapiro		return -1;
8090792Sgshapiro	}
8190792Sgshapiro}
8290792Sgshapiro
8390792Sgshapiro/*
8490792Sgshapiro**  SASL_OPEN -- creates the sasl specific information for opening a
8590792Sgshapiro**		file of the sasl type.
8690792Sgshapiro**
8790792Sgshapiro**	Parameters:
8890792Sgshapiro**		fp -- the file pointer associated with the new open
8990792Sgshapiro**		info -- contains the sasl connection information pointer and
9090792Sgshapiro**			the original SM_FILE_T that holds the open
9190792Sgshapiro**		flags -- ignored
9290792Sgshapiro**		rpool -- ignored
9390792Sgshapiro**
9490792Sgshapiro**	Returns:
9590792Sgshapiro**		0 on success
9690792Sgshapiro*/
9790792Sgshapiro
9890792Sgshapirostatic int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
9990792Sgshapiro
10090792Sgshapiro/* ARGSUSED2 */
10190792Sgshapirostatic int
10290792Sgshapirosasl_open(fp, info, flags, rpool)
10390792Sgshapiro	SM_FILE_T *fp;
10490792Sgshapiro	const void *info;
10590792Sgshapiro	int flags;
10690792Sgshapiro	const void *rpool;
10790792Sgshapiro{
10890792Sgshapiro	struct sasl_obj *so;
10990792Sgshapiro	struct sasl_info *si = (struct sasl_info *) info;
11090792Sgshapiro
11190792Sgshapiro	so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
112120256Sgshapiro	if (so == NULL)
113120256Sgshapiro	{
114120256Sgshapiro		errno = ENOMEM;
115120256Sgshapiro		return -1;
116120256Sgshapiro	}
11790792Sgshapiro	so->fp = si->fp;
11890792Sgshapiro	so->conn = si->conn;
11990792Sgshapiro
12090792Sgshapiro	/*
12190792Sgshapiro	**  The underlying 'fp' is set to SM_IO_NOW so that the entire
12290792Sgshapiro	**  encoded string is written in one chunk. Otherwise there is
12390792Sgshapiro	**  the possibility that it may appear illegal, bogus or
12490792Sgshapiro	**  mangled to the other side of the connection.
12590792Sgshapiro	**  We will read or write through 'fp' since it is the opaque
12690792Sgshapiro	**  connection for the communications. We need to treat it this
12790792Sgshapiro	**  way in case the encoded string is to be sent down a TLS
12890792Sgshapiro	**  connection rather than, say, sm_io's stdio.
12990792Sgshapiro	*/
13090792Sgshapiro
13190792Sgshapiro	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
13290792Sgshapiro	fp->f_cookie = so;
13390792Sgshapiro	return 0;
13490792Sgshapiro}
13590792Sgshapiro
13690792Sgshapiro/*
13790792Sgshapiro**  SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
13890792Sgshapiro**
13990792Sgshapiro**	Parameters:
14090792Sgshapiro**		fp -- the file pointer to close
14190792Sgshapiro**
14290792Sgshapiro**	Returns:
14390792Sgshapiro**		0 on success
14490792Sgshapiro*/
14590792Sgshapiro
14690792Sgshapirostatic int sasl_close __P((SM_FILE_T *));
14790792Sgshapiro
14890792Sgshapirostatic int
14990792Sgshapirosasl_close(fp)
15090792Sgshapiro	SM_FILE_T *fp;
15190792Sgshapiro{
15290792Sgshapiro	struct sasl_obj *so;
15390792Sgshapiro
15490792Sgshapiro	so = (struct sasl_obj *) fp->f_cookie;
155120256Sgshapiro	if (so == NULL)
156120256Sgshapiro		return 0;
15790792Sgshapiro	if (so->fp != NULL)
15890792Sgshapiro	{
15990792Sgshapiro		sm_io_close(so->fp, SM_TIME_DEFAULT);
16090792Sgshapiro		so->fp = NULL;
16190792Sgshapiro	}
16290792Sgshapiro	sm_free(so);
16390792Sgshapiro	so = NULL;
16490792Sgshapiro	return 0;
16590792Sgshapiro}
16690792Sgshapiro
16771345Sgshapiro/* how to deallocate a buffer allocated by SASL */
16890792Sgshapiroextern void	sm_sasl_free __P((void *));
16998841Sgshapiro#  define SASL_DEALLOC(b)	sm_sasl_free(b)
17071345Sgshapiro
17190792Sgshapiro/*
17290792Sgshapiro**  SASL_READ -- read encrypted information and decrypt it for the caller
17390792Sgshapiro**
17490792Sgshapiro**	Parameters:
17590792Sgshapiro**		fp -- the file pointer
17690792Sgshapiro**		buf -- the location to place the decrypted information
17790792Sgshapiro**		size -- the number of bytes to read after decryption
17890792Sgshapiro**
17990792Sgshapiro**	Results:
18090792Sgshapiro**		-1 on error
18190792Sgshapiro**		otherwise the number of bytes read
18290792Sgshapiro*/
18390792Sgshapiro
18490792Sgshapirostatic ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
18590792Sgshapiro
18664562Sgshapirostatic ssize_t
18790792Sgshapirosasl_read(fp, buf, size)
18890792Sgshapiro	SM_FILE_T *fp;
18990792Sgshapiro	char *buf;
19064562Sgshapiro	size_t size;
19164562Sgshapiro{
19290792Sgshapiro	int result;
19390792Sgshapiro	ssize_t len;
19498121Sgshapiro# if SASL >= 20000
195110560Sgshapiro	static const char *outbuf = NULL;
196363466Sgshapiro# else
19771345Sgshapiro	static char *outbuf = NULL;
198363466Sgshapiro# endif
19971345Sgshapiro	static unsigned int outlen = 0;
20071345Sgshapiro	static unsigned int offset = 0;
20190792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
20264562Sgshapiro
20371345Sgshapiro	/*
20471345Sgshapiro	**  sasl_decode() may require more data than a single read() returns.
20571345Sgshapiro	**  Hence we have to put a loop around the decoding.
20671345Sgshapiro	**  This also requires that we may have to split up the returned
20771345Sgshapiro	**  data since it might be larger than the allowed size.
20871345Sgshapiro	**  Therefore we use a static pointer and return portions of it
20971345Sgshapiro	**  if necessary.
210120256Sgshapiro	**  XXX Note: This function is not thread-safe nor can it be used
211120256Sgshapiro	**  on more than one file. A correct implementation would store
212120256Sgshapiro	**  this data in fp->f_cookie.
21371345Sgshapiro	*/
21464562Sgshapiro
215110560Sgshapiro# if SASL >= 20000
216110560Sgshapiro	while (outlen == 0)
217363466Sgshapiro# else
21871345Sgshapiro	while (outbuf == NULL && outlen == 0)
219363466Sgshapiro# endif
22064562Sgshapiro	{
22190792Sgshapiro		len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
22271345Sgshapiro		if (len <= 0)
22371345Sgshapiro			return len;
22490792Sgshapiro		result = sasl_decode(so->conn, buf,
22590792Sgshapiro				     (unsigned int) len, &outbuf, &outlen);
22671345Sgshapiro		if (result != SASL_OK)
22771345Sgshapiro		{
228157001Sgshapiro			if (LogLevel > 7)
229157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
230157001Sgshapiro					"AUTH: sasl_decode error=%d", result);
23171345Sgshapiro			outbuf = NULL;
23271345Sgshapiro			offset = 0;
23371345Sgshapiro			outlen = 0;
23471345Sgshapiro			return -1;
23571345Sgshapiro		}
23664562Sgshapiro	}
23764562Sgshapiro
23890792Sgshapiro	if (outbuf == NULL)
23964562Sgshapiro	{
24090792Sgshapiro		/* be paranoid: outbuf == NULL but outlen != 0 */
24190792Sgshapiro		syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
24290792Sgshapiro		/* NOTREACHED */
24364562Sgshapiro	}
24490792Sgshapiro	if (outlen - offset > size)
24590792Sgshapiro	{
24690792Sgshapiro		/* return another part of the buffer */
24790792Sgshapiro		(void) memcpy(buf, outbuf + offset, size);
24890792Sgshapiro		offset += size;
24990792Sgshapiro		len = size;
25090792Sgshapiro	}
25171345Sgshapiro	else
25271345Sgshapiro	{
25390792Sgshapiro		/* return the rest of the buffer */
25490792Sgshapiro		len = outlen - offset;
25590792Sgshapiro		(void) memcpy(buf, outbuf + offset, (size_t) len);
25698121Sgshapiro# if SASL < 20000
25790792Sgshapiro		SASL_DEALLOC(outbuf);
258363466Sgshapiro# endif
25990792Sgshapiro		outbuf = NULL;
26090792Sgshapiro		offset = 0;
26190792Sgshapiro		outlen = 0;
26271345Sgshapiro	}
26390792Sgshapiro	return len;
26464562Sgshapiro}
26564562Sgshapiro
26690792Sgshapiro/*
26790792Sgshapiro**  SASL_WRITE -- write information out after encrypting it
26890792Sgshapiro**
26990792Sgshapiro**	Parameters:
27090792Sgshapiro**		fp -- the file pointer
27190792Sgshapiro**		buf -- holds the data to be encrypted and written
27290792Sgshapiro**		size -- the number of bytes to have encrypted and written
27390792Sgshapiro**
27490792Sgshapiro**	Returns:
27590792Sgshapiro**		-1 on error
27690792Sgshapiro**		otherwise number of bytes written
27790792Sgshapiro*/
27890792Sgshapiro
27990792Sgshapirostatic ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
28090792Sgshapiro
28164562Sgshapirostatic ssize_t
28290792Sgshapirosasl_write(fp, buf, size)
28390792Sgshapiro	SM_FILE_T *fp;
28490792Sgshapiro	const char *buf;
28564562Sgshapiro	size_t size;
28664562Sgshapiro{
28764562Sgshapiro	int result;
28898121Sgshapiro# if SASL >= 20000
28998121Sgshapiro	const char *outbuf;
290363466Sgshapiro# else
29164562Sgshapiro	char *outbuf;
292363466Sgshapiro# endif
293141858Sgshapiro	unsigned int outlen, *maxencode;
29490792Sgshapiro	size_t ret = 0, total = 0;
29590792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
29664562Sgshapiro
297141858Sgshapiro	/*
298141858Sgshapiro	**  Fetch the maximum input buffer size for sasl_encode().
299141858Sgshapiro	**  This can be less than the size set in attemptauth()
300203004Sgshapiro	**  due to a negotiation with the other side, e.g.,
301141858Sgshapiro	**  Cyrus IMAP lmtp program sets maxbuf=4096,
302363466Sgshapiro	**  digestmd5 subtracts 25 and hence we'll get 4071
303141858Sgshapiro	**  instead of 8192 (MAXOUTLEN).
304141858Sgshapiro	**  Hack (for now): simply reduce the size, callers are (must be)
305141858Sgshapiro	**  able to deal with that and invoke sasl_write() again with
306141858Sgshapiro	**  the rest of the data.
307141858Sgshapiro	**  Note: it would be better to store this value in the context
308141858Sgshapiro	**  after the negotiation.
309141858Sgshapiro	*/
310141858Sgshapiro
311141858Sgshapiro	result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
312157001Sgshapiro				(const void **) &maxencode);
313141858Sgshapiro	if (result == SASL_OK && size > *maxencode && *maxencode > 0)
314141858Sgshapiro		size = *maxencode;
315141858Sgshapiro
31690792Sgshapiro	result = sasl_encode(so->conn, buf,
31790792Sgshapiro			     (unsigned int) size, &outbuf, &outlen);
31864562Sgshapiro
31964562Sgshapiro	if (result != SASL_OK)
320157001Sgshapiro	{
321157001Sgshapiro		if (LogLevel > 7)
322157001Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
323157001Sgshapiro				"AUTH: sasl_encode error=%d", result);
32464562Sgshapiro		return -1;
325157001Sgshapiro	}
32664562Sgshapiro
32764562Sgshapiro	if (outbuf != NULL)
32864562Sgshapiro	{
32990792Sgshapiro		while (outlen > 0)
33090792Sgshapiro		{
331159609Sgshapiro			errno = 0;
33294334Sgshapiro			/* XXX result == 0? */
33390792Sgshapiro			ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
33490792Sgshapiro					  &outbuf[total], outlen);
335120256Sgshapiro			if (ret <= 0)
336120256Sgshapiro				return ret;
33790792Sgshapiro			outlen -= ret;
33890792Sgshapiro			total += ret;
33990792Sgshapiro		}
34098121Sgshapiro# if SASL < 20000
34171345Sgshapiro		SASL_DEALLOC(outbuf);
342363466Sgshapiro# endif
34364562Sgshapiro	}
34464562Sgshapiro	return size;
34564562Sgshapiro}
34664562Sgshapiro
34790792Sgshapiro/*
34890792Sgshapiro**  SFDCSASL -- create sasl file type and open in and out file pointers
34990792Sgshapiro**	       for sendmail to read from and write to.
35090792Sgshapiro**
35190792Sgshapiro**	Parameters:
35290792Sgshapiro**		fin -- the sm_io file encrypted data to be read from
353159609Sgshapiro**		fout -- the sm_io file encrypted data to be written to
35490792Sgshapiro**		conn -- the sasl connection pointer
355159609Sgshapiro**		tmo -- timeout
35690792Sgshapiro**
35790792Sgshapiro**	Returns:
35890792Sgshapiro**		-1 on error
35990792Sgshapiro**		0 on success
36090792Sgshapiro**
36190792Sgshapiro**	Side effects:
36290792Sgshapiro**		The arguments "fin" and "fout" are replaced with the new
36390792Sgshapiro**		SM_FILE_T pointers.
36490792Sgshapiro*/
36590792Sgshapiro
36664562Sgshapiroint
367159609Sgshapirosfdcsasl(fin, fout, conn, tmo)
36890792Sgshapiro	SM_FILE_T **fin;
36990792Sgshapiro	SM_FILE_T **fout;
37064562Sgshapiro	sasl_conn_t *conn;
371159609Sgshapiro	int tmo;
37264562Sgshapiro{
37390792Sgshapiro	SM_FILE_T *newin, *newout;
37490792Sgshapiro	SM_FILE_T  SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
37590792Sgshapiro		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
376159609Sgshapiro		SM_TIME_DEFAULT);
37790792Sgshapiro	struct sasl_info info;
37864562Sgshapiro
37964562Sgshapiro	if (conn == NULL)
38064562Sgshapiro	{
38164562Sgshapiro		/* no need to do anything */
38264562Sgshapiro		return 0;
38364562Sgshapiro	}
38464562Sgshapiro
38590792Sgshapiro	SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
38690792Sgshapiro		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
387159609Sgshapiro		SM_TIME_DEFAULT);
38890792Sgshapiro	info.fp = *fin;
38990792Sgshapiro	info.conn = conn;
390132943Sgshapiro	newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
391132943Sgshapiro			SM_IO_RDONLY_B, NULL);
39264562Sgshapiro
39390792Sgshapiro	if (newin == NULL)
39490792Sgshapiro		return -1;
39564562Sgshapiro
39690792Sgshapiro	info.fp = *fout;
39790792Sgshapiro	info.conn = conn;
398132943Sgshapiro	newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
399132943Sgshapiro			SM_IO_WRONLY_B, NULL);
40064562Sgshapiro
40190792Sgshapiro	if (newout == NULL)
40264562Sgshapiro	{
40390792Sgshapiro		(void) sm_io_close(newin, SM_TIME_DEFAULT);
40464562Sgshapiro		return -1;
40564562Sgshapiro	}
40690792Sgshapiro	sm_io_automode(newin, newout);
40790792Sgshapiro
408159609Sgshapiro	sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
409159609Sgshapiro	sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
410159609Sgshapiro
41190792Sgshapiro	*fin = newin;
41290792Sgshapiro	*fout = newout;
41364562Sgshapiro	return 0;
41464562Sgshapiro}
41590792Sgshapiro#endif /* SASL */
41664562Sgshapiro
41790792Sgshapiro#if STARTTLS
41890792Sgshapiro# include "sfsasl.h"
419363466Sgshapiro# include <tls.h>
420285229Sgshapiro# include <openssl/err.h>
42190792Sgshapiro
42290792Sgshapiro/* Structure used by the "tls" file type */
42390792Sgshapirostruct tls_obj
42490792Sgshapiro{
42590792Sgshapiro	SM_FILE_T *fp;
42690792Sgshapiro	SSL *con;
42790792Sgshapiro};
42890792Sgshapiro
42990792Sgshapirostruct tls_info
43090792Sgshapiro{
43190792Sgshapiro	SM_FILE_T *fp;
43290792Sgshapiro	SSL *con;
43390792Sgshapiro};
43490792Sgshapiro
43564562Sgshapiro/*
43690792Sgshapiro**  TLS_GETINFO - returns requested information about a "tls" file
43790792Sgshapiro**		 descriptor.
43890792Sgshapiro**
43990792Sgshapiro**	Parameters:
44090792Sgshapiro**		fp -- the file descriptor
44190792Sgshapiro**		what -- the type of information requested
44290792Sgshapiro**		valp -- the thang to return the information in (unused)
44390792Sgshapiro**
44490792Sgshapiro**	Returns:
44590792Sgshapiro**		-1 for unknown requests
44690792Sgshapiro**		>=0 on success with valp filled in (if possible).
44764562Sgshapiro*/
44864562Sgshapiro
44990792Sgshapirostatic int tls_getinfo __P((SM_FILE_T *, int, void *));
45064562Sgshapiro
45190792Sgshapiro/* ARGSUSED2 */
45280785Sgshapirostatic int
45390792Sgshapirotls_getinfo(fp, what, valp)
45490792Sgshapiro	SM_FILE_T *fp;
45590792Sgshapiro	int what;
45690792Sgshapiro	void *valp;
45764562Sgshapiro{
45890792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
45964562Sgshapiro
46090792Sgshapiro	switch (what)
46164562Sgshapiro	{
46290792Sgshapiro	  case SM_IO_WHAT_FD:
46390792Sgshapiro		if (so->fp == NULL)
46490792Sgshapiro			return -1;
465363466Sgshapiro		return so->fp->f_file; /* for stdio fileno() compatibility */
46664562Sgshapiro
46790792Sgshapiro	  case SM_IO_IS_READABLE:
46890792Sgshapiro		return SSL_pending(so->con) > 0;
46990792Sgshapiro
47090792Sgshapiro	  default:
47190792Sgshapiro		return -1;
47290792Sgshapiro	}
47390792Sgshapiro}
47490792Sgshapiro
47564562Sgshapiro/*
47690792Sgshapiro**  TLS_OPEN -- creates the tls specific information for opening a
47790792Sgshapiro**	       file of the tls type.
47890792Sgshapiro**
47990792Sgshapiro**	Parameters:
48090792Sgshapiro**		fp -- the file pointer associated with the new open
48190792Sgshapiro**		info -- the sm_io file pointer holding the open and the
48290792Sgshapiro**			TLS encryption connection to be read from or written to
48390792Sgshapiro**		flags -- ignored
48490792Sgshapiro**		rpool -- ignored
48590792Sgshapiro**
48690792Sgshapiro**	Returns:
48790792Sgshapiro**		0 on success
48864562Sgshapiro*/
48990792Sgshapiro
49090792Sgshapirostatic int tls_open __P((SM_FILE_T *, const void *, int, const void *));
49190792Sgshapiro
49290792Sgshapiro/* ARGSUSED2 */
49390792Sgshapirostatic int
49490792Sgshapirotls_open(fp, info, flags, rpool)
49590792Sgshapiro	SM_FILE_T *fp;
49690792Sgshapiro	const void *info;
49790792Sgshapiro	int flags;
49890792Sgshapiro	const void *rpool;
49990792Sgshapiro{
50090792Sgshapiro	struct tls_obj *so;
50190792Sgshapiro	struct tls_info *ti = (struct tls_info *) info;
50290792Sgshapiro
50390792Sgshapiro	so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
504120256Sgshapiro	if (so == NULL)
505120256Sgshapiro	{
506120256Sgshapiro		errno = ENOMEM;
507120256Sgshapiro		return -1;
508120256Sgshapiro	}
50990792Sgshapiro	so->fp = ti->fp;
51090792Sgshapiro	so->con = ti->con;
51190792Sgshapiro
51290792Sgshapiro	/*
51390792Sgshapiro	**  We try to get the "raw" file descriptor that TLS uses to
51490792Sgshapiro	**  do the actual read/write with. This is to allow us control
51590792Sgshapiro	**  over the file descriptor being a blocking or non-blocking type.
51690792Sgshapiro	**  Under the covers TLS handles the change and this allows us
51790792Sgshapiro	**  to do timeouts with sm_io.
51890792Sgshapiro	*/
51990792Sgshapiro
52090792Sgshapiro	fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
52190792Sgshapiro	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
52290792Sgshapiro	fp->f_cookie = so;
52390792Sgshapiro	return 0;
52490792Sgshapiro}
52590792Sgshapiro
52690792Sgshapiro/*
52790792Sgshapiro**  TLS_CLOSE -- close the tls specific parts of the tls file pointer
52890792Sgshapiro**
52990792Sgshapiro**	Parameters:
53090792Sgshapiro**		fp -- the file pointer to close
53190792Sgshapiro**
53290792Sgshapiro**	Returns:
53390792Sgshapiro**		0 on success
53490792Sgshapiro*/
53590792Sgshapiro
53690792Sgshapirostatic int tls_close __P((SM_FILE_T *));
53790792Sgshapiro
53890792Sgshapirostatic int
53990792Sgshapirotls_close(fp)
54090792Sgshapiro	SM_FILE_T *fp;
54190792Sgshapiro{
54290792Sgshapiro	struct tls_obj *so;
54390792Sgshapiro
54490792Sgshapiro	so = (struct tls_obj *) fp->f_cookie;
545120256Sgshapiro	if (so == NULL)
546120256Sgshapiro		return 0;
54790792Sgshapiro	if (so->fp != NULL)
54890792Sgshapiro	{
54990792Sgshapiro		sm_io_close(so->fp, SM_TIME_DEFAULT);
55090792Sgshapiro		so->fp = NULL;
55164562Sgshapiro	}
55290792Sgshapiro	sm_free(so);
55390792Sgshapiro	so = NULL;
55490792Sgshapiro	return 0;
55564562Sgshapiro}
55664562Sgshapiro
55790792Sgshapiro/* maximum number of retries for TLS related I/O due to handshakes */
55890792Sgshapiro# define MAX_TLS_IOS	4
55990792Sgshapiro
56090792Sgshapiro/*
561157001Sgshapiro**  TLS_RETRY -- check whether a failed SSL operation can be retried
562157001Sgshapiro**
563157001Sgshapiro**	Parameters:
564157001Sgshapiro**		ssl -- TLS structure
565157001Sgshapiro**		rfd -- read fd
566157001Sgshapiro**		wfd -- write fd
567157001Sgshapiro**		tlsstart -- start time of TLS operation
568157001Sgshapiro**		timeout -- timeout for TLS operation
569157001Sgshapiro**		err -- SSL error
570157001Sgshapiro**		where -- description of operation
571157001Sgshapiro**
572157001Sgshapiro**	Results:
573157001Sgshapiro**		>0 on success
574157001Sgshapiro**		0 on timeout
575157001Sgshapiro**		<0 on error
576157001Sgshapiro*/
577157001Sgshapiro
578157001Sgshapiroint
579157001Sgshapirotls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
580157001Sgshapiro	SSL *ssl;
581157001Sgshapiro	int rfd;
582157001Sgshapiro	int wfd;
583157001Sgshapiro	time_t tlsstart;
584157001Sgshapiro	int timeout;
585157001Sgshapiro	int err;
586157001Sgshapiro	const char *where;
587157001Sgshapiro{
588157001Sgshapiro	int ret;
589157001Sgshapiro	time_t left;
590157001Sgshapiro	time_t now = curtime();
591157001Sgshapiro	struct timeval tv;
592157001Sgshapiro
593157001Sgshapiro	ret = -1;
594157001Sgshapiro
595157001Sgshapiro	/*
596157001Sgshapiro	**  For SSL_ERROR_WANT_{READ,WRITE}:
597157001Sgshapiro	**  There is not a complete SSL record available yet
598157001Sgshapiro	**  or there is only a partial SSL record removed from
599157001Sgshapiro	**  the network (socket) buffer into the SSL buffer.
600157001Sgshapiro	**  The SSL_connect will only succeed when a full
601157001Sgshapiro	**  SSL record is available (assuming a "real" error
602157001Sgshapiro	**  doesn't happen). To handle when a "real" error
603157001Sgshapiro	**  does happen the select is set for exceptions too.
604157001Sgshapiro	**  The connection may be re-negotiated during this time
605157001Sgshapiro	**  so both read and write "want errors" need to be handled.
606157001Sgshapiro	**  A select() exception loops back so that a proper SSL
607157001Sgshapiro	**  error message can be gotten.
608157001Sgshapiro	*/
609157001Sgshapiro
610157001Sgshapiro	left = timeout - (now - tlsstart);
611157001Sgshapiro	if (left <= 0)
612157001Sgshapiro		return 0;	/* timeout */
613157001Sgshapiro	tv.tv_sec = left;
614157001Sgshapiro	tv.tv_usec = 0;
615157001Sgshapiro
616157001Sgshapiro	if (LogLevel > 14)
617157001Sgshapiro	{
618157001Sgshapiro		sm_syslog(LOG_INFO, NOQID,
619157001Sgshapiro			  "STARTTLS=%s, info: fds=%d/%d, err=%d",
620157001Sgshapiro			  where, rfd, wfd, err);
621157001Sgshapiro	}
622157001Sgshapiro
623285229Sgshapiro	if ((err == SSL_ERROR_WANT_READ && !SM_FD_OK_SELECT(rfd)) ||
624285229Sgshapiro	    (err == SSL_ERROR_WANT_WRITE && !SM_FD_OK_SELECT(wfd)))
625157001Sgshapiro	{
626157001Sgshapiro		if (LogLevel > 5)
627157001Sgshapiro		{
628157001Sgshapiro			sm_syslog(LOG_ERR, NOQID,
629157001Sgshapiro				  "STARTTLS=%s, error: fd %d/%d too large",
630157001Sgshapiro				  where, rfd, wfd);
631363466Sgshapiro			tlslogerr(LOG_WARNING, 8, where);
632157001Sgshapiro		}
633157001Sgshapiro		errno = EINVAL;
634157001Sgshapiro	}
635157001Sgshapiro	else if (err == SSL_ERROR_WANT_READ)
636157001Sgshapiro	{
637157001Sgshapiro		fd_set ssl_maskr, ssl_maskx;
638249729Sgshapiro		int save_errno = errno;
639157001Sgshapiro
640157001Sgshapiro		FD_ZERO(&ssl_maskr);
641157001Sgshapiro		FD_SET(rfd, &ssl_maskr);
642157001Sgshapiro		FD_ZERO(&ssl_maskx);
643157001Sgshapiro		FD_SET(rfd, &ssl_maskx);
644157001Sgshapiro		do
645157001Sgshapiro		{
646157001Sgshapiro			ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
647157001Sgshapiro					&tv);
648157001Sgshapiro		} while (ret < 0 && errno == EINTR);
649157001Sgshapiro		if (ret < 0 && errno > 0)
650157001Sgshapiro			ret = -errno;
651249729Sgshapiro		errno = save_errno;
652157001Sgshapiro	}
653157001Sgshapiro	else if (err == SSL_ERROR_WANT_WRITE)
654157001Sgshapiro	{
655157001Sgshapiro		fd_set ssl_maskw, ssl_maskx;
656249729Sgshapiro		int save_errno = errno;
657157001Sgshapiro
658157001Sgshapiro		FD_ZERO(&ssl_maskw);
659157001Sgshapiro		FD_SET(wfd, &ssl_maskw);
660157001Sgshapiro		FD_ZERO(&ssl_maskx);
661157001Sgshapiro		FD_SET(rfd, &ssl_maskx);
662157001Sgshapiro		do
663157001Sgshapiro		{
664157001Sgshapiro			ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
665157001Sgshapiro					&tv);
666157001Sgshapiro		} while (ret < 0 && errno == EINTR);
667157001Sgshapiro		if (ret < 0 && errno > 0)
668157001Sgshapiro			ret = -errno;
669249729Sgshapiro		errno = save_errno;
670157001Sgshapiro	}
671157001Sgshapiro	return ret;
672157001Sgshapiro}
673157001Sgshapiro
674157001Sgshapiro/* errno to force refill() etc to stop (see IS_IO_ERROR()) */
675157001Sgshapiro#ifdef ETIMEDOUT
676157001Sgshapiro# define SM_ERR_TIMEOUT	ETIMEDOUT
677363466Sgshapiro#else
678157001Sgshapiro# define SM_ERR_TIMEOUT	EIO
679363466Sgshapiro#endif
680157001Sgshapiro
681157001Sgshapiro/*
682182352Sgshapiro**  SET_TLS_RD_TMO -- read secured information for the caller
683182352Sgshapiro**
684182352Sgshapiro**	Parameters:
685182352Sgshapiro**		rd_tmo -- read timeout
686182352Sgshapiro**
687182352Sgshapiro**	Results:
688285229Sgshapiro**		previous read timeout
689182352Sgshapiro**	This is a hack: there is no way to pass it in
690182352Sgshapiro*/
691182352Sgshapiro
692182352Sgshapirostatic int tls_rd_tmo = -1;
693182352Sgshapiro
694285229Sgshapiroint
695182352Sgshapiroset_tls_rd_tmo(rd_tmo)
696182352Sgshapiro	int rd_tmo;
697182352Sgshapiro{
698285229Sgshapiro	int old_rd_tmo;
699285229Sgshapiro
700285229Sgshapiro	old_rd_tmo = tls_rd_tmo;
701182352Sgshapiro	tls_rd_tmo = rd_tmo;
702285229Sgshapiro	return old_rd_tmo;
703182352Sgshapiro}
704182352Sgshapiro
705182352Sgshapiro/*
70690792Sgshapiro**  TLS_READ -- read secured information for the caller
70790792Sgshapiro**
70890792Sgshapiro**	Parameters:
70990792Sgshapiro**		fp -- the file pointer
71090792Sgshapiro**		buf -- the location to place the data
71190792Sgshapiro**		size -- the number of bytes to read from connection
71290792Sgshapiro**
71390792Sgshapiro**	Results:
71490792Sgshapiro**		-1 on error
71590792Sgshapiro**		otherwise the number of bytes read
71690792Sgshapiro*/
71790792Sgshapiro
71890792Sgshapirostatic ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
71990792Sgshapiro
72064562Sgshapirostatic ssize_t
72190792Sgshapirotls_read(fp, buf, size)
72290792Sgshapiro	SM_FILE_T *fp;
72390792Sgshapiro	char *buf;
72464562Sgshapiro	size_t size;
72564562Sgshapiro{
726157001Sgshapiro	int r, rfd, wfd, try, ssl_err;
72790792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
728157001Sgshapiro	time_t tlsstart;
72990792Sgshapiro	char *err;
73064562Sgshapiro
731157001Sgshapiro	try = 99;
732157001Sgshapiro	err = NULL;
733157001Sgshapiro	tlsstart = curtime();
734157001Sgshapiro
735157001Sgshapiro  retry:
73690792Sgshapiro	r = SSL_read(so->con, (char *) buf, size);
73764562Sgshapiro
73890792Sgshapiro	if (r > 0)
73990792Sgshapiro		return r;
74064562Sgshapiro
74190792Sgshapiro	err = NULL;
742157001Sgshapiro	switch (ssl_err = SSL_get_error(so->con, r))
74390792Sgshapiro	{
74490792Sgshapiro	  case SSL_ERROR_NONE:
74590792Sgshapiro	  case SSL_ERROR_ZERO_RETURN:
74690792Sgshapiro		break;
74790792Sgshapiro	  case SSL_ERROR_WANT_WRITE:
748157001Sgshapiro		err = "read W BLOCK";
749157001Sgshapiro		/* FALLTHROUGH */
75090792Sgshapiro	  case SSL_ERROR_WANT_READ:
751157001Sgshapiro		if (err == NULL)
75290792Sgshapiro			err = "read R BLOCK";
753157001Sgshapiro		rfd = SSL_get_rfd(so->con);
754157001Sgshapiro		wfd = SSL_get_wfd(so->con);
755157001Sgshapiro		try = tls_retry(so->con, rfd, wfd, tlsstart,
756182352Sgshapiro				(tls_rd_tmo < 0) ? TimeOuts.to_datablock
757182352Sgshapiro						 : tls_rd_tmo,
758182352Sgshapiro				ssl_err, "read");
759157001Sgshapiro		if (try > 0)
760157001Sgshapiro			goto retry;
761157001Sgshapiro		errno = SM_ERR_TIMEOUT;
76290792Sgshapiro		break;
763157001Sgshapiro
76490792Sgshapiro	  case SSL_ERROR_WANT_X509_LOOKUP:
76590792Sgshapiro		err = "write X BLOCK";
76690792Sgshapiro		break;
76790792Sgshapiro	  case SSL_ERROR_SYSCALL:
76890792Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
76990792Sgshapiro			break;
77090792Sgshapiro		err = "syscall error";
77190792Sgshapiro		break;
77290792Sgshapiro	  case SSL_ERROR_SSL:
773141858Sgshapiro#if DEAL_WITH_ERROR_SSL
774102528Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
775102528Sgshapiro			break;
776363466Sgshapiro#endif
77790792Sgshapiro		err = "generic SSL error";
778249729Sgshapiro
77990792Sgshapiro		if (LogLevel > 9)
780249729Sgshapiro		{
781249729Sgshapiro			int pri;
782102528Sgshapiro
783249729Sgshapiro			if (errno == EAGAIN && try > 0)
784249729Sgshapiro				pri = LOG_DEBUG;
785249729Sgshapiro			else
786249729Sgshapiro				pri = LOG_WARNING;
787363466Sgshapiro			tlslogerr(pri, 9, "read");
788249729Sgshapiro		}
789249729Sgshapiro
790141858Sgshapiro#if DEAL_WITH_ERROR_SSL
791102528Sgshapiro		/* avoid repeated calls? */
792102528Sgshapiro		if (r == 0)
793102528Sgshapiro			r = -1;
794363466Sgshapiro#endif
79590792Sgshapiro		break;
79664562Sgshapiro	}
79790792Sgshapiro	if (err != NULL)
79890792Sgshapiro	{
79994334Sgshapiro		int save_errno;
80094334Sgshapiro
80194334Sgshapiro		save_errno = (errno == 0) ? EIO : errno;
802157001Sgshapiro		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
803157001Sgshapiro		{
804157001Sgshapiro			if (LogLevel > 7)
805157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
806157001Sgshapiro					  "STARTTLS: read error=timeout");
807157001Sgshapiro		}
808157001Sgshapiro		else if (LogLevel > 8)
809249729Sgshapiro		{
810249729Sgshapiro			int pri;
811249729Sgshapiro
812249729Sgshapiro			if (save_errno == EAGAIN && try > 0)
813249729Sgshapiro				pri = LOG_DEBUG;
814249729Sgshapiro			else
815249729Sgshapiro				pri = LOG_WARNING;
816249729Sgshapiro			sm_syslog(pri, NOQID,
817157001Sgshapiro				  "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
818120256Sgshapiro				  err, r, errno,
819157001Sgshapiro				  ERR_error_string(ERR_get_error(), NULL), try,
820157001Sgshapiro				  ssl_err);
821249729Sgshapiro		}
822120256Sgshapiro		else if (LogLevel > 7)
823120256Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
824285229Sgshapiro				  "STARTTLS: read error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
825157001Sgshapiro				  err, r, errno, try, ssl_err);
82694334Sgshapiro		errno = save_errno;
82790792Sgshapiro	}
82864562Sgshapiro	return r;
82964562Sgshapiro}
83064562Sgshapiro
83190792Sgshapiro/*
83290792Sgshapiro**  TLS_WRITE -- write information out through secure connection
83390792Sgshapiro**
83490792Sgshapiro**	Parameters:
83590792Sgshapiro**		fp -- the file pointer
83690792Sgshapiro**		buf -- holds the data to be securely written
83790792Sgshapiro**		size -- the number of bytes to write
83890792Sgshapiro**
83990792Sgshapiro**	Returns:
84090792Sgshapiro**		-1 on error
84190792Sgshapiro**		otherwise number of bytes written
84290792Sgshapiro*/
84390792Sgshapiro
84490792Sgshapirostatic ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
84590792Sgshapiro
84690792Sgshapirostatic ssize_t
84790792Sgshapirotls_write(fp, buf, size)
84890792Sgshapiro	SM_FILE_T *fp;
84990792Sgshapiro	const char *buf;
85090792Sgshapiro	size_t size;
85164562Sgshapiro{
852157001Sgshapiro	int r, rfd, wfd, try, ssl_err;
85390792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
854157001Sgshapiro	time_t tlsstart;
85590792Sgshapiro	char *err;
85664562Sgshapiro
857157001Sgshapiro	try = 99;
858157001Sgshapiro	err = NULL;
859157001Sgshapiro	tlsstart = curtime();
860157001Sgshapiro
861157001Sgshapiro  retry:
86290792Sgshapiro	r = SSL_write(so->con, (char *) buf, size);
86364562Sgshapiro
86490792Sgshapiro	if (r > 0)
86590792Sgshapiro		return r;
86690792Sgshapiro	err = NULL;
867157001Sgshapiro	switch (ssl_err = SSL_get_error(so->con, r))
86890792Sgshapiro	{
86990792Sgshapiro	  case SSL_ERROR_NONE:
87090792Sgshapiro	  case SSL_ERROR_ZERO_RETURN:
87190792Sgshapiro		break;
87290792Sgshapiro	  case SSL_ERROR_WANT_WRITE:
873157001Sgshapiro		err = "read W BLOCK";
874157001Sgshapiro		/* FALLTHROUGH */
87590792Sgshapiro	  case SSL_ERROR_WANT_READ:
876157001Sgshapiro		if (err == NULL)
877157001Sgshapiro			err = "read R BLOCK";
878157001Sgshapiro		rfd = SSL_get_rfd(so->con);
879157001Sgshapiro		wfd = SSL_get_wfd(so->con);
880157001Sgshapiro		try = tls_retry(so->con, rfd, wfd, tlsstart,
881157001Sgshapiro				DATA_PROGRESS_TIMEOUT, ssl_err, "write");
882157001Sgshapiro		if (try > 0)
883157001Sgshapiro			goto retry;
884157001Sgshapiro		errno = SM_ERR_TIMEOUT;
88590792Sgshapiro		break;
88690792Sgshapiro	  case SSL_ERROR_WANT_X509_LOOKUP:
88790792Sgshapiro		err = "write X BLOCK";
88890792Sgshapiro		break;
88990792Sgshapiro	  case SSL_ERROR_SYSCALL:
89090792Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
89190792Sgshapiro			break;
89290792Sgshapiro		err = "syscall error";
89390792Sgshapiro		break;
89490792Sgshapiro	  case SSL_ERROR_SSL:
89590792Sgshapiro		err = "generic SSL error";
89690792Sgshapiro/*
89790792Sgshapiro		ERR_GET_REASON(ERR_peek_error()));
89890792Sgshapiro*/
899363466Sgshapiro		tlslogerr(LOG_WARNING, 9, "write");
900102528Sgshapiro
901141858Sgshapiro#if DEAL_WITH_ERROR_SSL
902102528Sgshapiro		/* avoid repeated calls? */
903102528Sgshapiro		if (r == 0)
904102528Sgshapiro			r = -1;
905363466Sgshapiro#endif
90690792Sgshapiro		break;
90790792Sgshapiro	}
90890792Sgshapiro	if (err != NULL)
90990792Sgshapiro	{
91094334Sgshapiro		int save_errno;
91194334Sgshapiro
91294334Sgshapiro		save_errno = (errno == 0) ? EIO : errno;
913157001Sgshapiro		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
914157001Sgshapiro		{
915157001Sgshapiro			if (LogLevel > 7)
916157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
917157001Sgshapiro					  "STARTTLS: write error=timeout");
918157001Sgshapiro		}
919157001Sgshapiro		else if (LogLevel > 8)
92090792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
921157001Sgshapiro				  "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
922120256Sgshapiro				  err, r, errno,
923157001Sgshapiro				  ERR_error_string(ERR_get_error(), NULL), try,
924157001Sgshapiro				  ssl_err);
925120256Sgshapiro		else if (LogLevel > 7)
926120256Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
927157001Sgshapiro				  "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
928157001Sgshapiro				  err, r, errno, try, ssl_err);
92994334Sgshapiro		errno = save_errno;
93090792Sgshapiro	}
93190792Sgshapiro	return r;
93264562Sgshapiro}
93364562Sgshapiro
93490792Sgshapiro/*
93590792Sgshapiro**  SFDCTLS -- create tls file type and open in and out file pointers
93690792Sgshapiro**	      for sendmail to read from and write to.
93790792Sgshapiro**
93890792Sgshapiro**	Parameters:
93990792Sgshapiro**		fin -- data input source being replaced
94090792Sgshapiro**		fout -- data output source being replaced
941120256Sgshapiro**		con -- the tls connection pointer
94290792Sgshapiro**
94390792Sgshapiro**	Returns:
94490792Sgshapiro**		-1 on error
94590792Sgshapiro**		0 on success
94690792Sgshapiro**
94790792Sgshapiro**	Side effects:
94890792Sgshapiro**		The arguments "fin" and "fout" are replaced with the new
94990792Sgshapiro**		SM_FILE_T pointers.
95090792Sgshapiro**		The original "fin" and "fout" are preserved in the tls file
95190792Sgshapiro**		type but are not actually used because of the design of TLS.
95290792Sgshapiro*/
95390792Sgshapiro
95464562Sgshapiroint
95564562Sgshapirosfdctls(fin, fout, con)
95690792Sgshapiro	SM_FILE_T **fin;
95790792Sgshapiro	SM_FILE_T **fout;
95864562Sgshapiro	SSL *con;
95964562Sgshapiro{
96090792Sgshapiro	SM_FILE_T *tlsin, *tlsout;
96190792Sgshapiro	SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
96290792Sgshapiro		tls_read, tls_write, NULL, tls_getinfo, NULL,
96390792Sgshapiro		SM_TIME_FOREVER);
96490792Sgshapiro	struct tls_info info;
96564562Sgshapiro
96690792Sgshapiro	SM_ASSERT(con != NULL);
96764562Sgshapiro
96890792Sgshapiro	SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
96990792Sgshapiro		tls_read, tls_write, NULL, tls_getinfo, NULL,
97090792Sgshapiro		SM_TIME_FOREVER);
97190792Sgshapiro	info.fp = *fin;
97290792Sgshapiro	info.con = con;
973132943Sgshapiro	tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
97490792Sgshapiro			   NULL);
97590792Sgshapiro	if (tlsin == NULL)
97690792Sgshapiro		return -1;
97764562Sgshapiro
97890792Sgshapiro	info.fp = *fout;
979132943Sgshapiro	tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
98090792Sgshapiro			    NULL);
98190792Sgshapiro	if (tlsout == NULL)
98266494Sgshapiro	{
98390792Sgshapiro		(void) sm_io_close(tlsin, SM_TIME_DEFAULT);
98466494Sgshapiro		return -1;
98566494Sgshapiro	}
98690792Sgshapiro	sm_io_automode(tlsin, tlsout);
98764562Sgshapiro
98890792Sgshapiro	*fin = tlsin;
98990792Sgshapiro	*fout = tlsout;
99064562Sgshapiro	return 0;
99164562Sgshapiro}
99290792Sgshapiro#endif /* STARTTLS */
993