sfsasl.c revision 90792
1/*
2 * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: sfsasl.c,v 8.86 2001/09/11 04:05:16 gshapiro Exp $")
13#include <stdlib.h>
14#include <sendmail.h>
15#include <errno.h>
16#if SASL
17# include <sasl.h>
18# include "sfsasl.h"
19
20/* Structure used by the "sasl" file type */
21struct sasl_obj
22{
23	SM_FILE_T *fp;
24	sasl_conn_t *conn;
25};
26
27struct sasl_info
28{
29	SM_FILE_T *fp;
30	sasl_conn_t *conn;
31};
32
33/*
34**  SASL_GETINFO - returns requested information about a "sasl" file
35**		  descriptor.
36**
37**	Parameters:
38**		fp -- the file descriptor
39**		what -- the type of information requested
40**		valp -- the thang to return the information in
41**
42**	Returns:
43**		-1 for unknown requests
44**		>=0 on success with valp filled in (if possible).
45*/
46
47static int sasl_getinfo __P((SM_FILE_T *, int, void *));
48
49static int
50sasl_getinfo(fp, what, valp)
51	SM_FILE_T *fp;
52	int what;
53	void *valp;
54{
55	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
56
57	switch (what)
58	{
59	  case SM_IO_WHAT_FD:
60		if (so->fp == NULL)
61			return -1;
62		return so->fp->f_file; /* for stdio fileno() compatability */
63
64	  case SM_IO_IS_READABLE:
65		if (so->fp == NULL)
66			return 0;
67
68		/* get info from underlying file */
69		return sm_io_getinfo(so->fp, what, valp);
70
71	  default:
72		return -1;
73	}
74}
75
76/*
77**  SASL_OPEN -- creates the sasl specific information for opening a
78**		file of the sasl type.
79**
80**	Parameters:
81**		fp -- the file pointer associated with the new open
82**		info -- contains the sasl connection information pointer and
83**			the original SM_FILE_T that holds the open
84**		flags -- ignored
85**		rpool -- ignored
86**
87**	Returns:
88**		0 on success
89*/
90
91static int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
92
93/* ARGSUSED2 */
94static int
95sasl_open(fp, info, flags, rpool)
96	SM_FILE_T *fp;
97	const void *info;
98	int flags;
99	const void *rpool;
100{
101	struct sasl_obj *so;
102	struct sasl_info *si = (struct sasl_info *) info;
103
104	so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
105	so->fp = si->fp;
106	so->conn = si->conn;
107
108	/*
109	**  The underlying 'fp' is set to SM_IO_NOW so that the entire
110	**  encoded string is written in one chunk. Otherwise there is
111	**  the possibility that it may appear illegal, bogus or
112	**  mangled to the other side of the connection.
113	**  We will read or write through 'fp' since it is the opaque
114	**  connection for the communications. We need to treat it this
115	**  way in case the encoded string is to be sent down a TLS
116	**  connection rather than, say, sm_io's stdio.
117	*/
118
119	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
120	fp->f_cookie = so;
121	return 0;
122}
123
124/*
125**  SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
126**
127**	Parameters:
128**		fp -- the file pointer to close
129**
130**	Returns:
131**		0 on success
132*/
133
134static int sasl_close __P((SM_FILE_T *));
135
136static int
137sasl_close(fp)
138	SM_FILE_T *fp;
139{
140	struct sasl_obj *so;
141
142	so = (struct sasl_obj *) fp->f_cookie;
143	if (so->fp != NULL)
144	{
145		sm_io_close(so->fp, SM_TIME_DEFAULT);
146		so->fp = NULL;
147	}
148	sm_free(so);
149	so = NULL;
150	return 0;
151}
152
153/* how to deallocate a buffer allocated by SASL */
154extern void	sm_sasl_free __P((void *));
155# define SASL_DEALLOC(b)	sm_sasl_free(b)
156
157/*
158**  SASL_READ -- read encrypted information and decrypt it for the caller
159**
160**	Parameters:
161**		fp -- the file pointer
162**		buf -- the location to place the decrypted information
163**		size -- the number of bytes to read after decryption
164**
165**	Results:
166**		-1 on error
167**		otherwise the number of bytes read
168*/
169
170static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
171
172static ssize_t
173sasl_read(fp, buf, size)
174	SM_FILE_T *fp;
175	char *buf;
176	size_t size;
177{
178	int result;
179	ssize_t len;
180	static char *outbuf = NULL;
181	static unsigned int outlen = 0;
182	static unsigned int offset = 0;
183	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
184
185	/*
186	**  sasl_decode() may require more data than a single read() returns.
187	**  Hence we have to put a loop around the decoding.
188	**  This also requires that we may have to split up the returned
189	**  data since it might be larger than the allowed size.
190	**  Therefore we use a static pointer and return portions of it
191	**  if necessary.
192	*/
193
194	while (outbuf == NULL && outlen == 0)
195	{
196		len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
197		if (len <= 0)
198			return len;
199		result = sasl_decode(so->conn, buf,
200				     (unsigned int) len, &outbuf, &outlen);
201		if (result != SASL_OK)
202		{
203			outbuf = NULL;
204			offset = 0;
205			outlen = 0;
206			return -1;
207		}
208	}
209
210	if (outbuf == NULL)
211	{
212		/* be paranoid: outbuf == NULL but outlen != 0 */
213		syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
214		/* NOTREACHED */
215	}
216	if (outlen - offset > size)
217	{
218		/* return another part of the buffer */
219		(void) memcpy(buf, outbuf + offset, size);
220		offset += size;
221		len = size;
222	}
223	else
224	{
225		/* return the rest of the buffer */
226		len = outlen - offset;
227		(void) memcpy(buf, outbuf + offset, (size_t) len);
228		SASL_DEALLOC(outbuf);
229		outbuf = NULL;
230		offset = 0;
231		outlen = 0;
232	}
233	return len;
234}
235
236/*
237**  SASL_WRITE -- write information out after encrypting it
238**
239**	Parameters:
240**		fp -- the file pointer
241**		buf -- holds the data to be encrypted and written
242**		size -- the number of bytes to have encrypted and written
243**
244**	Returns:
245**		-1 on error
246**		otherwise number of bytes written
247*/
248
249static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
250
251static ssize_t
252sasl_write(fp, buf, size)
253	SM_FILE_T *fp;
254	const char *buf;
255	size_t size;
256{
257	int result;
258	char *outbuf;
259	unsigned int outlen;
260	size_t ret = 0, total = 0;
261	struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
262
263	result = sasl_encode(so->conn, buf,
264			     (unsigned int) size, &outbuf, &outlen);
265
266	if (result != SASL_OK)
267		return -1;
268
269	if (outbuf != NULL)
270	{
271		while (outlen > 0)
272		{
273			ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
274					  &outbuf[total], outlen);
275			outlen -= ret;
276			total += ret;
277		}
278		SASL_DEALLOC(outbuf);
279	}
280	return size;
281}
282
283/*
284**  SFDCSASL -- create sasl file type and open in and out file pointers
285**	       for sendmail to read from and write to.
286**
287**	Parameters:
288**		fin -- the sm_io file encrypted data to be read from
289**		fout -- the sm_io file encrypted data to be writen to
290**		conn -- the sasl connection pointer
291**
292**	Returns:
293**		-1 on error
294**		0 on success
295**
296**	Side effects:
297**		The arguments "fin" and "fout" are replaced with the new
298**		SM_FILE_T pointers.
299*/
300
301int
302sfdcsasl(fin, fout, conn)
303	SM_FILE_T **fin;
304	SM_FILE_T **fout;
305	sasl_conn_t *conn;
306{
307	SM_FILE_T *newin, *newout;
308	SM_FILE_T  SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
309		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
310		SM_TIME_FOREVER);
311	struct sasl_info info;
312
313	if (conn == NULL)
314	{
315		/* no need to do anything */
316		return 0;
317	}
318
319	SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
320		sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
321		SM_TIME_FOREVER);
322	info.fp = *fin;
323	info.conn = conn;
324	newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY,
325			   NULL);
326
327	if (newin == NULL)
328		return -1;
329
330	info.fp = *fout;
331	info.conn = conn;
332	newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY,
333			    NULL);
334
335	if (newout == NULL)
336	{
337		(void) sm_io_close(newin, SM_TIME_DEFAULT);
338		return -1;
339	}
340	sm_io_automode(newin, newout);
341
342	*fin = newin;
343	*fout = newout;
344	return 0;
345}
346#endif /* SASL */
347
348#if STARTTLS
349# include "sfsasl.h"
350#  include <openssl/err.h>
351
352/* Structure used by the "tls" file type */
353struct tls_obj
354{
355	SM_FILE_T *fp;
356	SSL *con;
357};
358
359struct tls_info
360{
361	SM_FILE_T *fp;
362	SSL *con;
363};
364
365/*
366**  TLS_GETINFO - returns requested information about a "tls" file
367**		 descriptor.
368**
369**	Parameters:
370**		fp -- the file descriptor
371**		what -- the type of information requested
372**		valp -- the thang to return the information in (unused)
373**
374**	Returns:
375**		-1 for unknown requests
376**		>=0 on success with valp filled in (if possible).
377*/
378
379static int tls_getinfo __P((SM_FILE_T *, int, void *));
380
381/* ARGSUSED2 */
382static int
383tls_getinfo(fp, what, valp)
384	SM_FILE_T *fp;
385	int what;
386	void *valp;
387{
388	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
389
390	switch (what)
391	{
392	  case SM_IO_WHAT_FD:
393		if (so->fp == NULL)
394			return -1;
395		return so->fp->f_file; /* for stdio fileno() compatability */
396
397	  case SM_IO_IS_READABLE:
398		return SSL_pending(so->con) > 0;
399
400	  default:
401		return -1;
402	}
403}
404
405/*
406**  TLS_OPEN -- creates the tls specific information for opening a
407**	       file of the tls type.
408**
409**	Parameters:
410**		fp -- the file pointer associated with the new open
411**		info -- the sm_io file pointer holding the open and the
412**			TLS encryption connection to be read from or written to
413**		flags -- ignored
414**		rpool -- ignored
415**
416**	Returns:
417**		0 on success
418*/
419
420static int tls_open __P((SM_FILE_T *, const void *, int, const void *));
421
422/* ARGSUSED2 */
423static int
424tls_open(fp, info, flags, rpool)
425	SM_FILE_T *fp;
426	const void *info;
427	int flags;
428	const void *rpool;
429{
430	struct tls_obj *so;
431	struct tls_info *ti = (struct tls_info *) info;
432
433	so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
434	so->fp = ti->fp;
435	so->con = ti->con;
436
437	/*
438	**  We try to get the "raw" file descriptor that TLS uses to
439	**  do the actual read/write with. This is to allow us control
440	**  over the file descriptor being a blocking or non-blocking type.
441	**  Under the covers TLS handles the change and this allows us
442	**  to do timeouts with sm_io.
443	*/
444
445	fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
446	(void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
447	fp->f_cookie = so;
448	return 0;
449}
450
451/*
452**  TLS_CLOSE -- close the tls specific parts of the tls file pointer
453**
454**	Parameters:
455**		fp -- the file pointer to close
456**
457**	Returns:
458**		0 on success
459*/
460
461static int tls_close __P((SM_FILE_T *));
462
463static int
464tls_close(fp)
465	SM_FILE_T *fp;
466{
467	struct tls_obj *so;
468
469	so = (struct tls_obj *) fp->f_cookie;
470	if (so->fp != NULL)
471	{
472		sm_io_close(so->fp, SM_TIME_DEFAULT);
473		so->fp = NULL;
474	}
475	sm_free(so);
476	so = NULL;
477	return 0;
478}
479
480/* maximum number of retries for TLS related I/O due to handshakes */
481# define MAX_TLS_IOS	4
482
483/*
484**  TLS_READ -- read secured information for the caller
485**
486**	Parameters:
487**		fp -- the file pointer
488**		buf -- the location to place the data
489**		size -- the number of bytes to read from connection
490**
491**	Results:
492**		-1 on error
493**		otherwise the number of bytes read
494*/
495
496static ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
497
498static ssize_t
499tls_read(fp, buf, size)
500	SM_FILE_T *fp;
501	char *buf;
502	size_t size;
503{
504	int r;
505	static int again = MAX_TLS_IOS;
506	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
507	char *err;
508
509	r = SSL_read(so->con, (char *) buf, size);
510
511	if (r > 0)
512	{
513		again = MAX_TLS_IOS;
514		return r;
515	}
516
517	err = NULL;
518	switch (SSL_get_error(so->con, r))
519	{
520	  case SSL_ERROR_NONE:
521	  case SSL_ERROR_ZERO_RETURN:
522		again = MAX_TLS_IOS;
523		break;
524	  case SSL_ERROR_WANT_WRITE:
525		if (--again <= 0)
526			err = "read W BLOCK";
527		else
528			errno = EAGAIN;
529		break;
530	  case SSL_ERROR_WANT_READ:
531		if (--again <= 0)
532			err = "read R BLOCK";
533		else
534			errno = EAGAIN;
535		break;
536	  case SSL_ERROR_WANT_X509_LOOKUP:
537		err = "write X BLOCK";
538		break;
539	  case SSL_ERROR_SYSCALL:
540		if (r == 0 && errno == 0) /* out of protocol EOF found */
541			break;
542		err = "syscall error";
543/*
544		get_last_socket_error());
545*/
546		break;
547	  case SSL_ERROR_SSL:
548		err = "generic SSL error";
549		if (LogLevel > 9)
550			tlslogerr("read");
551		break;
552	}
553	if (err != NULL)
554	{
555		again = MAX_TLS_IOS;
556		if (errno == 0)
557			errno = EIO;
558		if (LogLevel > 7)
559			sm_syslog(LOG_WARNING, NOQID,
560				  "STARTTLS: read error=%s (%d)", err, r);
561	}
562	return r;
563}
564
565/*
566**  TLS_WRITE -- write information out through secure connection
567**
568**	Parameters:
569**		fp -- the file pointer
570**		buf -- holds the data to be securely written
571**		size -- the number of bytes to write
572**
573**	Returns:
574**		-1 on error
575**		otherwise number of bytes written
576*/
577
578static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
579
580static ssize_t
581tls_write(fp, buf, size)
582	SM_FILE_T *fp;
583	const char *buf;
584	size_t size;
585{
586	int r;
587	static int again = MAX_TLS_IOS;
588	struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
589	char *err;
590
591	r = SSL_write(so->con, (char *) buf, size);
592
593	if (r > 0)
594	{
595		again = MAX_TLS_IOS;
596		return r;
597	}
598	err = NULL;
599	switch (SSL_get_error(so->con, r))
600	{
601	  case SSL_ERROR_NONE:
602	  case SSL_ERROR_ZERO_RETURN:
603		again = MAX_TLS_IOS;
604		break;
605	  case SSL_ERROR_WANT_WRITE:
606		if (--again <= 0)
607			err = "write W BLOCK";
608		else
609			errno = EAGAIN;
610		break;
611	  case SSL_ERROR_WANT_READ:
612		if (--again <= 0)
613			err = "write R BLOCK";
614		else
615			errno = EAGAIN;
616		break;
617	  case SSL_ERROR_WANT_X509_LOOKUP:
618		err = "write X BLOCK";
619		break;
620	  case SSL_ERROR_SYSCALL:
621		if (r == 0 && errno == 0) /* out of protocol EOF found */
622			break;
623		err = "syscall error";
624/*
625		get_last_socket_error());
626*/
627		break;
628	  case SSL_ERROR_SSL:
629		err = "generic SSL error";
630/*
631		ERR_GET_REASON(ERR_peek_error()));
632*/
633		if (LogLevel > 9)
634			tlslogerr("write");
635		break;
636	}
637	if (err != NULL)
638	{
639		again = MAX_TLS_IOS;
640		if (errno == 0)
641			errno = EIO;
642		if (LogLevel > 7)
643			sm_syslog(LOG_WARNING, NOQID,
644				  "STARTTLS: write error=%s (%d)", err, r);
645	}
646	return r;
647}
648
649/*
650**  SFDCTLS -- create tls file type and open in and out file pointers
651**	      for sendmail to read from and write to.
652**
653**	Parameters:
654**		fin -- data input source being replaced
655**		fout -- data output source being replaced
656**		conn -- the tls connection pointer
657**
658**	Returns:
659**		-1 on error
660**		0 on success
661**
662**	Side effects:
663**		The arguments "fin" and "fout" are replaced with the new
664**		SM_FILE_T pointers.
665**		The original "fin" and "fout" are preserved in the tls file
666**		type but are not actually used because of the design of TLS.
667*/
668
669int
670sfdctls(fin, fout, con)
671	SM_FILE_T **fin;
672	SM_FILE_T **fout;
673	SSL *con;
674{
675	SM_FILE_T *tlsin, *tlsout;
676	SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
677		tls_read, tls_write, NULL, tls_getinfo, NULL,
678		SM_TIME_FOREVER);
679	struct tls_info info;
680
681	SM_ASSERT(con != NULL);
682
683	SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
684		tls_read, tls_write, NULL, tls_getinfo, NULL,
685		SM_TIME_FOREVER);
686	info.fp = *fin;
687	info.con = con;
688	tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY,
689			   NULL);
690	if (tlsin == NULL)
691		return -1;
692
693	info.fp = *fout;
694	tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY,
695			    NULL);
696	if (tlsout == NULL)
697	{
698		(void) sm_io_close(tlsin, SM_TIME_DEFAULT);
699		return -1;
700	}
701	sm_io_automode(tlsin, tlsout);
702
703	*fin = tlsin;
704	*fout = tlsout;
705	return 0;
706}
707#endif /* STARTTLS */
708