1N/A/*
1N/A * Copyright (c) 1999-2006, 2008 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A */
1N/A
1N/A#include <sm/gen.h>
1N/ASM_RCSID("@(#)$Id: sfsasl.c,v 8.118 2008/07/22 15:12:48 ca Exp $")
1N/A#include <stdlib.h>
1N/A#include <sendmail.h>
1N/A#include <sm/time.h>
1N/A#include <errno.h>
1N/A
1N/A/* allow to disable error handling code just in case... */
1N/A#ifndef DEAL_WITH_ERROR_SSL
1N/A# define DEAL_WITH_ERROR_SSL 1
1N/A#endif /* ! DEAL_WITH_ERROR_SSL */
1N/A
1N/A#if SASL
1N/A# include "sfsasl.h"
1N/A
1N/A/* Structure used by the "sasl" file type */
1N/Astruct sasl_obj
1N/A{
1N/A SM_FILE_T *fp;
1N/A sasl_conn_t *conn;
1N/A};
1N/A
1N/Astruct sasl_info
1N/A{
1N/A SM_FILE_T *fp;
1N/A sasl_conn_t *conn;
1N/A};
1N/A
1N/A/*
1N/A** SASL_GETINFO - returns requested information about a "sasl" file
1N/A** descriptor.
1N/A**
1N/A** Parameters:
1N/A** fp -- the file descriptor
1N/A** what -- the type of information requested
1N/A** valp -- the thang to return the information in
1N/A**
1N/A** Returns:
1N/A** -1 for unknown requests
1N/A** >=0 on success with valp filled in (if possible).
1N/A*/
1N/A
1N/Astatic int sasl_getinfo __P((SM_FILE_T *, int, void *));
1N/A
1N/Astatic int
1N/Asasl_getinfo(fp, what, valp)
1N/A SM_FILE_T *fp;
1N/A int what;
1N/A void *valp;
1N/A{
1N/A struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
1N/A
1N/A switch (what)
1N/A {
1N/A case SM_IO_WHAT_FD:
1N/A if (so->fp == NULL)
1N/A return -1;
1N/A return so->fp->f_file; /* for stdio fileno() compatability */
1N/A
1N/A case SM_IO_IS_READABLE:
1N/A if (so->fp == NULL)
1N/A return 0;
1N/A
1N/A /* get info from underlying file */
1N/A return sm_io_getinfo(so->fp, what, valp);
1N/A
1N/A default:
1N/A return -1;
1N/A }
1N/A}
1N/A
1N/A/*
1N/A** SASL_OPEN -- creates the sasl specific information for opening a
1N/A** file of the sasl type.
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer associated with the new open
1N/A** info -- contains the sasl connection information pointer and
1N/A** the original SM_FILE_T that holds the open
1N/A** flags -- ignored
1N/A** rpool -- ignored
1N/A**
1N/A** Returns:
1N/A** 0 on success
1N/A*/
1N/A
1N/Astatic int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
1N/A
1N/A/* ARGSUSED2 */
1N/Astatic int
1N/Asasl_open(fp, info, flags, rpool)
1N/A SM_FILE_T *fp;
1N/A const void *info;
1N/A int flags;
1N/A const void *rpool;
1N/A{
1N/A struct sasl_obj *so;
1N/A struct sasl_info *si = (struct sasl_info *) info;
1N/A
1N/A so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
1N/A if (so == NULL)
1N/A {
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A so->fp = si->fp;
1N/A so->conn = si->conn;
1N/A
1N/A /*
1N/A ** The underlying 'fp' is set to SM_IO_NOW so that the entire
1N/A ** encoded string is written in one chunk. Otherwise there is
1N/A ** the possibility that it may appear illegal, bogus or
1N/A ** mangled to the other side of the connection.
1N/A ** We will read or write through 'fp' since it is the opaque
1N/A ** connection for the communications. We need to treat it this
1N/A ** way in case the encoded string is to be sent down a TLS
1N/A ** connection rather than, say, sm_io's stdio.
1N/A */
1N/A
1N/A (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
1N/A fp->f_cookie = so;
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer to close
1N/A**
1N/A** Returns:
1N/A** 0 on success
1N/A*/
1N/A
1N/Astatic int sasl_close __P((SM_FILE_T *));
1N/A
1N/Astatic int
1N/Asasl_close(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A struct sasl_obj *so;
1N/A
1N/A so = (struct sasl_obj *) fp->f_cookie;
1N/A if (so == NULL)
1N/A return 0;
1N/A if (so->fp != NULL)
1N/A {
1N/A sm_io_close(so->fp, SM_TIME_DEFAULT);
1N/A so->fp = NULL;
1N/A }
1N/A sm_free(so);
1N/A so = NULL;
1N/A return 0;
1N/A}
1N/A
1N/A/* how to deallocate a buffer allocated by SASL */
1N/Aextern void sm_sasl_free __P((void *));
1N/A# define SASL_DEALLOC(b) sm_sasl_free(b)
1N/A
1N/A/*
1N/A** SASL_READ -- read encrypted information and decrypt it for the caller
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer
1N/A** buf -- the location to place the decrypted information
1N/A** size -- the number of bytes to read after decryption
1N/A**
1N/A** Results:
1N/A** -1 on error
1N/A** otherwise the number of bytes read
1N/A*/
1N/A
1N/Astatic ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
1N/A
1N/Astatic ssize_t
1N/Asasl_read(fp, buf, size)
1N/A SM_FILE_T *fp;
1N/A char *buf;
1N/A size_t size;
1N/A{
1N/A int result;
1N/A ssize_t len;
1N/A# if SASL >= 20000
1N/A static const char *outbuf = NULL;
1N/A# else /* SASL >= 20000 */
1N/A static char *outbuf = NULL;
1N/A# endif /* SASL >= 20000 */
1N/A static unsigned int outlen = 0;
1N/A static unsigned int offset = 0;
1N/A struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
1N/A
1N/A /*
1N/A ** sasl_decode() may require more data than a single read() returns.
1N/A ** Hence we have to put a loop around the decoding.
1N/A ** This also requires that we may have to split up the returned
1N/A ** data since it might be larger than the allowed size.
1N/A ** Therefore we use a static pointer and return portions of it
1N/A ** if necessary.
1N/A ** XXX Note: This function is not thread-safe nor can it be used
1N/A ** on more than one file. A correct implementation would store
1N/A ** this data in fp->f_cookie.
1N/A */
1N/A
1N/A# if SASL >= 20000
1N/A while (outlen == 0)
1N/A# else /* SASL >= 20000 */
1N/A while (outbuf == NULL && outlen == 0)
1N/A# endif /* SASL >= 20000 */
1N/A {
1N/A len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
1N/A if (len <= 0)
1N/A return len;
1N/A result = sasl_decode(so->conn, buf,
1N/A (unsigned int) len, &outbuf, &outlen);
1N/A if (result != SASL_OK)
1N/A {
1N/A if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "AUTH: sasl_decode error=%d", result);
1N/A outbuf = NULL;
1N/A offset = 0;
1N/A outlen = 0;
1N/A return -1;
1N/A }
1N/A }
1N/A
1N/A if (outbuf == NULL)
1N/A {
1N/A /* be paranoid: outbuf == NULL but outlen != 0 */
1N/A syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
1N/A /* NOTREACHED */
1N/A }
1N/A if (outlen - offset > size)
1N/A {
1N/A /* return another part of the buffer */
1N/A (void) memcpy(buf, outbuf + offset, size);
1N/A offset += size;
1N/A len = size;
1N/A }
1N/A else
1N/A {
1N/A /* return the rest of the buffer */
1N/A len = outlen - offset;
1N/A (void) memcpy(buf, outbuf + offset, (size_t) len);
1N/A# if SASL < 20000
1N/A SASL_DEALLOC(outbuf);
1N/A# endif /* SASL < 20000 */
1N/A outbuf = NULL;
1N/A offset = 0;
1N/A outlen = 0;
1N/A }
1N/A return len;
1N/A}
1N/A
1N/A/*
1N/A** SASL_WRITE -- write information out after encrypting it
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer
1N/A** buf -- holds the data to be encrypted and written
1N/A** size -- the number of bytes to have encrypted and written
1N/A**
1N/A** Returns:
1N/A** -1 on error
1N/A** otherwise number of bytes written
1N/A*/
1N/A
1N/Astatic ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
1N/A
1N/Astatic ssize_t
1N/Asasl_write(fp, buf, size)
1N/A SM_FILE_T *fp;
1N/A const char *buf;
1N/A size_t size;
1N/A{
1N/A int result;
1N/A# if SASL >= 20000
1N/A const char *outbuf;
1N/A# else /* SASL >= 20000 */
1N/A char *outbuf;
1N/A# endif /* SASL >= 20000 */
1N/A unsigned int outlen, *maxencode;
1N/A size_t ret = 0, total = 0;
1N/A struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
1N/A
1N/A /*
1N/A ** Fetch the maximum input buffer size for sasl_encode().
1N/A ** This can be less than the size set in attemptauth()
1N/A ** due to a negotiation with the other side, e.g.,
1N/A ** Cyrus IMAP lmtp program sets maxbuf=4096,
1N/A ** digestmd5 substracts 25 and hence we'll get 4071
1N/A ** instead of 8192 (MAXOUTLEN).
1N/A ** Hack (for now): simply reduce the size, callers are (must be)
1N/A ** able to deal with that and invoke sasl_write() again with
1N/A ** the rest of the data.
1N/A ** Note: it would be better to store this value in the context
1N/A ** after the negotiation.
1N/A */
1N/A
1N/A result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
1N/A (const void **) &maxencode);
1N/A if (result == SASL_OK && size > *maxencode && *maxencode > 0)
1N/A size = *maxencode;
1N/A
1N/A result = sasl_encode(so->conn, buf,
1N/A (unsigned int) size, &outbuf, &outlen);
1N/A
1N/A if (result != SASL_OK)
1N/A {
1N/A if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "AUTH: sasl_encode error=%d", result);
1N/A return -1;
1N/A }
1N/A
1N/A if (outbuf != NULL)
1N/A {
1N/A while (outlen > 0)
1N/A {
1N/A errno = 0;
1N/A /* XXX result == 0? */
1N/A ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
1N/A &outbuf[total], outlen);
1N/A if (ret <= 0)
1N/A return ret;
1N/A outlen -= ret;
1N/A total += ret;
1N/A }
1N/A# if SASL < 20000
1N/A SASL_DEALLOC(outbuf);
1N/A# endif /* SASL < 20000 */
1N/A }
1N/A return size;
1N/A}
1N/A
1N/A/*
1N/A** SFDCSASL -- create sasl file type and open in and out file pointers
1N/A** for sendmail to read from and write to.
1N/A**
1N/A** Parameters:
1N/A** fin -- the sm_io file encrypted data to be read from
1N/A** fout -- the sm_io file encrypted data to be written to
1N/A** conn -- the sasl connection pointer
1N/A** tmo -- timeout
1N/A**
1N/A** Returns:
1N/A** -1 on error
1N/A** 0 on success
1N/A**
1N/A** Side effects:
1N/A** The arguments "fin" and "fout" are replaced with the new
1N/A** SM_FILE_T pointers.
1N/A*/
1N/A
1N/Aint
1N/Asfdcsasl(fin, fout, conn, tmo)
1N/A SM_FILE_T **fin;
1N/A SM_FILE_T **fout;
1N/A sasl_conn_t *conn;
1N/A int tmo;
1N/A{
1N/A SM_FILE_T *newin, *newout;
1N/A SM_FILE_T SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
1N/A sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
1N/A SM_TIME_DEFAULT);
1N/A struct sasl_info info;
1N/A
1N/A if (conn == NULL)
1N/A {
1N/A /* no need to do anything */
1N/A return 0;
1N/A }
1N/A
1N/A SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
1N/A sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
1N/A SM_TIME_DEFAULT);
1N/A info.fp = *fin;
1N/A info.conn = conn;
1N/A newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
1N/A SM_IO_RDONLY_B, NULL);
1N/A
1N/A if (newin == NULL)
1N/A return -1;
1N/A
1N/A info.fp = *fout;
1N/A info.conn = conn;
1N/A newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
1N/A SM_IO_WRONLY_B, NULL);
1N/A
1N/A if (newout == NULL)
1N/A {
1N/A (void) sm_io_close(newin, SM_TIME_DEFAULT);
1N/A return -1;
1N/A }
1N/A sm_io_automode(newin, newout);
1N/A
1N/A sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
1N/A sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
1N/A
1N/A *fin = newin;
1N/A *fout = newout;
1N/A return 0;
1N/A}
1N/A#endif /* SASL */
1N/A
1N/A#if STARTTLS
1N/A# include "sfsasl.h"
1N/A# include <openssl/err.h>
1N/A
1N/A/* Structure used by the "tls" file type */
1N/Astruct tls_obj
1N/A{
1N/A SM_FILE_T *fp;
1N/A SSL *con;
1N/A};
1N/A
1N/Astruct tls_info
1N/A{
1N/A SM_FILE_T *fp;
1N/A SSL *con;
1N/A};
1N/A
1N/A/*
1N/A** TLS_GETINFO - returns requested information about a "tls" file
1N/A** descriptor.
1N/A**
1N/A** Parameters:
1N/A** fp -- the file descriptor
1N/A** what -- the type of information requested
1N/A** valp -- the thang to return the information in (unused)
1N/A**
1N/A** Returns:
1N/A** -1 for unknown requests
1N/A** >=0 on success with valp filled in (if possible).
1N/A*/
1N/A
1N/Astatic int tls_getinfo __P((SM_FILE_T *, int, void *));
1N/A
1N/A/* ARGSUSED2 */
1N/Astatic int
1N/Atls_getinfo(fp, what, valp)
1N/A SM_FILE_T *fp;
1N/A int what;
1N/A void *valp;
1N/A{
1N/A struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
1N/A
1N/A switch (what)
1N/A {
1N/A case SM_IO_WHAT_FD:
1N/A if (so->fp == NULL)
1N/A return -1;
1N/A return so->fp->f_file; /* for stdio fileno() compatability */
1N/A
1N/A case SM_IO_IS_READABLE:
1N/A return SSL_pending(so->con) > 0;
1N/A
1N/A default:
1N/A return -1;
1N/A }
1N/A}
1N/A
1N/A/*
1N/A** TLS_OPEN -- creates the tls specific information for opening a
1N/A** file of the tls type.
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer associated with the new open
1N/A** info -- the sm_io file pointer holding the open and the
1N/A** TLS encryption connection to be read from or written to
1N/A** flags -- ignored
1N/A** rpool -- ignored
1N/A**
1N/A** Returns:
1N/A** 0 on success
1N/A*/
1N/A
1N/Astatic int tls_open __P((SM_FILE_T *, const void *, int, const void *));
1N/A
1N/A/* ARGSUSED2 */
1N/Astatic int
1N/Atls_open(fp, info, flags, rpool)
1N/A SM_FILE_T *fp;
1N/A const void *info;
1N/A int flags;
1N/A const void *rpool;
1N/A{
1N/A struct tls_obj *so;
1N/A struct tls_info *ti = (struct tls_info *) info;
1N/A
1N/A so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
1N/A if (so == NULL)
1N/A {
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A so->fp = ti->fp;
1N/A so->con = ti->con;
1N/A
1N/A /*
1N/A ** We try to get the "raw" file descriptor that TLS uses to
1N/A ** do the actual read/write with. This is to allow us control
1N/A ** over the file descriptor being a blocking or non-blocking type.
1N/A ** Under the covers TLS handles the change and this allows us
1N/A ** to do timeouts with sm_io.
1N/A */
1N/A
1N/A fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
1N/A (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
1N/A fp->f_cookie = so;
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** TLS_CLOSE -- close the tls specific parts of the tls file pointer
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer to close
1N/A**
1N/A** Returns:
1N/A** 0 on success
1N/A*/
1N/A
1N/Astatic int tls_close __P((SM_FILE_T *));
1N/A
1N/Astatic int
1N/Atls_close(fp)
1N/A SM_FILE_T *fp;
1N/A{
1N/A struct tls_obj *so;
1N/A
1N/A so = (struct tls_obj *) fp->f_cookie;
1N/A if (so == NULL)
1N/A return 0;
1N/A if (so->fp != NULL)
1N/A {
1N/A sm_io_close(so->fp, SM_TIME_DEFAULT);
1N/A so->fp = NULL;
1N/A }
1N/A sm_free(so);
1N/A so = NULL;
1N/A return 0;
1N/A}
1N/A
1N/A/* maximum number of retries for TLS related I/O due to handshakes */
1N/A# define MAX_TLS_IOS 4
1N/A
1N/A/*
1N/A** TLS_RETRY -- check whether a failed SSL operation can be retried
1N/A**
1N/A** Parameters:
1N/A** ssl -- TLS structure
1N/A** rfd -- read fd
1N/A** wfd -- write fd
1N/A** tlsstart -- start time of TLS operation
1N/A** timeout -- timeout for TLS operation
1N/A** err -- SSL error
1N/A** where -- description of operation
1N/A**
1N/A** Results:
1N/A** >0 on success
1N/A** 0 on timeout
1N/A** <0 on error
1N/A*/
1N/A
1N/Aint
1N/Atls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
1N/A SSL *ssl;
1N/A int rfd;
1N/A int wfd;
1N/A time_t tlsstart;
1N/A int timeout;
1N/A int err;
1N/A const char *where;
1N/A{
1N/A int ret;
1N/A time_t left;
1N/A time_t now = curtime();
1N/A struct timeval tv;
1N/A
1N/A ret = -1;
1N/A
1N/A /*
1N/A ** For SSL_ERROR_WANT_{READ,WRITE}:
1N/A ** There is not a complete SSL record available yet
1N/A ** or there is only a partial SSL record removed from
1N/A ** the network (socket) buffer into the SSL buffer.
1N/A ** The SSL_connect will only succeed when a full
1N/A ** SSL record is available (assuming a "real" error
1N/A ** doesn't happen). To handle when a "real" error
1N/A ** does happen the select is set for exceptions too.
1N/A ** The connection may be re-negotiated during this time
1N/A ** so both read and write "want errors" need to be handled.
1N/A ** A select() exception loops back so that a proper SSL
1N/A ** error message can be gotten.
1N/A */
1N/A
1N/A left = timeout - (now - tlsstart);
1N/A if (left <= 0)
1N/A return 0; /* timeout */
1N/A tv.tv_sec = left;
1N/A tv.tv_usec = 0;
1N/A
1N/A if (LogLevel > 14)
1N/A {
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "STARTTLS=%s, info: fds=%d/%d, err=%d",
1N/A where, rfd, wfd, err);
1N/A }
1N/A
1N/A if (FD_SETSIZE > 0 &&
1N/A ((err == SSL_ERROR_WANT_READ && rfd >= FD_SETSIZE) ||
1N/A (err == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
1N/A {
1N/A if (LogLevel > 5)
1N/A {
1N/A sm_syslog(LOG_ERR, NOQID,
1N/A "STARTTLS=%s, error: fd %d/%d too large",
1N/A where, rfd, wfd);
1N/A if (LogLevel > 8)
1N/A tlslogerr(where);
1N/A }
1N/A errno = EINVAL;
1N/A }
1N/A else if (err == SSL_ERROR_WANT_READ)
1N/A {
1N/A fd_set ssl_maskr, ssl_maskx;
1N/A
1N/A FD_ZERO(&ssl_maskr);
1N/A FD_SET(rfd, &ssl_maskr);
1N/A FD_ZERO(&ssl_maskx);
1N/A FD_SET(rfd, &ssl_maskx);
1N/A do
1N/A {
1N/A ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
1N/A &tv);
1N/A } while (ret < 0 && errno == EINTR);
1N/A if (ret < 0 && errno > 0)
1N/A ret = -errno;
1N/A }
1N/A else if (err == SSL_ERROR_WANT_WRITE)
1N/A {
1N/A fd_set ssl_maskw, ssl_maskx;
1N/A
1N/A FD_ZERO(&ssl_maskw);
1N/A FD_SET(wfd, &ssl_maskw);
1N/A FD_ZERO(&ssl_maskx);
1N/A FD_SET(rfd, &ssl_maskx);
1N/A do
1N/A {
1N/A ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
1N/A &tv);
1N/A } while (ret < 0 && errno == EINTR);
1N/A if (ret < 0 && errno > 0)
1N/A ret = -errno;
1N/A }
1N/A return ret;
1N/A}
1N/A
1N/A/* errno to force refill() etc to stop (see IS_IO_ERROR()) */
1N/A#ifdef ETIMEDOUT
1N/A# define SM_ERR_TIMEOUT ETIMEDOUT
1N/A#else /* ETIMEDOUT */
1N/A# define SM_ERR_TIMEOUT EIO
1N/A#endif /* ETIMEDOUT */
1N/A
1N/A/*
1N/A** SET_TLS_RD_TMO -- read secured information for the caller
1N/A**
1N/A** Parameters:
1N/A** rd_tmo -- read timeout
1N/A**
1N/A** Results:
1N/A** none
1N/A** This is a hack: there is no way to pass it in
1N/A*/
1N/A
1N/Astatic int tls_rd_tmo = -1;
1N/A
1N/Avoid
1N/Aset_tls_rd_tmo(rd_tmo)
1N/A int rd_tmo;
1N/A{
1N/A tls_rd_tmo = rd_tmo;
1N/A}
1N/A
1N/A/*
1N/A** TLS_READ -- read secured information for the caller
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer
1N/A** buf -- the location to place the data
1N/A** size -- the number of bytes to read from connection
1N/A**
1N/A** Results:
1N/A** -1 on error
1N/A** otherwise the number of bytes read
1N/A*/
1N/A
1N/Astatic ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
1N/A
1N/Astatic ssize_t
1N/Atls_read(fp, buf, size)
1N/A SM_FILE_T *fp;
1N/A char *buf;
1N/A size_t size;
1N/A{
1N/A int r, rfd, wfd, try, ssl_err;
1N/A struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
1N/A time_t tlsstart;
1N/A char *err;
1N/A
1N/A try = 99;
1N/A err = NULL;
1N/A tlsstart = curtime();
1N/A
1N/A retry:
1N/A r = SSL_read(so->con, (char *) buf, size);
1N/A
1N/A if (r > 0)
1N/A return r;
1N/A
1N/A err = NULL;
1N/A switch (ssl_err = SSL_get_error(so->con, r))
1N/A {
1N/A case SSL_ERROR_NONE:
1N/A case SSL_ERROR_ZERO_RETURN:
1N/A break;
1N/A case SSL_ERROR_WANT_WRITE:
1N/A err = "read W BLOCK";
1N/A /* FALLTHROUGH */
1N/A case SSL_ERROR_WANT_READ:
1N/A if (err == NULL)
1N/A err = "read R BLOCK";
1N/A rfd = SSL_get_rfd(so->con);
1N/A wfd = SSL_get_wfd(so->con);
1N/A try = tls_retry(so->con, rfd, wfd, tlsstart,
1N/A (tls_rd_tmo < 0) ? TimeOuts.to_datablock
1N/A : tls_rd_tmo,
1N/A ssl_err, "read");
1N/A if (try > 0)
1N/A goto retry;
1N/A errno = SM_ERR_TIMEOUT;
1N/A break;
1N/A
1N/A case SSL_ERROR_WANT_X509_LOOKUP:
1N/A err = "write X BLOCK";
1N/A break;
1N/A case SSL_ERROR_SYSCALL:
1N/A if (r == 0 && errno == 0) /* out of protocol EOF found */
1N/A break;
1N/A err = "syscall error";
1N/A/*
1N/A get_last_socket_error());
1N/A*/
1N/A break;
1N/A case SSL_ERROR_SSL:
1N/A#if DEAL_WITH_ERROR_SSL
1N/A if (r == 0 && errno == 0) /* out of protocol EOF found */
1N/A break;
1N/A#endif /* DEAL_WITH_ERROR_SSL */
1N/A err = "generic SSL error";
1N/A if (LogLevel > 9)
1N/A tlslogerr("read");
1N/A
1N/A#if DEAL_WITH_ERROR_SSL
1N/A /* avoid repeated calls? */
1N/A if (r == 0)
1N/A r = -1;
1N/A#endif /* DEAL_WITH_ERROR_SSL */
1N/A break;
1N/A }
1N/A if (err != NULL)
1N/A {
1N/A int save_errno;
1N/A
1N/A save_errno = (errno == 0) ? EIO : errno;
1N/A if (try == 0 && save_errno == SM_ERR_TIMEOUT)
1N/A {
1N/A if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: read error=timeout");
1N/A }
1N/A else if (LogLevel > 8)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
1N/A err, r, errno,
1N/A ERR_error_string(ERR_get_error(), NULL), try,
1N/A ssl_err);
1N/A else if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: read error=%s (%d), retry=%d, ssl_err=%d",
1N/A err, r, errno, try, ssl_err);
1N/A errno = save_errno;
1N/A }
1N/A return r;
1N/A}
1N/A
1N/A/*
1N/A** TLS_WRITE -- write information out through secure connection
1N/A**
1N/A** Parameters:
1N/A** fp -- the file pointer
1N/A** buf -- holds the data to be securely written
1N/A** size -- the number of bytes to write
1N/A**
1N/A** Returns:
1N/A** -1 on error
1N/A** otherwise number of bytes written
1N/A*/
1N/A
1N/Astatic ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
1N/A
1N/Astatic ssize_t
1N/Atls_write(fp, buf, size)
1N/A SM_FILE_T *fp;
1N/A const char *buf;
1N/A size_t size;
1N/A{
1N/A int r, rfd, wfd, try, ssl_err;
1N/A struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
1N/A time_t tlsstart;
1N/A char *err;
1N/A
1N/A try = 99;
1N/A err = NULL;
1N/A tlsstart = curtime();
1N/A
1N/A retry:
1N/A r = SSL_write(so->con, (char *) buf, size);
1N/A
1N/A if (r > 0)
1N/A return r;
1N/A err = NULL;
1N/A switch (ssl_err = SSL_get_error(so->con, r))
1N/A {
1N/A case SSL_ERROR_NONE:
1N/A case SSL_ERROR_ZERO_RETURN:
1N/A break;
1N/A case SSL_ERROR_WANT_WRITE:
1N/A err = "read W BLOCK";
1N/A /* FALLTHROUGH */
1N/A case SSL_ERROR_WANT_READ:
1N/A if (err == NULL)
1N/A err = "read R BLOCK";
1N/A rfd = SSL_get_rfd(so->con);
1N/A wfd = SSL_get_wfd(so->con);
1N/A try = tls_retry(so->con, rfd, wfd, tlsstart,
1N/A DATA_PROGRESS_TIMEOUT, ssl_err, "write");
1N/A if (try > 0)
1N/A goto retry;
1N/A errno = SM_ERR_TIMEOUT;
1N/A break;
1N/A case SSL_ERROR_WANT_X509_LOOKUP:
1N/A err = "write X BLOCK";
1N/A break;
1N/A case SSL_ERROR_SYSCALL:
1N/A if (r == 0 && errno == 0) /* out of protocol EOF found */
1N/A break;
1N/A err = "syscall error";
1N/A/*
1N/A get_last_socket_error());
1N/A*/
1N/A break;
1N/A case SSL_ERROR_SSL:
1N/A err = "generic SSL error";
1N/A/*
1N/A ERR_GET_REASON(ERR_peek_error()));
1N/A*/
1N/A if (LogLevel > 9)
1N/A tlslogerr("write");
1N/A
1N/A#if DEAL_WITH_ERROR_SSL
1N/A /* avoid repeated calls? */
1N/A if (r == 0)
1N/A r = -1;
1N/A#endif /* DEAL_WITH_ERROR_SSL */
1N/A break;
1N/A }
1N/A if (err != NULL)
1N/A {
1N/A int save_errno;
1N/A
1N/A save_errno = (errno == 0) ? EIO : errno;
1N/A if (try == 0 && save_errno == SM_ERR_TIMEOUT)
1N/A {
1N/A if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: write error=timeout");
1N/A }
1N/A else if (LogLevel > 8)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
1N/A err, r, errno,
1N/A ERR_error_string(ERR_get_error(), NULL), try,
1N/A ssl_err);
1N/A else if (LogLevel > 7)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
1N/A err, r, errno, try, ssl_err);
1N/A errno = save_errno;
1N/A }
1N/A return r;
1N/A}
1N/A
1N/A/*
1N/A** SFDCTLS -- create tls file type and open in and out file pointers
1N/A** for sendmail to read from and write to.
1N/A**
1N/A** Parameters:
1N/A** fin -- data input source being replaced
1N/A** fout -- data output source being replaced
1N/A** con -- the tls connection pointer
1N/A**
1N/A** Returns:
1N/A** -1 on error
1N/A** 0 on success
1N/A**
1N/A** Side effects:
1N/A** The arguments "fin" and "fout" are replaced with the new
1N/A** SM_FILE_T pointers.
1N/A** The original "fin" and "fout" are preserved in the tls file
1N/A** type but are not actually used because of the design of TLS.
1N/A*/
1N/A
1N/Aint
1N/Asfdctls(fin, fout, con)
1N/A SM_FILE_T **fin;
1N/A SM_FILE_T **fout;
1N/A SSL *con;
1N/A{
1N/A SM_FILE_T *tlsin, *tlsout;
1N/A SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
1N/A tls_read, tls_write, NULL, tls_getinfo, NULL,
1N/A SM_TIME_FOREVER);
1N/A struct tls_info info;
1N/A
1N/A SM_ASSERT(con != NULL);
1N/A
1N/A SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
1N/A tls_read, tls_write, NULL, tls_getinfo, NULL,
1N/A SM_TIME_FOREVER);
1N/A info.fp = *fin;
1N/A info.con = con;
1N/A tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
1N/A NULL);
1N/A if (tlsin == NULL)
1N/A return -1;
1N/A
1N/A info.fp = *fout;
1N/A tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
1N/A NULL);
1N/A if (tlsout == NULL)
1N/A {
1N/A (void) sm_io_close(tlsin, SM_TIME_DEFAULT);
1N/A return -1;
1N/A }
1N/A sm_io_automode(tlsin, tlsout);
1N/A
1N/A *fin = tlsin;
1N/A *fout = tlsout;
1N/A return 0;
1N/A}
1N/A#endif /* STARTTLS */