xref: /illumos-gate/usr/src/cmd/sendmail/src/sfsasl.c (revision e9af4bc0)
17c478bd9Sstevel@tonic-gate /*
2d4660949Sjbeck  * Copyright (c) 1999-2006, 2008 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
67c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
77c478bd9Sstevel@tonic-gate  * the sendmail distribution.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate #include <sm/gen.h>
12*e9af4bc0SJohn Beck SM_RCSID("@(#)$Id: sfsasl.c,v 8.118 2008/07/22 15:12:48 ca Exp $")
137c478bd9Sstevel@tonic-gate #include <stdlib.h>
147c478bd9Sstevel@tonic-gate #include <sendmail.h>
153ee0e492Sjbeck #include <sm/time.h>
167c478bd9Sstevel@tonic-gate #include <errno.h>
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate /* allow to disable error handling code just in case... */
197c478bd9Sstevel@tonic-gate #ifndef DEAL_WITH_ERROR_SSL
207c478bd9Sstevel@tonic-gate # define DEAL_WITH_ERROR_SSL	1
217c478bd9Sstevel@tonic-gate #endif /* ! DEAL_WITH_ERROR_SSL */
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate #if SASL
247c478bd9Sstevel@tonic-gate # include "sfsasl.h"
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /* Structure used by the "sasl" file type */
277c478bd9Sstevel@tonic-gate struct sasl_obj
287c478bd9Sstevel@tonic-gate {
297c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
307c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
317c478bd9Sstevel@tonic-gate };
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate struct sasl_info
347c478bd9Sstevel@tonic-gate {
357c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
367c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
377c478bd9Sstevel@tonic-gate };
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate **  SASL_GETINFO - returns requested information about a "sasl" file
417c478bd9Sstevel@tonic-gate **		  descriptor.
427c478bd9Sstevel@tonic-gate **
437c478bd9Sstevel@tonic-gate **	Parameters:
447c478bd9Sstevel@tonic-gate **		fp -- the file descriptor
457c478bd9Sstevel@tonic-gate **		what -- the type of information requested
467c478bd9Sstevel@tonic-gate **		valp -- the thang to return the information in
477c478bd9Sstevel@tonic-gate **
487c478bd9Sstevel@tonic-gate **	Returns:
497c478bd9Sstevel@tonic-gate **		-1 for unknown requests
507c478bd9Sstevel@tonic-gate **		>=0 on success with valp filled in (if possible).
517c478bd9Sstevel@tonic-gate */
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static int sasl_getinfo __P((SM_FILE_T *, int, void *));
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate static int
sasl_getinfo(fp,what,valp)567c478bd9Sstevel@tonic-gate sasl_getinfo(fp, what, valp)
577c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
587c478bd9Sstevel@tonic-gate 	int what;
597c478bd9Sstevel@tonic-gate 	void *valp;
607c478bd9Sstevel@tonic-gate {
617c478bd9Sstevel@tonic-gate 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	switch (what)
647c478bd9Sstevel@tonic-gate 	{
657c478bd9Sstevel@tonic-gate 	  case SM_IO_WHAT_FD:
667c478bd9Sstevel@tonic-gate 		if (so->fp == NULL)
677c478bd9Sstevel@tonic-gate 			return -1;
687c478bd9Sstevel@tonic-gate 		return so->fp->f_file; /* for stdio fileno() compatability */
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	  case SM_IO_IS_READABLE:
717c478bd9Sstevel@tonic-gate 		if (so->fp == NULL)
727c478bd9Sstevel@tonic-gate 			return 0;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate 		/* get info from underlying file */
757c478bd9Sstevel@tonic-gate 		return sm_io_getinfo(so->fp, what, valp);
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 	  default:
787c478bd9Sstevel@tonic-gate 		return -1;
797c478bd9Sstevel@tonic-gate 	}
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate **  SASL_OPEN -- creates the sasl specific information for opening a
847c478bd9Sstevel@tonic-gate **		file of the sasl type.
857c478bd9Sstevel@tonic-gate **
867c478bd9Sstevel@tonic-gate **	Parameters:
877c478bd9Sstevel@tonic-gate **		fp -- the file pointer associated with the new open
887c478bd9Sstevel@tonic-gate **		info -- contains the sasl connection information pointer and
897c478bd9Sstevel@tonic-gate **			the original SM_FILE_T that holds the open
907c478bd9Sstevel@tonic-gate **		flags -- ignored
917c478bd9Sstevel@tonic-gate **		rpool -- ignored
927c478bd9Sstevel@tonic-gate **
937c478bd9Sstevel@tonic-gate **	Returns:
947c478bd9Sstevel@tonic-gate **		0 on success
957c478bd9Sstevel@tonic-gate */
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate /* ARGSUSED2 */
1007c478bd9Sstevel@tonic-gate static int
sasl_open(fp,info,flags,rpool)1017c478bd9Sstevel@tonic-gate sasl_open(fp, info, flags, rpool)
1027c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
1037c478bd9Sstevel@tonic-gate 	const void *info;
1047c478bd9Sstevel@tonic-gate 	int flags;
1057c478bd9Sstevel@tonic-gate 	const void *rpool;
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	struct sasl_obj *so;
1087c478bd9Sstevel@tonic-gate 	struct sasl_info *si = (struct sasl_info *) info;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
1117c478bd9Sstevel@tonic-gate 	if (so == NULL)
1127c478bd9Sstevel@tonic-gate 	{
1137c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
1147c478bd9Sstevel@tonic-gate 		return -1;
1157c478bd9Sstevel@tonic-gate 	}
1167c478bd9Sstevel@tonic-gate 	so->fp = si->fp;
1177c478bd9Sstevel@tonic-gate 	so->conn = si->conn;
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	/*
1207c478bd9Sstevel@tonic-gate 	**  The underlying 'fp' is set to SM_IO_NOW so that the entire
1217c478bd9Sstevel@tonic-gate 	**  encoded string is written in one chunk. Otherwise there is
1227c478bd9Sstevel@tonic-gate 	**  the possibility that it may appear illegal, bogus or
1237c478bd9Sstevel@tonic-gate 	**  mangled to the other side of the connection.
1247c478bd9Sstevel@tonic-gate 	**  We will read or write through 'fp' since it is the opaque
1257c478bd9Sstevel@tonic-gate 	**  connection for the communications. We need to treat it this
1267c478bd9Sstevel@tonic-gate 	**  way in case the encoded string is to be sent down a TLS
1277c478bd9Sstevel@tonic-gate 	**  connection rather than, say, sm_io's stdio.
1287c478bd9Sstevel@tonic-gate 	*/
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
1317c478bd9Sstevel@tonic-gate 	fp->f_cookie = so;
1327c478bd9Sstevel@tonic-gate 	return 0;
1337c478bd9Sstevel@tonic-gate }
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate **  SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
1377c478bd9Sstevel@tonic-gate **
1387c478bd9Sstevel@tonic-gate **	Parameters:
1397c478bd9Sstevel@tonic-gate **		fp -- the file pointer to close
1407c478bd9Sstevel@tonic-gate **
1417c478bd9Sstevel@tonic-gate **	Returns:
1427c478bd9Sstevel@tonic-gate **		0 on success
1437c478bd9Sstevel@tonic-gate */
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate static int sasl_close __P((SM_FILE_T *));
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static int
sasl_close(fp)1487c478bd9Sstevel@tonic-gate sasl_close(fp)
1497c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
1507c478bd9Sstevel@tonic-gate {
1517c478bd9Sstevel@tonic-gate 	struct sasl_obj *so;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	so = (struct sasl_obj *) fp->f_cookie;
1547c478bd9Sstevel@tonic-gate 	if (so == NULL)
1557c478bd9Sstevel@tonic-gate 		return 0;
1567c478bd9Sstevel@tonic-gate 	if (so->fp != NULL)
1577c478bd9Sstevel@tonic-gate 	{
1587c478bd9Sstevel@tonic-gate 		sm_io_close(so->fp, SM_TIME_DEFAULT);
1597c478bd9Sstevel@tonic-gate 		so->fp = NULL;
1607c478bd9Sstevel@tonic-gate 	}
1617c478bd9Sstevel@tonic-gate 	sm_free(so);
1627c478bd9Sstevel@tonic-gate 	so = NULL;
1637c478bd9Sstevel@tonic-gate 	return 0;
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /* how to deallocate a buffer allocated by SASL */
1677c478bd9Sstevel@tonic-gate extern void	sm_sasl_free __P((void *));
1687c478bd9Sstevel@tonic-gate #  define SASL_DEALLOC(b)	sm_sasl_free(b)
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate /*
1717c478bd9Sstevel@tonic-gate **  SASL_READ -- read encrypted information and decrypt it for the caller
1727c478bd9Sstevel@tonic-gate **
1737c478bd9Sstevel@tonic-gate **	Parameters:
1747c478bd9Sstevel@tonic-gate **		fp -- the file pointer
1757c478bd9Sstevel@tonic-gate **		buf -- the location to place the decrypted information
1767c478bd9Sstevel@tonic-gate **		size -- the number of bytes to read after decryption
1777c478bd9Sstevel@tonic-gate **
1787c478bd9Sstevel@tonic-gate **	Results:
1797c478bd9Sstevel@tonic-gate **		-1 on error
1807c478bd9Sstevel@tonic-gate **		otherwise the number of bytes read
1817c478bd9Sstevel@tonic-gate */
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate static ssize_t
sasl_read(fp,buf,size)1867c478bd9Sstevel@tonic-gate sasl_read(fp, buf, size)
1877c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
1887c478bd9Sstevel@tonic-gate 	char *buf;
1897c478bd9Sstevel@tonic-gate 	size_t size;
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate 	int result;
1927c478bd9Sstevel@tonic-gate 	ssize_t len;
1937c478bd9Sstevel@tonic-gate # if SASL >= 20000
1947c478bd9Sstevel@tonic-gate 	static const char *outbuf = NULL;
1957c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1967c478bd9Sstevel@tonic-gate 	static char *outbuf = NULL;
1977c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1987c478bd9Sstevel@tonic-gate 	static unsigned int outlen = 0;
1997c478bd9Sstevel@tonic-gate 	static unsigned int offset = 0;
2007c478bd9Sstevel@tonic-gate 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	/*
2037c478bd9Sstevel@tonic-gate 	**  sasl_decode() may require more data than a single read() returns.
2047c478bd9Sstevel@tonic-gate 	**  Hence we have to put a loop around the decoding.
2057c478bd9Sstevel@tonic-gate 	**  This also requires that we may have to split up the returned
2067c478bd9Sstevel@tonic-gate 	**  data since it might be larger than the allowed size.
2077c478bd9Sstevel@tonic-gate 	**  Therefore we use a static pointer and return portions of it
2087c478bd9Sstevel@tonic-gate 	**  if necessary.
2097c478bd9Sstevel@tonic-gate 	**  XXX Note: This function is not thread-safe nor can it be used
2107c478bd9Sstevel@tonic-gate 	**  on more than one file. A correct implementation would store
2117c478bd9Sstevel@tonic-gate 	**  this data in fp->f_cookie.
2127c478bd9Sstevel@tonic-gate 	*/
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate # if SASL >= 20000
2157c478bd9Sstevel@tonic-gate 	while (outlen == 0)
2167c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
2177c478bd9Sstevel@tonic-gate 	while (outbuf == NULL && outlen == 0)
2187c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
2197c478bd9Sstevel@tonic-gate 	{
2207c478bd9Sstevel@tonic-gate 		len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
2217c478bd9Sstevel@tonic-gate 		if (len <= 0)
2227c478bd9Sstevel@tonic-gate 			return len;
2237c478bd9Sstevel@tonic-gate 		result = sasl_decode(so->conn, buf,
2247c478bd9Sstevel@tonic-gate 				     (unsigned int) len, &outbuf, &outlen);
2257c478bd9Sstevel@tonic-gate 		if (result != SASL_OK)
2267c478bd9Sstevel@tonic-gate 		{
22749218d4fSjbeck 			if (LogLevel > 7)
22849218d4fSjbeck 				sm_syslog(LOG_WARNING, NOQID,
229445f2479Sjbeck 					"AUTH: sasl_decode error=%d", result);
2307c478bd9Sstevel@tonic-gate 			outbuf = NULL;
2317c478bd9Sstevel@tonic-gate 			offset = 0;
2327c478bd9Sstevel@tonic-gate 			outlen = 0;
2337c478bd9Sstevel@tonic-gate 			return -1;
2347c478bd9Sstevel@tonic-gate 		}
2357c478bd9Sstevel@tonic-gate 	}
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	if (outbuf == NULL)
2387c478bd9Sstevel@tonic-gate 	{
2397c478bd9Sstevel@tonic-gate 		/* be paranoid: outbuf == NULL but outlen != 0 */
2407c478bd9Sstevel@tonic-gate 		syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
2417c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
2427c478bd9Sstevel@tonic-gate 	}
2437c478bd9Sstevel@tonic-gate 	if (outlen - offset > size)
2447c478bd9Sstevel@tonic-gate 	{
2457c478bd9Sstevel@tonic-gate 		/* return another part of the buffer */
2467c478bd9Sstevel@tonic-gate 		(void) memcpy(buf, outbuf + offset, size);
2477c478bd9Sstevel@tonic-gate 		offset += size;
2487c478bd9Sstevel@tonic-gate 		len = size;
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 	else
2517c478bd9Sstevel@tonic-gate 	{
2527c478bd9Sstevel@tonic-gate 		/* return the rest of the buffer */
2537c478bd9Sstevel@tonic-gate 		len = outlen - offset;
2547c478bd9Sstevel@tonic-gate 		(void) memcpy(buf, outbuf + offset, (size_t) len);
2557c478bd9Sstevel@tonic-gate # if SASL < 20000
2567c478bd9Sstevel@tonic-gate 		SASL_DEALLOC(outbuf);
2577c478bd9Sstevel@tonic-gate # endif /* SASL < 20000 */
2587c478bd9Sstevel@tonic-gate 		outbuf = NULL;
2597c478bd9Sstevel@tonic-gate 		offset = 0;
2607c478bd9Sstevel@tonic-gate 		outlen = 0;
2617c478bd9Sstevel@tonic-gate 	}
2627c478bd9Sstevel@tonic-gate 	return len;
2637c478bd9Sstevel@tonic-gate }
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate /*
2667c478bd9Sstevel@tonic-gate **  SASL_WRITE -- write information out after encrypting it
2677c478bd9Sstevel@tonic-gate **
2687c478bd9Sstevel@tonic-gate **	Parameters:
2697c478bd9Sstevel@tonic-gate **		fp -- the file pointer
2707c478bd9Sstevel@tonic-gate **		buf -- holds the data to be encrypted and written
2717c478bd9Sstevel@tonic-gate **		size -- the number of bytes to have encrypted and written
2727c478bd9Sstevel@tonic-gate **
2737c478bd9Sstevel@tonic-gate **	Returns:
2747c478bd9Sstevel@tonic-gate **		-1 on error
2757c478bd9Sstevel@tonic-gate **		otherwise number of bytes written
2767c478bd9Sstevel@tonic-gate */
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate static ssize_t
sasl_write(fp,buf,size)2817c478bd9Sstevel@tonic-gate sasl_write(fp, buf, size)
2827c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
2837c478bd9Sstevel@tonic-gate 	const char *buf;
2847c478bd9Sstevel@tonic-gate 	size_t size;
2857c478bd9Sstevel@tonic-gate {
2867c478bd9Sstevel@tonic-gate 	int result;
2877c478bd9Sstevel@tonic-gate # if SASL >= 20000
2887c478bd9Sstevel@tonic-gate 	const char *outbuf;
2897c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
2907c478bd9Sstevel@tonic-gate 	char *outbuf;
2917c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
2927c478bd9Sstevel@tonic-gate 	unsigned int outlen, *maxencode;
2937c478bd9Sstevel@tonic-gate 	size_t ret = 0, total = 0;
2947c478bd9Sstevel@tonic-gate 	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/*
2977c478bd9Sstevel@tonic-gate 	**  Fetch the maximum input buffer size for sasl_encode().
2987c478bd9Sstevel@tonic-gate 	**  This can be less than the size set in attemptauth()
299*e9af4bc0SJohn Beck 	**  due to a negotiation with the other side, e.g.,
3007c478bd9Sstevel@tonic-gate 	**  Cyrus IMAP lmtp program sets maxbuf=4096,
3017c478bd9Sstevel@tonic-gate 	**  digestmd5 substracts 25 and hence we'll get 4071
3027c478bd9Sstevel@tonic-gate 	**  instead of 8192 (MAXOUTLEN).
3037c478bd9Sstevel@tonic-gate 	**  Hack (for now): simply reduce the size, callers are (must be)
3047c478bd9Sstevel@tonic-gate 	**  able to deal with that and invoke sasl_write() again with
3057c478bd9Sstevel@tonic-gate 	**  the rest of the data.
3067c478bd9Sstevel@tonic-gate 	**  Note: it would be better to store this value in the context
3077c478bd9Sstevel@tonic-gate 	**  after the negotiation.
3087c478bd9Sstevel@tonic-gate 	*/
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
311445f2479Sjbeck 				(const void **) &maxencode);
3127c478bd9Sstevel@tonic-gate 	if (result == SASL_OK && size > *maxencode && *maxencode > 0)
3137c478bd9Sstevel@tonic-gate 		size = *maxencode;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	result = sasl_encode(so->conn, buf,
3167c478bd9Sstevel@tonic-gate 			     (unsigned int) size, &outbuf, &outlen);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
31949218d4fSjbeck 	{
32049218d4fSjbeck 		if (LogLevel > 7)
32149218d4fSjbeck 			sm_syslog(LOG_WARNING, NOQID,
322445f2479Sjbeck 				"AUTH: sasl_encode error=%d", result);
3237c478bd9Sstevel@tonic-gate 		return -1;
32449218d4fSjbeck 	}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (outbuf != NULL)
3277c478bd9Sstevel@tonic-gate 	{
3287c478bd9Sstevel@tonic-gate 		while (outlen > 0)
3297c478bd9Sstevel@tonic-gate 		{
3303ee0e492Sjbeck 			errno = 0;
3317c478bd9Sstevel@tonic-gate 			/* XXX result == 0? */
3327c478bd9Sstevel@tonic-gate 			ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
3337c478bd9Sstevel@tonic-gate 					  &outbuf[total], outlen);
3347c478bd9Sstevel@tonic-gate 			if (ret <= 0)
3357c478bd9Sstevel@tonic-gate 				return ret;
3367c478bd9Sstevel@tonic-gate 			outlen -= ret;
3377c478bd9Sstevel@tonic-gate 			total += ret;
3387c478bd9Sstevel@tonic-gate 		}
3397c478bd9Sstevel@tonic-gate # if SASL < 20000
3407c478bd9Sstevel@tonic-gate 		SASL_DEALLOC(outbuf);
3417c478bd9Sstevel@tonic-gate # endif /* SASL < 20000 */
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 	return size;
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate /*
3477c478bd9Sstevel@tonic-gate **  SFDCSASL -- create sasl file type and open in and out file pointers
3487c478bd9Sstevel@tonic-gate **	       for sendmail to read from and write to.
3497c478bd9Sstevel@tonic-gate **
3507c478bd9Sstevel@tonic-gate **	Parameters:
3517c478bd9Sstevel@tonic-gate **		fin -- the sm_io file encrypted data to be read from
3523ee0e492Sjbeck **		fout -- the sm_io file encrypted data to be written to
3537c478bd9Sstevel@tonic-gate **		conn -- the sasl connection pointer
3543ee0e492Sjbeck **		tmo -- timeout
3557c478bd9Sstevel@tonic-gate **
3567c478bd9Sstevel@tonic-gate **	Returns:
3577c478bd9Sstevel@tonic-gate **		-1 on error
3587c478bd9Sstevel@tonic-gate **		0 on success
3597c478bd9Sstevel@tonic-gate **
3607c478bd9Sstevel@tonic-gate **	Side effects:
3617c478bd9Sstevel@tonic-gate **		The arguments "fin" and "fout" are replaced with the new
3627c478bd9Sstevel@tonic-gate **		SM_FILE_T pointers.
3637c478bd9Sstevel@tonic-gate */
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate int
sfdcsasl(fin,fout,conn,tmo)3663ee0e492Sjbeck sfdcsasl(fin, fout, conn, tmo)
3677c478bd9Sstevel@tonic-gate 	SM_FILE_T **fin;
3687c478bd9Sstevel@tonic-gate 	SM_FILE_T **fout;
3697c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
3703ee0e492Sjbeck 	int tmo;
3717c478bd9Sstevel@tonic-gate {
3727c478bd9Sstevel@tonic-gate 	SM_FILE_T *newin, *newout;
3737c478bd9Sstevel@tonic-gate 	SM_FILE_T  SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
3747c478bd9Sstevel@tonic-gate 		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
3753ee0e492Sjbeck 		SM_TIME_DEFAULT);
3767c478bd9Sstevel@tonic-gate 	struct sasl_info info;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	if (conn == NULL)
3797c478bd9Sstevel@tonic-gate 	{
3807c478bd9Sstevel@tonic-gate 		/* no need to do anything */
3817c478bd9Sstevel@tonic-gate 		return 0;
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
3857c478bd9Sstevel@tonic-gate 		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
3863ee0e492Sjbeck 		SM_TIME_DEFAULT);
3877c478bd9Sstevel@tonic-gate 	info.fp = *fin;
3887c478bd9Sstevel@tonic-gate 	info.conn = conn;
3897c478bd9Sstevel@tonic-gate 	newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
3907c478bd9Sstevel@tonic-gate 			SM_IO_RDONLY_B, NULL);
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	if (newin == NULL)
3937c478bd9Sstevel@tonic-gate 		return -1;
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	info.fp = *fout;
3967c478bd9Sstevel@tonic-gate 	info.conn = conn;
3977c478bd9Sstevel@tonic-gate 	newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
3987c478bd9Sstevel@tonic-gate 			SM_IO_WRONLY_B, NULL);
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	if (newout == NULL)
4017c478bd9Sstevel@tonic-gate 	{
4027c478bd9Sstevel@tonic-gate 		(void) sm_io_close(newin, SM_TIME_DEFAULT);
4037c478bd9Sstevel@tonic-gate 		return -1;
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 	sm_io_automode(newin, newout);
4067c478bd9Sstevel@tonic-gate 
4073ee0e492Sjbeck 	sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
4083ee0e492Sjbeck 	sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
4093ee0e492Sjbeck 
4107c478bd9Sstevel@tonic-gate 	*fin = newin;
4117c478bd9Sstevel@tonic-gate 	*fout = newout;
4127c478bd9Sstevel@tonic-gate 	return 0;
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate #endif /* SASL */
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate #if STARTTLS
4177c478bd9Sstevel@tonic-gate # include "sfsasl.h"
4187c478bd9Sstevel@tonic-gate #  include <openssl/err.h>
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate /* Structure used by the "tls" file type */
4217c478bd9Sstevel@tonic-gate struct tls_obj
4227c478bd9Sstevel@tonic-gate {
4237c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
4247c478bd9Sstevel@tonic-gate 	SSL *con;
4257c478bd9Sstevel@tonic-gate };
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate struct tls_info
4287c478bd9Sstevel@tonic-gate {
4297c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
4307c478bd9Sstevel@tonic-gate 	SSL *con;
4317c478bd9Sstevel@tonic-gate };
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate /*
4347c478bd9Sstevel@tonic-gate **  TLS_GETINFO - returns requested information about a "tls" file
4357c478bd9Sstevel@tonic-gate **		 descriptor.
4367c478bd9Sstevel@tonic-gate **
4377c478bd9Sstevel@tonic-gate **	Parameters:
4387c478bd9Sstevel@tonic-gate **		fp -- the file descriptor
4397c478bd9Sstevel@tonic-gate **		what -- the type of information requested
4407c478bd9Sstevel@tonic-gate **		valp -- the thang to return the information in (unused)
4417c478bd9Sstevel@tonic-gate **
4427c478bd9Sstevel@tonic-gate **	Returns:
4437c478bd9Sstevel@tonic-gate **		-1 for unknown requests
4447c478bd9Sstevel@tonic-gate **		>=0 on success with valp filled in (if possible).
4457c478bd9Sstevel@tonic-gate */
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate static int tls_getinfo __P((SM_FILE_T *, int, void *));
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate /* ARGSUSED2 */
4507c478bd9Sstevel@tonic-gate static int
tls_getinfo(fp,what,valp)4517c478bd9Sstevel@tonic-gate tls_getinfo(fp, what, valp)
4527c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
4537c478bd9Sstevel@tonic-gate 	int what;
4547c478bd9Sstevel@tonic-gate 	void *valp;
4557c478bd9Sstevel@tonic-gate {
4567c478bd9Sstevel@tonic-gate 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	switch (what)
4597c478bd9Sstevel@tonic-gate 	{
4607c478bd9Sstevel@tonic-gate 	  case SM_IO_WHAT_FD:
4617c478bd9Sstevel@tonic-gate 		if (so->fp == NULL)
4627c478bd9Sstevel@tonic-gate 			return -1;
4637c478bd9Sstevel@tonic-gate 		return so->fp->f_file; /* for stdio fileno() compatability */
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	  case SM_IO_IS_READABLE:
4667c478bd9Sstevel@tonic-gate 		return SSL_pending(so->con) > 0;
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	  default:
4697c478bd9Sstevel@tonic-gate 		return -1;
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate /*
4747c478bd9Sstevel@tonic-gate **  TLS_OPEN -- creates the tls specific information for opening a
4757c478bd9Sstevel@tonic-gate **	       file of the tls type.
4767c478bd9Sstevel@tonic-gate **
4777c478bd9Sstevel@tonic-gate **	Parameters:
4787c478bd9Sstevel@tonic-gate **		fp -- the file pointer associated with the new open
4797c478bd9Sstevel@tonic-gate **		info -- the sm_io file pointer holding the open and the
4807c478bd9Sstevel@tonic-gate **			TLS encryption connection to be read from or written to
4817c478bd9Sstevel@tonic-gate **		flags -- ignored
4827c478bd9Sstevel@tonic-gate **		rpool -- ignored
4837c478bd9Sstevel@tonic-gate **
4847c478bd9Sstevel@tonic-gate **	Returns:
4857c478bd9Sstevel@tonic-gate **		0 on success
4867c478bd9Sstevel@tonic-gate */
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate static int tls_open __P((SM_FILE_T *, const void *, int, const void *));
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate /* ARGSUSED2 */
4917c478bd9Sstevel@tonic-gate static int
tls_open(fp,info,flags,rpool)4927c478bd9Sstevel@tonic-gate tls_open(fp, info, flags, rpool)
4937c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
4947c478bd9Sstevel@tonic-gate 	const void *info;
4957c478bd9Sstevel@tonic-gate 	int flags;
4967c478bd9Sstevel@tonic-gate 	const void *rpool;
4977c478bd9Sstevel@tonic-gate {
4987c478bd9Sstevel@tonic-gate 	struct tls_obj *so;
4997c478bd9Sstevel@tonic-gate 	struct tls_info *ti = (struct tls_info *) info;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
5027c478bd9Sstevel@tonic-gate 	if (so == NULL)
5037c478bd9Sstevel@tonic-gate 	{
5047c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
5057c478bd9Sstevel@tonic-gate 		return -1;
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate 	so->fp = ti->fp;
5087c478bd9Sstevel@tonic-gate 	so->con = ti->con;
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	/*
5117c478bd9Sstevel@tonic-gate 	**  We try to get the "raw" file descriptor that TLS uses to
5127c478bd9Sstevel@tonic-gate 	**  do the actual read/write with. This is to allow us control
5137c478bd9Sstevel@tonic-gate 	**  over the file descriptor being a blocking or non-blocking type.
5147c478bd9Sstevel@tonic-gate 	**  Under the covers TLS handles the change and this allows us
5157c478bd9Sstevel@tonic-gate 	**  to do timeouts with sm_io.
5167c478bd9Sstevel@tonic-gate 	*/
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
5197c478bd9Sstevel@tonic-gate 	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
5207c478bd9Sstevel@tonic-gate 	fp->f_cookie = so;
5217c478bd9Sstevel@tonic-gate 	return 0;
5227c478bd9Sstevel@tonic-gate }
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate /*
5257c478bd9Sstevel@tonic-gate **  TLS_CLOSE -- close the tls specific parts of the tls file pointer
5267c478bd9Sstevel@tonic-gate **
5277c478bd9Sstevel@tonic-gate **	Parameters:
5287c478bd9Sstevel@tonic-gate **		fp -- the file pointer to close
5297c478bd9Sstevel@tonic-gate **
5307c478bd9Sstevel@tonic-gate **	Returns:
5317c478bd9Sstevel@tonic-gate **		0 on success
5327c478bd9Sstevel@tonic-gate */
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate static int tls_close __P((SM_FILE_T *));
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate static int
tls_close(fp)5377c478bd9Sstevel@tonic-gate tls_close(fp)
5387c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	struct tls_obj *so;
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	so = (struct tls_obj *) fp->f_cookie;
5437c478bd9Sstevel@tonic-gate 	if (so == NULL)
5447c478bd9Sstevel@tonic-gate 		return 0;
5457c478bd9Sstevel@tonic-gate 	if (so->fp != NULL)
5467c478bd9Sstevel@tonic-gate 	{
5477c478bd9Sstevel@tonic-gate 		sm_io_close(so->fp, SM_TIME_DEFAULT);
5487c478bd9Sstevel@tonic-gate 		so->fp = NULL;
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 	sm_free(so);
5517c478bd9Sstevel@tonic-gate 	so = NULL;
5527c478bd9Sstevel@tonic-gate 	return 0;
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate /* maximum number of retries for TLS related I/O due to handshakes */
5567c478bd9Sstevel@tonic-gate # define MAX_TLS_IOS	4
5577c478bd9Sstevel@tonic-gate 
558445f2479Sjbeck /*
559445f2479Sjbeck **  TLS_RETRY -- check whether a failed SSL operation can be retried
560445f2479Sjbeck **
561445f2479Sjbeck **	Parameters:
562445f2479Sjbeck **		ssl -- TLS structure
563445f2479Sjbeck **		rfd -- read fd
564445f2479Sjbeck **		wfd -- write fd
565445f2479Sjbeck **		tlsstart -- start time of TLS operation
566445f2479Sjbeck **		timeout -- timeout for TLS operation
567445f2479Sjbeck **		err -- SSL error
568445f2479Sjbeck **		where -- description of operation
569445f2479Sjbeck **
570445f2479Sjbeck **	Results:
571445f2479Sjbeck **		>0 on success
572445f2479Sjbeck **		0 on timeout
573445f2479Sjbeck **		<0 on error
574445f2479Sjbeck */
575445f2479Sjbeck 
576445f2479Sjbeck int
tls_retry(ssl,rfd,wfd,tlsstart,timeout,err,where)577445f2479Sjbeck tls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
578445f2479Sjbeck 	SSL *ssl;
579445f2479Sjbeck 	int rfd;
580445f2479Sjbeck 	int wfd;
581445f2479Sjbeck 	time_t tlsstart;
582445f2479Sjbeck 	int timeout;
583445f2479Sjbeck 	int err;
584445f2479Sjbeck 	const char *where;
585445f2479Sjbeck {
586445f2479Sjbeck 	int ret;
587445f2479Sjbeck 	time_t left;
588445f2479Sjbeck 	time_t now = curtime();
589445f2479Sjbeck 	struct timeval tv;
590445f2479Sjbeck 
591445f2479Sjbeck 	ret = -1;
592445f2479Sjbeck 
593445f2479Sjbeck 	/*
594445f2479Sjbeck 	**  For SSL_ERROR_WANT_{READ,WRITE}:
595445f2479Sjbeck 	**  There is not a complete SSL record available yet
596445f2479Sjbeck 	**  or there is only a partial SSL record removed from
597445f2479Sjbeck 	**  the network (socket) buffer into the SSL buffer.
598445f2479Sjbeck 	**  The SSL_connect will only succeed when a full
599445f2479Sjbeck 	**  SSL record is available (assuming a "real" error
600445f2479Sjbeck 	**  doesn't happen). To handle when a "real" error
601445f2479Sjbeck 	**  does happen the select is set for exceptions too.
602445f2479Sjbeck 	**  The connection may be re-negotiated during this time
603445f2479Sjbeck 	**  so both read and write "want errors" need to be handled.
604445f2479Sjbeck 	**  A select() exception loops back so that a proper SSL
605445f2479Sjbeck 	**  error message can be gotten.
606445f2479Sjbeck 	*/
607445f2479Sjbeck 
608445f2479Sjbeck 	left = timeout - (now - tlsstart);
609445f2479Sjbeck 	if (left <= 0)
610445f2479Sjbeck 		return 0;	/* timeout */
611445f2479Sjbeck 	tv.tv_sec = left;
612445f2479Sjbeck 	tv.tv_usec = 0;
613445f2479Sjbeck 
614445f2479Sjbeck 	if (LogLevel > 14)
615445f2479Sjbeck 	{
616445f2479Sjbeck 		sm_syslog(LOG_INFO, NOQID,
617445f2479Sjbeck 			  "STARTTLS=%s, info: fds=%d/%d, err=%d",
618445f2479Sjbeck 			  where, rfd, wfd, err);
619445f2479Sjbeck 	}
620445f2479Sjbeck 
621445f2479Sjbeck 	if (FD_SETSIZE > 0 &&
622445f2479Sjbeck 	    ((err == SSL_ERROR_WANT_READ && rfd >= FD_SETSIZE) ||
623445f2479Sjbeck 	     (err == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
624445f2479Sjbeck 	{
625445f2479Sjbeck 		if (LogLevel > 5)
626445f2479Sjbeck 		{
627445f2479Sjbeck 			sm_syslog(LOG_ERR, NOQID,
628445f2479Sjbeck 				  "STARTTLS=%s, error: fd %d/%d too large",
629445f2479Sjbeck 				  where, rfd, wfd);
630445f2479Sjbeck 		if (LogLevel > 8)
631445f2479Sjbeck 			tlslogerr(where);
632445f2479Sjbeck 		}
633445f2479Sjbeck 		errno = EINVAL;
634445f2479Sjbeck 	}
635445f2479Sjbeck 	else if (err == SSL_ERROR_WANT_READ)
636445f2479Sjbeck 	{
637445f2479Sjbeck 		fd_set ssl_maskr, ssl_maskx;
638445f2479Sjbeck 
639445f2479Sjbeck 		FD_ZERO(&ssl_maskr);
640445f2479Sjbeck 		FD_SET(rfd, &ssl_maskr);
641445f2479Sjbeck 		FD_ZERO(&ssl_maskx);
642445f2479Sjbeck 		FD_SET(rfd, &ssl_maskx);
643445f2479Sjbeck 		do
644445f2479Sjbeck 		{
645445f2479Sjbeck 			ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
646445f2479Sjbeck 					&tv);
647445f2479Sjbeck 		} while (ret < 0 && errno == EINTR);
648445f2479Sjbeck 		if (ret < 0 && errno > 0)
649445f2479Sjbeck 			ret = -errno;
650445f2479Sjbeck 	}
651445f2479Sjbeck 	else if (err == SSL_ERROR_WANT_WRITE)
652445f2479Sjbeck 	{
653445f2479Sjbeck 		fd_set ssl_maskw, ssl_maskx;
654445f2479Sjbeck 
655445f2479Sjbeck 		FD_ZERO(&ssl_maskw);
656445f2479Sjbeck 		FD_SET(wfd, &ssl_maskw);
657445f2479Sjbeck 		FD_ZERO(&ssl_maskx);
658445f2479Sjbeck 		FD_SET(rfd, &ssl_maskx);
659445f2479Sjbeck 		do
660445f2479Sjbeck 		{
661445f2479Sjbeck 			ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
662445f2479Sjbeck 					&tv);
663445f2479Sjbeck 		} while (ret < 0 && errno == EINTR);
664445f2479Sjbeck 		if (ret < 0 && errno > 0)
665445f2479Sjbeck 			ret = -errno;
666445f2479Sjbeck 	}
667445f2479Sjbeck 	return ret;
668445f2479Sjbeck }
669445f2479Sjbeck 
670445f2479Sjbeck /* errno to force refill() etc to stop (see IS_IO_ERROR()) */
671445f2479Sjbeck #ifdef ETIMEDOUT
672445f2479Sjbeck # define SM_ERR_TIMEOUT	ETIMEDOUT
673445f2479Sjbeck #else /* ETIMEDOUT */
674445f2479Sjbeck # define SM_ERR_TIMEOUT	EIO
675445f2479Sjbeck #endif /* ETIMEDOUT */
676445f2479Sjbeck 
677d4660949Sjbeck /*
678d4660949Sjbeck **  SET_TLS_RD_TMO -- read secured information for the caller
679d4660949Sjbeck **
680d4660949Sjbeck **	Parameters:
681d4660949Sjbeck **		rd_tmo -- read timeout
682d4660949Sjbeck **
683d4660949Sjbeck **	Results:
684d4660949Sjbeck **		none
685d4660949Sjbeck **	This is a hack: there is no way to pass it in
686d4660949Sjbeck */
687d4660949Sjbeck 
688d4660949Sjbeck static int tls_rd_tmo = -1;
689d4660949Sjbeck 
690d4660949Sjbeck void
set_tls_rd_tmo(rd_tmo)691d4660949Sjbeck set_tls_rd_tmo(rd_tmo)
692d4660949Sjbeck 	int rd_tmo;
693d4660949Sjbeck {
694d4660949Sjbeck 	tls_rd_tmo = rd_tmo;
695d4660949Sjbeck }
696d4660949Sjbeck 
6977c478bd9Sstevel@tonic-gate /*
6987c478bd9Sstevel@tonic-gate **  TLS_READ -- read secured information for the caller
6997c478bd9Sstevel@tonic-gate **
7007c478bd9Sstevel@tonic-gate **	Parameters:
7017c478bd9Sstevel@tonic-gate **		fp -- the file pointer
7027c478bd9Sstevel@tonic-gate **		buf -- the location to place the data
7037c478bd9Sstevel@tonic-gate **		size -- the number of bytes to read from connection
7047c478bd9Sstevel@tonic-gate **
7057c478bd9Sstevel@tonic-gate **	Results:
7067c478bd9Sstevel@tonic-gate **		-1 on error
7077c478bd9Sstevel@tonic-gate **		otherwise the number of bytes read
7087c478bd9Sstevel@tonic-gate */
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate static ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate static ssize_t
tls_read(fp,buf,size)7137c478bd9Sstevel@tonic-gate tls_read(fp, buf, size)
7147c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
7157c478bd9Sstevel@tonic-gate 	char *buf;
7167c478bd9Sstevel@tonic-gate 	size_t size;
7177c478bd9Sstevel@tonic-gate {
718445f2479Sjbeck 	int r, rfd, wfd, try, ssl_err;
7197c478bd9Sstevel@tonic-gate 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
720445f2479Sjbeck 	time_t tlsstart;
7217c478bd9Sstevel@tonic-gate 	char *err;
7227c478bd9Sstevel@tonic-gate 
723445f2479Sjbeck 	try = 99;
724445f2479Sjbeck 	err = NULL;
725445f2479Sjbeck 	tlsstart = curtime();
726445f2479Sjbeck 
727445f2479Sjbeck   retry:
7287c478bd9Sstevel@tonic-gate 	r = SSL_read(so->con, (char *) buf, size);
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	if (r > 0)
7317c478bd9Sstevel@tonic-gate 		return r;
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	err = NULL;
734445f2479Sjbeck 	switch (ssl_err = SSL_get_error(so->con, r))
7357c478bd9Sstevel@tonic-gate 	{
7367c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_NONE:
7377c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_ZERO_RETURN:
7387c478bd9Sstevel@tonic-gate 		break;
7397c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_WRITE:
740445f2479Sjbeck 		err = "read W BLOCK";
741445f2479Sjbeck 		/* FALLTHROUGH */
7427c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_READ:
743445f2479Sjbeck 		if (err == NULL)
7447c478bd9Sstevel@tonic-gate 			err = "read R BLOCK";
745445f2479Sjbeck 		rfd = SSL_get_rfd(so->con);
746445f2479Sjbeck 		wfd = SSL_get_wfd(so->con);
747445f2479Sjbeck 		try = tls_retry(so->con, rfd, wfd, tlsstart,
748d4660949Sjbeck 				(tls_rd_tmo < 0) ? TimeOuts.to_datablock
749d4660949Sjbeck 						 : tls_rd_tmo,
750d4660949Sjbeck 				ssl_err, "read");
751445f2479Sjbeck 		if (try > 0)
752445f2479Sjbeck 			goto retry;
753445f2479Sjbeck 		errno = SM_ERR_TIMEOUT;
7547c478bd9Sstevel@tonic-gate 		break;
755445f2479Sjbeck 
7567c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_X509_LOOKUP:
7577c478bd9Sstevel@tonic-gate 		err = "write X BLOCK";
7587c478bd9Sstevel@tonic-gate 		break;
7597c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_SYSCALL:
7607c478bd9Sstevel@tonic-gate 		if (r == 0 && errno == 0) /* out of protocol EOF found */
7617c478bd9Sstevel@tonic-gate 			break;
7627c478bd9Sstevel@tonic-gate 		err = "syscall error";
7637c478bd9Sstevel@tonic-gate /*
7647c478bd9Sstevel@tonic-gate 		get_last_socket_error());
7657c478bd9Sstevel@tonic-gate */
7667c478bd9Sstevel@tonic-gate 		break;
7677c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_SSL:
7687c478bd9Sstevel@tonic-gate #if DEAL_WITH_ERROR_SSL
7697c478bd9Sstevel@tonic-gate 		if (r == 0 && errno == 0) /* out of protocol EOF found */
7707c478bd9Sstevel@tonic-gate 			break;
7717c478bd9Sstevel@tonic-gate #endif /* DEAL_WITH_ERROR_SSL */
7727c478bd9Sstevel@tonic-gate 		err = "generic SSL error";
7737c478bd9Sstevel@tonic-gate 		if (LogLevel > 9)
7747c478bd9Sstevel@tonic-gate 			tlslogerr("read");
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate #if DEAL_WITH_ERROR_SSL
7777c478bd9Sstevel@tonic-gate 		/* avoid repeated calls? */
7787c478bd9Sstevel@tonic-gate 		if (r == 0)
7797c478bd9Sstevel@tonic-gate 			r = -1;
7807c478bd9Sstevel@tonic-gate #endif /* DEAL_WITH_ERROR_SSL */
7817c478bd9Sstevel@tonic-gate 		break;
7827c478bd9Sstevel@tonic-gate 	}
7837c478bd9Sstevel@tonic-gate 	if (err != NULL)
7847c478bd9Sstevel@tonic-gate 	{
7857c478bd9Sstevel@tonic-gate 		int save_errno;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EIO : errno;
788445f2479Sjbeck 		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
789445f2479Sjbeck 		{
790445f2479Sjbeck 			if (LogLevel > 7)
791445f2479Sjbeck 				sm_syslog(LOG_WARNING, NOQID,
792445f2479Sjbeck 					  "STARTTLS: read error=timeout");
793445f2479Sjbeck 		}
794445f2479Sjbeck 		else if (LogLevel > 8)
7957c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
796445f2479Sjbeck 				  "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
7977c478bd9Sstevel@tonic-gate 				  err, r, errno,
798445f2479Sjbeck 				  ERR_error_string(ERR_get_error(), NULL), try,
799445f2479Sjbeck 				  ssl_err);
8007c478bd9Sstevel@tonic-gate 		else if (LogLevel > 7)
8017c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
802445f2479Sjbeck 				  "STARTTLS: read error=%s (%d), retry=%d, ssl_err=%d",
803445f2479Sjbeck 				  err, r, errno, try, ssl_err);
8047c478bd9Sstevel@tonic-gate 		errno = save_errno;
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 	return r;
8077c478bd9Sstevel@tonic-gate }
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*
8107c478bd9Sstevel@tonic-gate **  TLS_WRITE -- write information out through secure connection
8117c478bd9Sstevel@tonic-gate **
8127c478bd9Sstevel@tonic-gate **	Parameters:
8137c478bd9Sstevel@tonic-gate **		fp -- the file pointer
8147c478bd9Sstevel@tonic-gate **		buf -- holds the data to be securely written
8157c478bd9Sstevel@tonic-gate **		size -- the number of bytes to write
8167c478bd9Sstevel@tonic-gate **
8177c478bd9Sstevel@tonic-gate **	Returns:
8187c478bd9Sstevel@tonic-gate **		-1 on error
8197c478bd9Sstevel@tonic-gate **		otherwise number of bytes written
8207c478bd9Sstevel@tonic-gate */
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate static ssize_t
tls_write(fp,buf,size)8257c478bd9Sstevel@tonic-gate tls_write(fp, buf, size)
8267c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
8277c478bd9Sstevel@tonic-gate 	const char *buf;
8287c478bd9Sstevel@tonic-gate 	size_t size;
8297c478bd9Sstevel@tonic-gate {
830445f2479Sjbeck 	int r, rfd, wfd, try, ssl_err;
8317c478bd9Sstevel@tonic-gate 	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
832445f2479Sjbeck 	time_t tlsstart;
8337c478bd9Sstevel@tonic-gate 	char *err;
8347c478bd9Sstevel@tonic-gate 
835445f2479Sjbeck 	try = 99;
836445f2479Sjbeck 	err = NULL;
837445f2479Sjbeck 	tlsstart = curtime();
838445f2479Sjbeck 
839445f2479Sjbeck   retry:
8407c478bd9Sstevel@tonic-gate 	r = SSL_write(so->con, (char *) buf, size);
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	if (r > 0)
8437c478bd9Sstevel@tonic-gate 		return r;
8447c478bd9Sstevel@tonic-gate 	err = NULL;
845445f2479Sjbeck 	switch (ssl_err = SSL_get_error(so->con, r))
8467c478bd9Sstevel@tonic-gate 	{
8477c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_NONE:
8487c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_ZERO_RETURN:
8497c478bd9Sstevel@tonic-gate 		break;
8507c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_WRITE:
851445f2479Sjbeck 		err = "read W BLOCK";
852445f2479Sjbeck 		/* FALLTHROUGH */
8537c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_READ:
854445f2479Sjbeck 		if (err == NULL)
855445f2479Sjbeck 			err = "read R BLOCK";
856445f2479Sjbeck 		rfd = SSL_get_rfd(so->con);
857445f2479Sjbeck 		wfd = SSL_get_wfd(so->con);
858445f2479Sjbeck 		try = tls_retry(so->con, rfd, wfd, tlsstart,
859445f2479Sjbeck 				DATA_PROGRESS_TIMEOUT, ssl_err, "write");
860445f2479Sjbeck 		if (try > 0)
861445f2479Sjbeck 			goto retry;
862445f2479Sjbeck 		errno = SM_ERR_TIMEOUT;
8637c478bd9Sstevel@tonic-gate 		break;
8647c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_WANT_X509_LOOKUP:
8657c478bd9Sstevel@tonic-gate 		err = "write X BLOCK";
8667c478bd9Sstevel@tonic-gate 		break;
8677c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_SYSCALL:
8687c478bd9Sstevel@tonic-gate 		if (r == 0 && errno == 0) /* out of protocol EOF found */
8697c478bd9Sstevel@tonic-gate 			break;
8707c478bd9Sstevel@tonic-gate 		err = "syscall error";
8717c478bd9Sstevel@tonic-gate /*
8727c478bd9Sstevel@tonic-gate 		get_last_socket_error());
8737c478bd9Sstevel@tonic-gate */
8747c478bd9Sstevel@tonic-gate 		break;
8757c478bd9Sstevel@tonic-gate 	  case SSL_ERROR_SSL:
8767c478bd9Sstevel@tonic-gate 		err = "generic SSL error";
8777c478bd9Sstevel@tonic-gate /*
8787c478bd9Sstevel@tonic-gate 		ERR_GET_REASON(ERR_peek_error()));
8797c478bd9Sstevel@tonic-gate */
8807c478bd9Sstevel@tonic-gate 		if (LogLevel > 9)
8817c478bd9Sstevel@tonic-gate 			tlslogerr("write");
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate #if DEAL_WITH_ERROR_SSL
8847c478bd9Sstevel@tonic-gate 		/* avoid repeated calls? */
8857c478bd9Sstevel@tonic-gate 		if (r == 0)
8867c478bd9Sstevel@tonic-gate 			r = -1;
8877c478bd9Sstevel@tonic-gate #endif /* DEAL_WITH_ERROR_SSL */
8887c478bd9Sstevel@tonic-gate 		break;
8897c478bd9Sstevel@tonic-gate 	}
8907c478bd9Sstevel@tonic-gate 	if (err != NULL)
8917c478bd9Sstevel@tonic-gate 	{
8927c478bd9Sstevel@tonic-gate 		int save_errno;
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EIO : errno;
895445f2479Sjbeck 		if (try == 0 && save_errno == SM_ERR_TIMEOUT)
896445f2479Sjbeck 		{
897445f2479Sjbeck 			if (LogLevel > 7)
898445f2479Sjbeck 				sm_syslog(LOG_WARNING, NOQID,
899445f2479Sjbeck 					  "STARTTLS: write error=timeout");
900445f2479Sjbeck 		}
901445f2479Sjbeck 		else if (LogLevel > 8)
9027c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
903445f2479Sjbeck 				  "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
9047c478bd9Sstevel@tonic-gate 				  err, r, errno,
905445f2479Sjbeck 				  ERR_error_string(ERR_get_error(), NULL), try,
906445f2479Sjbeck 				  ssl_err);
9077c478bd9Sstevel@tonic-gate 		else if (LogLevel > 7)
9087c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
909445f2479Sjbeck 				  "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
910445f2479Sjbeck 				  err, r, errno, try, ssl_err);
9117c478bd9Sstevel@tonic-gate 		errno = save_errno;
9127c478bd9Sstevel@tonic-gate 	}
9137c478bd9Sstevel@tonic-gate 	return r;
9147c478bd9Sstevel@tonic-gate }
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate /*
9177c478bd9Sstevel@tonic-gate **  SFDCTLS -- create tls file type and open in and out file pointers
9187c478bd9Sstevel@tonic-gate **	      for sendmail to read from and write to.
9197c478bd9Sstevel@tonic-gate **
9207c478bd9Sstevel@tonic-gate **	Parameters:
9217c478bd9Sstevel@tonic-gate **		fin -- data input source being replaced
9227c478bd9Sstevel@tonic-gate **		fout -- data output source being replaced
9237c478bd9Sstevel@tonic-gate **		con -- the tls connection pointer
9247c478bd9Sstevel@tonic-gate **
9257c478bd9Sstevel@tonic-gate **	Returns:
9267c478bd9Sstevel@tonic-gate **		-1 on error
9277c478bd9Sstevel@tonic-gate **		0 on success
9287c478bd9Sstevel@tonic-gate **
9297c478bd9Sstevel@tonic-gate **	Side effects:
9307c478bd9Sstevel@tonic-gate **		The arguments "fin" and "fout" are replaced with the new
9317c478bd9Sstevel@tonic-gate **		SM_FILE_T pointers.
9327c478bd9Sstevel@tonic-gate **		The original "fin" and "fout" are preserved in the tls file
9337c478bd9Sstevel@tonic-gate **		type but are not actually used because of the design of TLS.
9347c478bd9Sstevel@tonic-gate */
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate int
sfdctls(fin,fout,con)9377c478bd9Sstevel@tonic-gate sfdctls(fin, fout, con)
9387c478bd9Sstevel@tonic-gate 	SM_FILE_T **fin;
9397c478bd9Sstevel@tonic-gate 	SM_FILE_T **fout;
9407c478bd9Sstevel@tonic-gate 	SSL *con;
9417c478bd9Sstevel@tonic-gate {
9427c478bd9Sstevel@tonic-gate 	SM_FILE_T *tlsin, *tlsout;
9437c478bd9Sstevel@tonic-gate 	SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
9447c478bd9Sstevel@tonic-gate 		tls_read, tls_write, NULL, tls_getinfo, NULL,
9457c478bd9Sstevel@tonic-gate 		SM_TIME_FOREVER);
9467c478bd9Sstevel@tonic-gate 	struct tls_info info;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	SM_ASSERT(con != NULL);
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
9517c478bd9Sstevel@tonic-gate 		tls_read, tls_write, NULL, tls_getinfo, NULL,
9527c478bd9Sstevel@tonic-gate 		SM_TIME_FOREVER);
9537c478bd9Sstevel@tonic-gate 	info.fp = *fin;
9547c478bd9Sstevel@tonic-gate 	info.con = con;
9557c478bd9Sstevel@tonic-gate 	tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
9567c478bd9Sstevel@tonic-gate 			   NULL);
9577c478bd9Sstevel@tonic-gate 	if (tlsin == NULL)
9587c478bd9Sstevel@tonic-gate 		return -1;
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	info.fp = *fout;
9617c478bd9Sstevel@tonic-gate 	tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
9627c478bd9Sstevel@tonic-gate 			    NULL);
9637c478bd9Sstevel@tonic-gate 	if (tlsout == NULL)
9647c478bd9Sstevel@tonic-gate 	{
9657c478bd9Sstevel@tonic-gate 		(void) sm_io_close(tlsin, SM_TIME_DEFAULT);
9667c478bd9Sstevel@tonic-gate 		return -1;
9677c478bd9Sstevel@tonic-gate 	}
9687c478bd9Sstevel@tonic-gate 	sm_io_automode(tlsin, tlsout);
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	*fin = tlsin;
9717c478bd9Sstevel@tonic-gate 	*fout = tlsout;
9727c478bd9Sstevel@tonic-gate 	return 0;
9737c478bd9Sstevel@tonic-gate }
9747c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
975