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