sfsasl.c revision 182352
164562Sgshapiro/*
2182352Sgshapiro * Copyright (c) 1999-2006, 2008 Sendmail, 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>
12182352SgshapiroSM_RCSID("@(#)$Id: sfsasl.c,v 8.117 2008/01/31 18:48:29 ca Exp $")
1364562Sgshapiro#include <stdlib.h>
1464562Sgshapiro#include <sendmail.h>
15159609Sgshapiro#include <sm/time.h>
1690792Sgshapiro#include <errno.h>
17141858Sgshapiro
18141858Sgshapiro/* allow to disable error handling code just in case... */
19141858Sgshapiro#ifndef DEAL_WITH_ERROR_SSL
20141858Sgshapiro# define DEAL_WITH_ERROR_SSL	1
21141858Sgshapiro#endif /* ! DEAL_WITH_ERROR_SSL */
22141858Sgshapiro
2390792Sgshapiro#if SASL
2490792Sgshapiro# include "sfsasl.h"
2564562Sgshapiro
2690792Sgshapiro/* Structure used by the "sasl" file type */
2790792Sgshapirostruct sasl_obj
2890792Sgshapiro{
2990792Sgshapiro	SM_FILE_T *fp;
3090792Sgshapiro	sasl_conn_t *conn;
3190792Sgshapiro};
3290792Sgshapiro
3390792Sgshapirostruct sasl_info
3490792Sgshapiro{
3590792Sgshapiro	SM_FILE_T *fp;
3690792Sgshapiro	sasl_conn_t *conn;
3790792Sgshapiro};
3890792Sgshapiro
3964562Sgshapiro/*
4090792Sgshapiro**  SASL_GETINFO - returns requested information about a "sasl" file
4190792Sgshapiro**		  descriptor.
4290792Sgshapiro**
4390792Sgshapiro**	Parameters:
4490792Sgshapiro**		fp -- the file descriptor
4590792Sgshapiro**		what -- the type of information requested
4690792Sgshapiro**		valp -- the thang to return the information in
4790792Sgshapiro**
4890792Sgshapiro**	Returns:
4990792Sgshapiro**		-1 for unknown requests
5090792Sgshapiro**		>=0 on success with valp filled in (if possible).
5164562Sgshapiro*/
5264562Sgshapiro
5390792Sgshapirostatic int sasl_getinfo __P((SM_FILE_T *, int, void *));
5464562Sgshapiro
5590792Sgshapirostatic int
5690792Sgshapirosasl_getinfo(fp, what, valp)
5790792Sgshapiro	SM_FILE_T *fp;
5890792Sgshapiro	int what;
5990792Sgshapiro	void *valp;
6090792Sgshapiro{
6190792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
6290792Sgshapiro
6390792Sgshapiro	switch (what)
6490792Sgshapiro	{
6590792Sgshapiro	  case SM_IO_WHAT_FD:
6690792Sgshapiro		if (so->fp == NULL)
6790792Sgshapiro			return -1;
6890792Sgshapiro		return so->fp->f_file; /* for stdio fileno() compatability */
6990792Sgshapiro
7090792Sgshapiro	  case SM_IO_IS_READABLE:
7190792Sgshapiro		if (so->fp == NULL)
7290792Sgshapiro			return 0;
7390792Sgshapiro
7490792Sgshapiro		/* get info from underlying file */
7590792Sgshapiro		return sm_io_getinfo(so->fp, what, valp);
7690792Sgshapiro
7790792Sgshapiro	  default:
7890792Sgshapiro		return -1;
7990792Sgshapiro	}
8090792Sgshapiro}
8190792Sgshapiro
8290792Sgshapiro/*
8390792Sgshapiro**  SASL_OPEN -- creates the sasl specific information for opening a
8490792Sgshapiro**		file of the sasl type.
8590792Sgshapiro**
8690792Sgshapiro**	Parameters:
8790792Sgshapiro**		fp -- the file pointer associated with the new open
8890792Sgshapiro**		info -- contains the sasl connection information pointer and
8990792Sgshapiro**			the original SM_FILE_T that holds the open
9090792Sgshapiro**		flags -- ignored
9190792Sgshapiro**		rpool -- ignored
9290792Sgshapiro**
9390792Sgshapiro**	Returns:
9490792Sgshapiro**		0 on success
9590792Sgshapiro*/
9690792Sgshapiro
9790792Sgshapirostatic int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
9890792Sgshapiro
9990792Sgshapiro/* ARGSUSED2 */
10090792Sgshapirostatic int
10190792Sgshapirosasl_open(fp, info, flags, rpool)
10290792Sgshapiro	SM_FILE_T *fp;
10390792Sgshapiro	const void *info;
10490792Sgshapiro	int flags;
10590792Sgshapiro	const void *rpool;
10690792Sgshapiro{
10790792Sgshapiro	struct sasl_obj *so;
10890792Sgshapiro	struct sasl_info *si = (struct sasl_info *) info;
10990792Sgshapiro
11090792Sgshapiro	so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
111120256Sgshapiro	if (so == NULL)
112120256Sgshapiro	{
113120256Sgshapiro		errno = ENOMEM;
114120256Sgshapiro		return -1;
115120256Sgshapiro	}
11690792Sgshapiro	so->fp = si->fp;
11790792Sgshapiro	so->conn = si->conn;
11890792Sgshapiro
11990792Sgshapiro	/*
12090792Sgshapiro	**  The underlying 'fp' is set to SM_IO_NOW so that the entire
12190792Sgshapiro	**  encoded string is written in one chunk. Otherwise there is
12290792Sgshapiro	**  the possibility that it may appear illegal, bogus or
12390792Sgshapiro	**  mangled to the other side of the connection.
12490792Sgshapiro	**  We will read or write through 'fp' since it is the opaque
12590792Sgshapiro	**  connection for the communications. We need to treat it this
12690792Sgshapiro	**  way in case the encoded string is to be sent down a TLS
12790792Sgshapiro	**  connection rather than, say, sm_io's stdio.
12890792Sgshapiro	*/
12990792Sgshapiro
13090792Sgshapiro	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
13190792Sgshapiro	fp->f_cookie = so;
13290792Sgshapiro	return 0;
13390792Sgshapiro}
13490792Sgshapiro
13590792Sgshapiro/*
13690792Sgshapiro**  SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
13790792Sgshapiro**
13890792Sgshapiro**	Parameters:
13990792Sgshapiro**		fp -- the file pointer to close
14090792Sgshapiro**
14190792Sgshapiro**	Returns:
14290792Sgshapiro**		0 on success
14390792Sgshapiro*/
14490792Sgshapiro
14590792Sgshapirostatic int sasl_close __P((SM_FILE_T *));
14690792Sgshapiro
14790792Sgshapirostatic int
14890792Sgshapirosasl_close(fp)
14990792Sgshapiro	SM_FILE_T *fp;
15090792Sgshapiro{
15190792Sgshapiro	struct sasl_obj *so;
15290792Sgshapiro
15390792Sgshapiro	so = (struct sasl_obj *) fp->f_cookie;
154120256Sgshapiro	if (so == NULL)
155120256Sgshapiro		return 0;
15690792Sgshapiro	if (so->fp != NULL)
15790792Sgshapiro	{
15890792Sgshapiro		sm_io_close(so->fp, SM_TIME_DEFAULT);
15990792Sgshapiro		so->fp = NULL;
16090792Sgshapiro	}
16190792Sgshapiro	sm_free(so);
16290792Sgshapiro	so = NULL;
16390792Sgshapiro	return 0;
16490792Sgshapiro}
16590792Sgshapiro
16671345Sgshapiro/* how to deallocate a buffer allocated by SASL */
16790792Sgshapiroextern void	sm_sasl_free __P((void *));
16898841Sgshapiro#  define SASL_DEALLOC(b)	sm_sasl_free(b)
16971345Sgshapiro
17090792Sgshapiro/*
17190792Sgshapiro**  SASL_READ -- read encrypted information and decrypt it for the caller
17290792Sgshapiro**
17390792Sgshapiro**	Parameters:
17490792Sgshapiro**		fp -- the file pointer
17590792Sgshapiro**		buf -- the location to place the decrypted information
17690792Sgshapiro**		size -- the number of bytes to read after decryption
17790792Sgshapiro**
17890792Sgshapiro**	Results:
17990792Sgshapiro**		-1 on error
18090792Sgshapiro**		otherwise the number of bytes read
18190792Sgshapiro*/
18290792Sgshapiro
18390792Sgshapirostatic ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
18490792Sgshapiro
18564562Sgshapirostatic ssize_t
18690792Sgshapirosasl_read(fp, buf, size)
18790792Sgshapiro	SM_FILE_T *fp;
18890792Sgshapiro	char *buf;
18964562Sgshapiro	size_t size;
19064562Sgshapiro{
19190792Sgshapiro	int result;
19290792Sgshapiro	ssize_t len;
19398121Sgshapiro# if SASL >= 20000
194110560Sgshapiro	static const char *outbuf = NULL;
19598121Sgshapiro# else /* SASL >= 20000 */
19671345Sgshapiro	static char *outbuf = NULL;
19798121Sgshapiro# endif /* SASL >= 20000 */
19871345Sgshapiro	static unsigned int outlen = 0;
19971345Sgshapiro	static unsigned int offset = 0;
20090792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
20164562Sgshapiro
20271345Sgshapiro	/*
20371345Sgshapiro	**  sasl_decode() may require more data than a single read() returns.
20471345Sgshapiro	**  Hence we have to put a loop around the decoding.
20571345Sgshapiro	**  This also requires that we may have to split up the returned
20671345Sgshapiro	**  data since it might be larger than the allowed size.
20771345Sgshapiro	**  Therefore we use a static pointer and return portions of it
20871345Sgshapiro	**  if necessary.
209120256Sgshapiro	**  XXX Note: This function is not thread-safe nor can it be used
210120256Sgshapiro	**  on more than one file. A correct implementation would store
211120256Sgshapiro	**  this data in fp->f_cookie.
21271345Sgshapiro	*/
21364562Sgshapiro
214110560Sgshapiro# if SASL >= 20000
215110560Sgshapiro	while (outlen == 0)
216110560Sgshapiro# else /* SASL >= 20000 */
21771345Sgshapiro	while (outbuf == NULL && outlen == 0)
218110560Sgshapiro# endif /* SASL >= 20000 */
21964562Sgshapiro	{
22090792Sgshapiro		len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
22171345Sgshapiro		if (len <= 0)
22271345Sgshapiro			return len;
22390792Sgshapiro		result = sasl_decode(so->conn, buf,
22490792Sgshapiro				     (unsigned int) len, &outbuf, &outlen);
22571345Sgshapiro		if (result != SASL_OK)
22671345Sgshapiro		{
227157001Sgshapiro			if (LogLevel > 7)
228157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
229157001Sgshapiro					"AUTH: sasl_decode error=%d", result);
23071345Sgshapiro			outbuf = NULL;
23171345Sgshapiro			offset = 0;
23271345Sgshapiro			outlen = 0;
23371345Sgshapiro			return -1;
23471345Sgshapiro		}
23564562Sgshapiro	}
23664562Sgshapiro
23790792Sgshapiro	if (outbuf == NULL)
23864562Sgshapiro	{
23990792Sgshapiro		/* be paranoid: outbuf == NULL but outlen != 0 */
24090792Sgshapiro		syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
24190792Sgshapiro		/* NOTREACHED */
24264562Sgshapiro	}
24390792Sgshapiro	if (outlen - offset > size)
24490792Sgshapiro	{
24590792Sgshapiro		/* return another part of the buffer */
24690792Sgshapiro		(void) memcpy(buf, outbuf + offset, size);
24790792Sgshapiro		offset += size;
24890792Sgshapiro		len = size;
24990792Sgshapiro	}
25071345Sgshapiro	else
25171345Sgshapiro	{
25290792Sgshapiro		/* return the rest of the buffer */
25390792Sgshapiro		len = outlen - offset;
25490792Sgshapiro		(void) memcpy(buf, outbuf + offset, (size_t) len);
25598121Sgshapiro# if SASL < 20000
25690792Sgshapiro		SASL_DEALLOC(outbuf);
25798121Sgshapiro# endif /* SASL < 20000 */
25890792Sgshapiro		outbuf = NULL;
25990792Sgshapiro		offset = 0;
26090792Sgshapiro		outlen = 0;
26171345Sgshapiro	}
26290792Sgshapiro	return len;
26364562Sgshapiro}
26464562Sgshapiro
26590792Sgshapiro/*
26690792Sgshapiro**  SASL_WRITE -- write information out after encrypting it
26790792Sgshapiro**
26890792Sgshapiro**	Parameters:
26990792Sgshapiro**		fp -- the file pointer
27090792Sgshapiro**		buf -- holds the data to be encrypted and written
27190792Sgshapiro**		size -- the number of bytes to have encrypted and written
27290792Sgshapiro**
27390792Sgshapiro**	Returns:
27490792Sgshapiro**		-1 on error
27590792Sgshapiro**		otherwise number of bytes written
27690792Sgshapiro*/
27790792Sgshapiro
27890792Sgshapirostatic ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
27990792Sgshapiro
28064562Sgshapirostatic ssize_t
28190792Sgshapirosasl_write(fp, buf, size)
28290792Sgshapiro	SM_FILE_T *fp;
28390792Sgshapiro	const char *buf;
28464562Sgshapiro	size_t size;
28564562Sgshapiro{
28664562Sgshapiro	int result;
28798121Sgshapiro# if SASL >= 20000
28898121Sgshapiro	const char *outbuf;
28998121Sgshapiro# else /* SASL >= 20000 */
29064562Sgshapiro	char *outbuf;
29198121Sgshapiro# endif /* SASL >= 20000 */
292141858Sgshapiro	unsigned int outlen, *maxencode;
29390792Sgshapiro	size_t ret = 0, total = 0;
29490792Sgshapiro	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
29564562Sgshapiro
296141858Sgshapiro	/*
297141858Sgshapiro	**  Fetch the maximum input buffer size for sasl_encode().
298141858Sgshapiro	**  This can be less than the size set in attemptauth()
299141858Sgshapiro	**  due to a negotation with the other side, e.g.,
300141858Sgshapiro	**  Cyrus IMAP lmtp program sets maxbuf=4096,
301141858Sgshapiro	**  digestmd5 substracts 25 and hence we'll get 4071
302141858Sgshapiro	**  instead of 8192 (MAXOUTLEN).
303141858Sgshapiro	**  Hack (for now): simply reduce the size, callers are (must be)
304141858Sgshapiro	**  able to deal with that and invoke sasl_write() again with
305141858Sgshapiro	**  the rest of the data.
306141858Sgshapiro	**  Note: it would be better to store this value in the context
307141858Sgshapiro	**  after the negotiation.
308141858Sgshapiro	*/
309141858Sgshapiro
310141858Sgshapiro	result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
311157001Sgshapiro				(const void **) &maxencode);
312141858Sgshapiro	if (result == SASL_OK && size > *maxencode && *maxencode > 0)
313141858Sgshapiro		size = *maxencode;
314141858Sgshapiro
31590792Sgshapiro	result = sasl_encode(so->conn, buf,
31690792Sgshapiro			     (unsigned int) size, &outbuf, &outlen);
31764562Sgshapiro
31864562Sgshapiro	if (result != SASL_OK)
319157001Sgshapiro	{
320157001Sgshapiro		if (LogLevel > 7)
321157001Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
322157001Sgshapiro				"AUTH: sasl_encode error=%d", result);
32364562Sgshapiro		return -1;
324157001Sgshapiro	}
32564562Sgshapiro
32664562Sgshapiro	if (outbuf != NULL)
32764562Sgshapiro	{
32890792Sgshapiro		while (outlen > 0)
32990792Sgshapiro		{
330159609Sgshapiro			errno = 0;
33194334Sgshapiro			/* XXX result == 0? */
33290792Sgshapiro			ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
33390792Sgshapiro					  &outbuf[total], outlen);
334120256Sgshapiro			if (ret <= 0)
335120256Sgshapiro				return ret;
33690792Sgshapiro			outlen -= ret;
33790792Sgshapiro			total += ret;
33890792Sgshapiro		}
33998121Sgshapiro# if SASL < 20000
34071345Sgshapiro		SASL_DEALLOC(outbuf);
34198121Sgshapiro# endif /* SASL < 20000 */
34264562Sgshapiro	}
34364562Sgshapiro	return size;
34464562Sgshapiro}
34564562Sgshapiro
34690792Sgshapiro/*
34790792Sgshapiro**  SFDCSASL -- create sasl file type and open in and out file pointers
34890792Sgshapiro**	       for sendmail to read from and write to.
34990792Sgshapiro**
35090792Sgshapiro**	Parameters:
35190792Sgshapiro**		fin -- the sm_io file encrypted data to be read from
352159609Sgshapiro**		fout -- the sm_io file encrypted data to be written to
35390792Sgshapiro**		conn -- the sasl connection pointer
354159609Sgshapiro**		tmo -- timeout
35590792Sgshapiro**
35690792Sgshapiro**	Returns:
35790792Sgshapiro**		-1 on error
35890792Sgshapiro**		0 on success
35990792Sgshapiro**
36090792Sgshapiro**	Side effects:
36190792Sgshapiro**		The arguments "fin" and "fout" are replaced with the new
36290792Sgshapiro**		SM_FILE_T pointers.
36390792Sgshapiro*/
36490792Sgshapiro
36564562Sgshapiroint
366159609Sgshapirosfdcsasl(fin, fout, conn, tmo)
36790792Sgshapiro	SM_FILE_T **fin;
36890792Sgshapiro	SM_FILE_T **fout;
36964562Sgshapiro	sasl_conn_t *conn;
370159609Sgshapiro	int tmo;
37164562Sgshapiro{
37290792Sgshapiro	SM_FILE_T *newin, *newout;
37390792Sgshapiro	SM_FILE_T  SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
37490792Sgshapiro		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
375159609Sgshapiro		SM_TIME_DEFAULT);
37690792Sgshapiro	struct sasl_info info;
37764562Sgshapiro
37864562Sgshapiro	if (conn == NULL)
37964562Sgshapiro	{
38064562Sgshapiro		/* no need to do anything */
38164562Sgshapiro		return 0;
38264562Sgshapiro	}
38364562Sgshapiro
38490792Sgshapiro	SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
38590792Sgshapiro		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
386159609Sgshapiro		SM_TIME_DEFAULT);
38790792Sgshapiro	info.fp = *fin;
38890792Sgshapiro	info.conn = conn;
389132943Sgshapiro	newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
390132943Sgshapiro			SM_IO_RDONLY_B, NULL);
39164562Sgshapiro
39290792Sgshapiro	if (newin == NULL)
39390792Sgshapiro		return -1;
39464562Sgshapiro
39590792Sgshapiro	info.fp = *fout;
39690792Sgshapiro	info.conn = conn;
397132943Sgshapiro	newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
398132943Sgshapiro			SM_IO_WRONLY_B, NULL);
39964562Sgshapiro
40090792Sgshapiro	if (newout == NULL)
40164562Sgshapiro	{
40290792Sgshapiro		(void) sm_io_close(newin, SM_TIME_DEFAULT);
40364562Sgshapiro		return -1;
40464562Sgshapiro	}
40590792Sgshapiro	sm_io_automode(newin, newout);
40690792Sgshapiro
407159609Sgshapiro	sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
408159609Sgshapiro	sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
409159609Sgshapiro
41090792Sgshapiro	*fin = newin;
41190792Sgshapiro	*fout = newout;
41264562Sgshapiro	return 0;
41364562Sgshapiro}
41490792Sgshapiro#endif /* SASL */
41564562Sgshapiro
41690792Sgshapiro#if STARTTLS
41790792Sgshapiro# include "sfsasl.h"
41890792Sgshapiro#  include <openssl/err.h>
41990792Sgshapiro
42090792Sgshapiro/* Structure used by the "tls" file type */
42190792Sgshapirostruct tls_obj
42290792Sgshapiro{
42390792Sgshapiro	SM_FILE_T *fp;
42490792Sgshapiro	SSL *con;
42590792Sgshapiro};
42690792Sgshapiro
42790792Sgshapirostruct tls_info
42890792Sgshapiro{
42990792Sgshapiro	SM_FILE_T *fp;
43090792Sgshapiro	SSL *con;
43190792Sgshapiro};
43290792Sgshapiro
43364562Sgshapiro/*
43490792Sgshapiro**  TLS_GETINFO - returns requested information about a "tls" file
43590792Sgshapiro**		 descriptor.
43690792Sgshapiro**
43790792Sgshapiro**	Parameters:
43890792Sgshapiro**		fp -- the file descriptor
43990792Sgshapiro**		what -- the type of information requested
44090792Sgshapiro**		valp -- the thang to return the information in (unused)
44190792Sgshapiro**
44290792Sgshapiro**	Returns:
44390792Sgshapiro**		-1 for unknown requests
44490792Sgshapiro**		>=0 on success with valp filled in (if possible).
44564562Sgshapiro*/
44664562Sgshapiro
44790792Sgshapirostatic int tls_getinfo __P((SM_FILE_T *, int, void *));
44864562Sgshapiro
44990792Sgshapiro/* ARGSUSED2 */
45080785Sgshapirostatic int
45190792Sgshapirotls_getinfo(fp, what, valp)
45290792Sgshapiro	SM_FILE_T *fp;
45390792Sgshapiro	int what;
45490792Sgshapiro	void *valp;
45564562Sgshapiro{
45690792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
45764562Sgshapiro
45890792Sgshapiro	switch (what)
45964562Sgshapiro	{
46090792Sgshapiro	  case SM_IO_WHAT_FD:
46190792Sgshapiro		if (so->fp == NULL)
46290792Sgshapiro			return -1;
46390792Sgshapiro		return so->fp->f_file; /* for stdio fileno() compatability */
46464562Sgshapiro
46590792Sgshapiro	  case SM_IO_IS_READABLE:
46690792Sgshapiro		return SSL_pending(so->con) > 0;
46790792Sgshapiro
46890792Sgshapiro	  default:
46990792Sgshapiro		return -1;
47090792Sgshapiro	}
47190792Sgshapiro}
47290792Sgshapiro
47364562Sgshapiro/*
47490792Sgshapiro**  TLS_OPEN -- creates the tls specific information for opening a
47590792Sgshapiro**	       file of the tls type.
47690792Sgshapiro**
47790792Sgshapiro**	Parameters:
47890792Sgshapiro**		fp -- the file pointer associated with the new open
47990792Sgshapiro**		info -- the sm_io file pointer holding the open and the
48090792Sgshapiro**			TLS encryption connection to be read from or written to
48190792Sgshapiro**		flags -- ignored
48290792Sgshapiro**		rpool -- ignored
48390792Sgshapiro**
48490792Sgshapiro**	Returns:
48590792Sgshapiro**		0 on success
48664562Sgshapiro*/
48790792Sgshapiro
48890792Sgshapirostatic int tls_open __P((SM_FILE_T *, const void *, int, const void *));
48990792Sgshapiro
49090792Sgshapiro/* ARGSUSED2 */
49190792Sgshapirostatic int
49290792Sgshapirotls_open(fp, info, flags, rpool)
49390792Sgshapiro	SM_FILE_T *fp;
49490792Sgshapiro	const void *info;
49590792Sgshapiro	int flags;
49690792Sgshapiro	const void *rpool;
49790792Sgshapiro{
49890792Sgshapiro	struct tls_obj *so;
49990792Sgshapiro	struct tls_info *ti = (struct tls_info *) info;
50090792Sgshapiro
50190792Sgshapiro	so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
502120256Sgshapiro	if (so == NULL)
503120256Sgshapiro	{
504120256Sgshapiro		errno = ENOMEM;
505120256Sgshapiro		return -1;
506120256Sgshapiro	}
50790792Sgshapiro	so->fp = ti->fp;
50890792Sgshapiro	so->con = ti->con;
50990792Sgshapiro
51090792Sgshapiro	/*
51190792Sgshapiro	**  We try to get the "raw" file descriptor that TLS uses to
51290792Sgshapiro	**  do the actual read/write with. This is to allow us control
51390792Sgshapiro	**  over the file descriptor being a blocking or non-blocking type.
51490792Sgshapiro	**  Under the covers TLS handles the change and this allows us
51590792Sgshapiro	**  to do timeouts with sm_io.
51690792Sgshapiro	*/
51790792Sgshapiro
51890792Sgshapiro	fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
51990792Sgshapiro	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
52090792Sgshapiro	fp->f_cookie = so;
52190792Sgshapiro	return 0;
52290792Sgshapiro}
52390792Sgshapiro
52490792Sgshapiro/*
52590792Sgshapiro**  TLS_CLOSE -- close the tls specific parts of the tls file pointer
52690792Sgshapiro**
52790792Sgshapiro**	Parameters:
52890792Sgshapiro**		fp -- the file pointer to close
52990792Sgshapiro**
53090792Sgshapiro**	Returns:
53190792Sgshapiro**		0 on success
53290792Sgshapiro*/
53390792Sgshapiro
53490792Sgshapirostatic int tls_close __P((SM_FILE_T *));
53590792Sgshapiro
53690792Sgshapirostatic int
53790792Sgshapirotls_close(fp)
53890792Sgshapiro	SM_FILE_T *fp;
53990792Sgshapiro{
54090792Sgshapiro	struct tls_obj *so;
54190792Sgshapiro
54290792Sgshapiro	so = (struct tls_obj *) fp->f_cookie;
543120256Sgshapiro	if (so == NULL)
544120256Sgshapiro		return 0;
54590792Sgshapiro	if (so->fp != NULL)
54690792Sgshapiro	{
54790792Sgshapiro		sm_io_close(so->fp, SM_TIME_DEFAULT);
54890792Sgshapiro		so->fp = NULL;
54964562Sgshapiro	}
55090792Sgshapiro	sm_free(so);
55190792Sgshapiro	so = NULL;
55290792Sgshapiro	return 0;
55364562Sgshapiro}
55464562Sgshapiro
55590792Sgshapiro/* maximum number of retries for TLS related I/O due to handshakes */
55690792Sgshapiro# define MAX_TLS_IOS	4
55790792Sgshapiro
55890792Sgshapiro/*
559157001Sgshapiro**  TLS_RETRY -- check whether a failed SSL operation can be retried
560157001Sgshapiro**
561157001Sgshapiro**	Parameters:
562157001Sgshapiro**		ssl -- TLS structure
563157001Sgshapiro**		rfd -- read fd
564157001Sgshapiro**		wfd -- write fd
565157001Sgshapiro**		tlsstart -- start time of TLS operation
566157001Sgshapiro**		timeout -- timeout for TLS operation
567157001Sgshapiro**		err -- SSL error
568157001Sgshapiro**		where -- description of operation
569157001Sgshapiro**
570157001Sgshapiro**	Results:
571157001Sgshapiro**		>0 on success
572157001Sgshapiro**		0 on timeout
573157001Sgshapiro**		<0 on error
574157001Sgshapiro*/
575157001Sgshapiro
576157001Sgshapiroint
577157001Sgshapirotls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
578157001Sgshapiro	SSL *ssl;
579157001Sgshapiro	int rfd;
580157001Sgshapiro	int wfd;
581157001Sgshapiro	time_t tlsstart;
582157001Sgshapiro	int timeout;
583157001Sgshapiro	int err;
584157001Sgshapiro	const char *where;
585157001Sgshapiro{
586157001Sgshapiro	int ret;
587157001Sgshapiro	time_t left;
588157001Sgshapiro	time_t now = curtime();
589157001Sgshapiro	struct timeval tv;
590157001Sgshapiro
591157001Sgshapiro	ret = -1;
592157001Sgshapiro
593157001Sgshapiro	/*
594157001Sgshapiro	**  For SSL_ERROR_WANT_{READ,WRITE}:
595157001Sgshapiro	**  There is not a complete SSL record available yet
596157001Sgshapiro	**  or there is only a partial SSL record removed from
597157001Sgshapiro	**  the network (socket) buffer into the SSL buffer.
598157001Sgshapiro	**  The SSL_connect will only succeed when a full
599157001Sgshapiro	**  SSL record is available (assuming a "real" error
600157001Sgshapiro	**  doesn't happen). To handle when a "real" error
601157001Sgshapiro	**  does happen the select is set for exceptions too.
602157001Sgshapiro	**  The connection may be re-negotiated during this time
603157001Sgshapiro	**  so both read and write "want errors" need to be handled.
604157001Sgshapiro	**  A select() exception loops back so that a proper SSL
605157001Sgshapiro	**  error message can be gotten.
606157001Sgshapiro	*/
607157001Sgshapiro
608157001Sgshapiro	left = timeout - (now - tlsstart);
609157001Sgshapiro	if (left <= 0)
610157001Sgshapiro		return 0;	/* timeout */
611157001Sgshapiro	tv.tv_sec = left;
612157001Sgshapiro	tv.tv_usec = 0;
613157001Sgshapiro
614157001Sgshapiro	if (LogLevel > 14)
615157001Sgshapiro	{
616157001Sgshapiro		sm_syslog(LOG_INFO, NOQID,
617157001Sgshapiro			  "STARTTLS=%s, info: fds=%d/%d, err=%d",
618157001Sgshapiro			  where, rfd, wfd, err);
619157001Sgshapiro	}
620157001Sgshapiro
621157001Sgshapiro	if (FD_SETSIZE > 0 &&
622157001Sgshapiro	    ((err == SSL_ERROR_WANT_READ && rfd >= FD_SETSIZE) ||
623157001Sgshapiro	     (err == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
624157001Sgshapiro	{
625157001Sgshapiro		if (LogLevel > 5)
626157001Sgshapiro		{
627157001Sgshapiro			sm_syslog(LOG_ERR, NOQID,
628157001Sgshapiro				  "STARTTLS=%s, error: fd %d/%d too large",
629157001Sgshapiro				  where, rfd, wfd);
630157001Sgshapiro		if (LogLevel > 8)
631157001Sgshapiro			tlslogerr(where);
632157001Sgshapiro		}
633157001Sgshapiro		errno = EINVAL;
634157001Sgshapiro	}
635157001Sgshapiro	else if (err == SSL_ERROR_WANT_READ)
636157001Sgshapiro	{
637157001Sgshapiro		fd_set ssl_maskr, ssl_maskx;
638157001Sgshapiro
639157001Sgshapiro		FD_ZERO(&ssl_maskr);
640157001Sgshapiro		FD_SET(rfd, &ssl_maskr);
641157001Sgshapiro		FD_ZERO(&ssl_maskx);
642157001Sgshapiro		FD_SET(rfd, &ssl_maskx);
643157001Sgshapiro		do
644157001Sgshapiro		{
645157001Sgshapiro			ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
646157001Sgshapiro					&tv);
647157001Sgshapiro		} while (ret < 0 && errno == EINTR);
648157001Sgshapiro		if (ret < 0 && errno > 0)
649157001Sgshapiro			ret = -errno;
650157001Sgshapiro	}
651157001Sgshapiro	else if (err == SSL_ERROR_WANT_WRITE)
652157001Sgshapiro	{
653157001Sgshapiro		fd_set ssl_maskw, ssl_maskx;
654157001Sgshapiro
655157001Sgshapiro		FD_ZERO(&ssl_maskw);
656157001Sgshapiro		FD_SET(wfd, &ssl_maskw);
657157001Sgshapiro		FD_ZERO(&ssl_maskx);
658157001Sgshapiro		FD_SET(rfd, &ssl_maskx);
659157001Sgshapiro		do
660157001Sgshapiro		{
661157001Sgshapiro			ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
662157001Sgshapiro					&tv);
663157001Sgshapiro		} while (ret < 0 && errno == EINTR);
664157001Sgshapiro		if (ret < 0 && errno > 0)
665157001Sgshapiro			ret = -errno;
666157001Sgshapiro	}
667157001Sgshapiro	return ret;
668157001Sgshapiro}
669157001Sgshapiro
670157001Sgshapiro/* errno to force refill() etc to stop (see IS_IO_ERROR()) */
671157001Sgshapiro#ifdef ETIMEDOUT
672157001Sgshapiro# define SM_ERR_TIMEOUT	ETIMEDOUT
673157001Sgshapiro#else /* ETIMEDOUT */
674157001Sgshapiro# define SM_ERR_TIMEOUT	EIO
675157001Sgshapiro#endif /* ETIMEDOUT */
676157001Sgshapiro
677157001Sgshapiro/*
678182352Sgshapiro**  SET_TLS_RD_TMO -- read secured information for the caller
679182352Sgshapiro**
680182352Sgshapiro**	Parameters:
681182352Sgshapiro**		rd_tmo -- read timeout
682182352Sgshapiro**
683182352Sgshapiro**	Results:
684182352Sgshapiro**		none
685182352Sgshapiro**	This is a hack: there is no way to pass it in
686182352Sgshapiro*/
687182352Sgshapiro
688182352Sgshapirostatic int tls_rd_tmo = -1;
689182352Sgshapiro
690182352Sgshapirovoid
691182352Sgshapiroset_tls_rd_tmo(rd_tmo)
692182352Sgshapiro	int rd_tmo;
693182352Sgshapiro{
694182352Sgshapiro	tls_rd_tmo = rd_tmo;
695182352Sgshapiro}
696182352Sgshapiro
697182352Sgshapiro/*
69890792Sgshapiro**  TLS_READ -- read secured information for the caller
69990792Sgshapiro**
70090792Sgshapiro**	Parameters:
70190792Sgshapiro**		fp -- the file pointer
70290792Sgshapiro**		buf -- the location to place the data
70390792Sgshapiro**		size -- the number of bytes to read from connection
70490792Sgshapiro**
70590792Sgshapiro**	Results:
70690792Sgshapiro**		-1 on error
70790792Sgshapiro**		otherwise the number of bytes read
70890792Sgshapiro*/
70990792Sgshapiro
71090792Sgshapirostatic ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
71190792Sgshapiro
71264562Sgshapirostatic ssize_t
71390792Sgshapirotls_read(fp, buf, size)
71490792Sgshapiro	SM_FILE_T *fp;
71590792Sgshapiro	char *buf;
71664562Sgshapiro	size_t size;
71764562Sgshapiro{
718157001Sgshapiro	int r, rfd, wfd, try, ssl_err;
71990792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
720157001Sgshapiro	time_t tlsstart;
72190792Sgshapiro	char *err;
72264562Sgshapiro
723157001Sgshapiro	try = 99;
724157001Sgshapiro	err = NULL;
725157001Sgshapiro	tlsstart = curtime();
726157001Sgshapiro
727157001Sgshapiro  retry:
72890792Sgshapiro	r = SSL_read(so->con, (char *) buf, size);
72964562Sgshapiro
73090792Sgshapiro	if (r > 0)
73190792Sgshapiro		return r;
73264562Sgshapiro
73390792Sgshapiro	err = NULL;
734157001Sgshapiro	switch (ssl_err = SSL_get_error(so->con, r))
73590792Sgshapiro	{
73690792Sgshapiro	  case SSL_ERROR_NONE:
73790792Sgshapiro	  case SSL_ERROR_ZERO_RETURN:
73890792Sgshapiro		break;
73990792Sgshapiro	  case SSL_ERROR_WANT_WRITE:
740157001Sgshapiro		err = "read W BLOCK";
741157001Sgshapiro		/* FALLTHROUGH */
74290792Sgshapiro	  case SSL_ERROR_WANT_READ:
743157001Sgshapiro		if (err == NULL)
74490792Sgshapiro			err = "read R BLOCK";
745157001Sgshapiro		rfd = SSL_get_rfd(so->con);
746157001Sgshapiro		wfd = SSL_get_wfd(so->con);
747157001Sgshapiro		try = tls_retry(so->con, rfd, wfd, tlsstart,
748182352Sgshapiro				(tls_rd_tmo < 0) ? TimeOuts.to_datablock
749182352Sgshapiro						 : tls_rd_tmo,
750182352Sgshapiro				ssl_err, "read");
751157001Sgshapiro		if (try > 0)
752157001Sgshapiro			goto retry;
753157001Sgshapiro		errno = SM_ERR_TIMEOUT;
75490792Sgshapiro		break;
755157001Sgshapiro
75690792Sgshapiro	  case SSL_ERROR_WANT_X509_LOOKUP:
75790792Sgshapiro		err = "write X BLOCK";
75890792Sgshapiro		break;
75990792Sgshapiro	  case SSL_ERROR_SYSCALL:
76090792Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
76190792Sgshapiro			break;
76290792Sgshapiro		err = "syscall error";
76364562Sgshapiro/*
76490792Sgshapiro		get_last_socket_error());
76564562Sgshapiro*/
76690792Sgshapiro		break;
76790792Sgshapiro	  case SSL_ERROR_SSL:
768141858Sgshapiro#if DEAL_WITH_ERROR_SSL
769102528Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
770102528Sgshapiro			break;
771141858Sgshapiro#endif /* DEAL_WITH_ERROR_SSL */
77290792Sgshapiro		err = "generic SSL error";
77390792Sgshapiro		if (LogLevel > 9)
77490792Sgshapiro			tlslogerr("read");
775102528Sgshapiro
776141858Sgshapiro#if DEAL_WITH_ERROR_SSL
777102528Sgshapiro		/* avoid repeated calls? */
778102528Sgshapiro		if (r == 0)
779102528Sgshapiro			r = -1;
780141858Sgshapiro#endif /* DEAL_WITH_ERROR_SSL */
78190792Sgshapiro		break;
78264562Sgshapiro	}
78390792Sgshapiro	if (err != NULL)
78490792Sgshapiro	{
78594334Sgshapiro		int save_errno;
78694334Sgshapiro
78794334Sgshapiro		save_errno = (errno == 0) ? EIO : errno;
788157001Sgshapiro		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
789157001Sgshapiro		{
790157001Sgshapiro			if (LogLevel > 7)
791157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
792157001Sgshapiro					  "STARTTLS: read error=timeout");
793157001Sgshapiro		}
794157001Sgshapiro		else if (LogLevel > 8)
79590792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
796157001Sgshapiro				  "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
797120256Sgshapiro				  err, r, errno,
798157001Sgshapiro				  ERR_error_string(ERR_get_error(), NULL), try,
799157001Sgshapiro				  ssl_err);
800120256Sgshapiro		else if (LogLevel > 7)
801120256Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
802157001Sgshapiro				  "STARTTLS: read error=%s (%d), retry=%d, ssl_err=%d",
803157001Sgshapiro				  err, r, errno, try, ssl_err);
80494334Sgshapiro		errno = save_errno;
80590792Sgshapiro	}
80664562Sgshapiro	return r;
80764562Sgshapiro}
80864562Sgshapiro
80990792Sgshapiro/*
81090792Sgshapiro**  TLS_WRITE -- write information out through secure connection
81190792Sgshapiro**
81290792Sgshapiro**	Parameters:
81390792Sgshapiro**		fp -- the file pointer
81490792Sgshapiro**		buf -- holds the data to be securely written
81590792Sgshapiro**		size -- the number of bytes to write
81690792Sgshapiro**
81790792Sgshapiro**	Returns:
81890792Sgshapiro**		-1 on error
81990792Sgshapiro**		otherwise number of bytes written
82090792Sgshapiro*/
82190792Sgshapiro
82290792Sgshapirostatic ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
82390792Sgshapiro
82490792Sgshapirostatic ssize_t
82590792Sgshapirotls_write(fp, buf, size)
82690792Sgshapiro	SM_FILE_T *fp;
82790792Sgshapiro	const char *buf;
82890792Sgshapiro	size_t size;
82964562Sgshapiro{
830157001Sgshapiro	int r, rfd, wfd, try, ssl_err;
83190792Sgshapiro	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
832157001Sgshapiro	time_t tlsstart;
83390792Sgshapiro	char *err;
83464562Sgshapiro
835157001Sgshapiro	try = 99;
836157001Sgshapiro	err = NULL;
837157001Sgshapiro	tlsstart = curtime();
838157001Sgshapiro
839157001Sgshapiro  retry:
84090792Sgshapiro	r = SSL_write(so->con, (char *) buf, size);
84164562Sgshapiro
84290792Sgshapiro	if (r > 0)
84390792Sgshapiro		return r;
84490792Sgshapiro	err = NULL;
845157001Sgshapiro	switch (ssl_err = SSL_get_error(so->con, r))
84690792Sgshapiro	{
84790792Sgshapiro	  case SSL_ERROR_NONE:
84890792Sgshapiro	  case SSL_ERROR_ZERO_RETURN:
84990792Sgshapiro		break;
85090792Sgshapiro	  case SSL_ERROR_WANT_WRITE:
851157001Sgshapiro		err = "read W BLOCK";
852157001Sgshapiro		/* FALLTHROUGH */
85390792Sgshapiro	  case SSL_ERROR_WANT_READ:
854157001Sgshapiro		if (err == NULL)
855157001Sgshapiro			err = "read R BLOCK";
856157001Sgshapiro		rfd = SSL_get_rfd(so->con);
857157001Sgshapiro		wfd = SSL_get_wfd(so->con);
858157001Sgshapiro		try = tls_retry(so->con, rfd, wfd, tlsstart,
859157001Sgshapiro				DATA_PROGRESS_TIMEOUT, ssl_err, "write");
860157001Sgshapiro		if (try > 0)
861157001Sgshapiro			goto retry;
862157001Sgshapiro		errno = SM_ERR_TIMEOUT;
86390792Sgshapiro		break;
86490792Sgshapiro	  case SSL_ERROR_WANT_X509_LOOKUP:
86590792Sgshapiro		err = "write X BLOCK";
86690792Sgshapiro		break;
86790792Sgshapiro	  case SSL_ERROR_SYSCALL:
86890792Sgshapiro		if (r == 0 && errno == 0) /* out of protocol EOF found */
86990792Sgshapiro			break;
87090792Sgshapiro		err = "syscall error";
87190792Sgshapiro/*
87290792Sgshapiro		get_last_socket_error());
87390792Sgshapiro*/
87490792Sgshapiro		break;
87590792Sgshapiro	  case SSL_ERROR_SSL:
87690792Sgshapiro		err = "generic SSL error";
87790792Sgshapiro/*
87890792Sgshapiro		ERR_GET_REASON(ERR_peek_error()));
87990792Sgshapiro*/
88090792Sgshapiro		if (LogLevel > 9)
88190792Sgshapiro			tlslogerr("write");
882102528Sgshapiro
883141858Sgshapiro#if DEAL_WITH_ERROR_SSL
884102528Sgshapiro		/* avoid repeated calls? */
885102528Sgshapiro		if (r == 0)
886102528Sgshapiro			r = -1;
887141858Sgshapiro#endif /* DEAL_WITH_ERROR_SSL */
88890792Sgshapiro		break;
88990792Sgshapiro	}
89090792Sgshapiro	if (err != NULL)
89190792Sgshapiro	{
89294334Sgshapiro		int save_errno;
89394334Sgshapiro
89494334Sgshapiro		save_errno = (errno == 0) ? EIO : errno;
895157001Sgshapiro		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
896157001Sgshapiro		{
897157001Sgshapiro			if (LogLevel > 7)
898157001Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
899157001Sgshapiro					  "STARTTLS: write error=timeout");
900157001Sgshapiro		}
901157001Sgshapiro		else if (LogLevel > 8)
90290792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
903157001Sgshapiro				  "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
904120256Sgshapiro				  err, r, errno,
905157001Sgshapiro				  ERR_error_string(ERR_get_error(), NULL), try,
906157001Sgshapiro				  ssl_err);
907120256Sgshapiro		else if (LogLevel > 7)
908120256Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
909157001Sgshapiro				  "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
910157001Sgshapiro				  err, r, errno, try, ssl_err);
91194334Sgshapiro		errno = save_errno;
91290792Sgshapiro	}
91390792Sgshapiro	return r;
91464562Sgshapiro}
91564562Sgshapiro
91690792Sgshapiro/*
91790792Sgshapiro**  SFDCTLS -- create tls file type and open in and out file pointers
91890792Sgshapiro**	      for sendmail to read from and write to.
91990792Sgshapiro**
92090792Sgshapiro**	Parameters:
92190792Sgshapiro**		fin -- data input source being replaced
92290792Sgshapiro**		fout -- data output source being replaced
923120256Sgshapiro**		con -- the tls connection pointer
92490792Sgshapiro**
92590792Sgshapiro**	Returns:
92690792Sgshapiro**		-1 on error
92790792Sgshapiro**		0 on success
92890792Sgshapiro**
92990792Sgshapiro**	Side effects:
93090792Sgshapiro**		The arguments "fin" and "fout" are replaced with the new
93190792Sgshapiro**		SM_FILE_T pointers.
93290792Sgshapiro**		The original "fin" and "fout" are preserved in the tls file
93390792Sgshapiro**		type but are not actually used because of the design of TLS.
93490792Sgshapiro*/
93590792Sgshapiro
93664562Sgshapiroint
93764562Sgshapirosfdctls(fin, fout, con)
93890792Sgshapiro	SM_FILE_T **fin;
93990792Sgshapiro	SM_FILE_T **fout;
94064562Sgshapiro	SSL *con;
94164562Sgshapiro{
94290792Sgshapiro	SM_FILE_T *tlsin, *tlsout;
94390792Sgshapiro	SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
94490792Sgshapiro		tls_read, tls_write, NULL, tls_getinfo, NULL,
94590792Sgshapiro		SM_TIME_FOREVER);
94690792Sgshapiro	struct tls_info info;
94764562Sgshapiro
94890792Sgshapiro	SM_ASSERT(con != NULL);
94964562Sgshapiro
95090792Sgshapiro	SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
95190792Sgshapiro		tls_read, tls_write, NULL, tls_getinfo, NULL,
95290792Sgshapiro		SM_TIME_FOREVER);
95390792Sgshapiro	info.fp = *fin;
95490792Sgshapiro	info.con = con;
955132943Sgshapiro	tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
95690792Sgshapiro			   NULL);
95790792Sgshapiro	if (tlsin == NULL)
95890792Sgshapiro		return -1;
95964562Sgshapiro
96090792Sgshapiro	info.fp = *fout;
961132943Sgshapiro	tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
96290792Sgshapiro			    NULL);
96390792Sgshapiro	if (tlsout == NULL)
96466494Sgshapiro	{
96590792Sgshapiro		(void) sm_io_close(tlsin, SM_TIME_DEFAULT);
96666494Sgshapiro		return -1;
96766494Sgshapiro	}
96890792Sgshapiro	sm_io_automode(tlsin, tlsout);
96964562Sgshapiro
97090792Sgshapiro	*fin = tlsin;
97190792Sgshapiro	*fout = tlsout;
97264562Sgshapiro	return 0;
97364562Sgshapiro}
97490792Sgshapiro#endif /* STARTTLS */
975