sfsasl.c revision 80785
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#ifndef lint
12static char id[] = "@(#)$Id: sfsasl.c,v 8.17.4.15 2001/07/11 17:37:07 gshapiro Exp $";
13#endif /* ! lint */
14
15#if SFIO
16# include <sfio/stdio.h>
17#endif /* SFIO */
18
19#include <stdlib.h>
20#include <sendmail.h>
21
22#if SASL && SFIO
23/*
24**  SASL
25*/
26
27# include <sasl.h>
28# include "sfsasl.h"
29
30/* how to deallocate a buffer allocated by SASL */
31#  define SASL_DEALLOC(b)	sm_free(b)
32
33static ssize_t
34sasl_read(f, buf, size, disc)
35	Sfio_t *f;
36	Void_t *buf;
37	size_t size;
38	Sfdisc_t *disc;
39{
40	int len, result;
41	static char *outbuf = NULL;
42	static unsigned int outlen = 0;
43	static unsigned int offset = 0;
44	Sasldisc_t *sd = (Sasldisc_t *) disc;
45
46	/*
47	**  sasl_decode() may require more data than a single read() returns.
48	**  Hence we have to put a loop around the decoding.
49	**  This also requires that we may have to split up the returned
50	**  data since it might be larger than the allowed size.
51	**  Therefore we use a static pointer and return portions of it
52	**  if necessary.
53	*/
54
55	while (outbuf == NULL && outlen == 0)
56	{
57		len = sfrd(f, buf, size, disc);
58		if (len <= 0)
59			return len;
60		result = sasl_decode(sd->conn, buf, len, &outbuf, &outlen);
61		if (result != SASL_OK)
62		{
63			outbuf = NULL;
64			offset = 0;
65			outlen = 0;
66			return -1;
67		}
68	}
69
70	if (outbuf != NULL)
71	{
72		if (outlen - offset > size)
73		{
74			/* return another part of the buffer */
75			(void) memcpy(buf, outbuf + offset, (size_t) size);
76			offset += size;
77			result = size;
78		}
79		else
80		{
81			/* return the rest of the buffer */
82			result = outlen - offset;
83			(void) memcpy(buf, outbuf + offset, (size_t) result);
84			SASL_DEALLOC(outbuf);
85			outbuf = NULL;
86			offset = 0;
87			outlen = 0;
88		}
89	}
90	else
91	{
92		/* be paranoid: outbuf == NULL but outlen != 0 */
93		syserr("!sasl_read failure: outbuf == NULL but outlen != 0");
94	}
95	return result;
96}
97
98static ssize_t
99sasl_write(f, buf, size, disc)
100	Sfio_t *f;
101	const Void_t *buf;
102	size_t size;
103	Sfdisc_t *disc;
104{
105	int result;
106	char *outbuf;
107	unsigned int outlen;
108	Sasldisc_t *sd = (Sasldisc_t *) disc;
109
110	result = sasl_encode(sd->conn, buf, size, &outbuf, &outlen);
111
112	if (result != SASL_OK)
113		return -1;
114
115	if (outbuf != NULL)
116	{
117		sfwr(f, outbuf, outlen, disc);
118		SASL_DEALLOC(outbuf);
119	}
120	return size;
121}
122
123int
124sfdcsasl(fin, fout, conn)
125	Sfio_t *fin;
126	Sfio_t *fout;
127	sasl_conn_t *conn;
128{
129	Sasldisc_t *saslin, *saslout;
130
131	if (conn == NULL)
132	{
133		/* no need to do anything */
134		return 0;
135	}
136
137	saslin = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t));
138	saslout = (Sasldisc_t *) xalloc(sizeof(Sasldisc_t));
139	saslin->disc.readf = sasl_read;
140	saslin->disc.writef = sasl_write;
141	saslin->disc.seekf = NULL;
142	saslin->disc.exceptf = NULL;
143
144	saslout->disc.readf = sasl_read;
145	saslout->disc.writef = sasl_write;
146	saslout->disc.seekf = NULL;
147	saslout->disc.exceptf = NULL;
148
149	saslin->conn = conn;
150	saslout->conn = conn;
151
152	if (sfdisc(fin, (Sfdisc_t *) saslin) != (Sfdisc_t *) saslin ||
153	    sfdisc(fout, (Sfdisc_t *) saslout) != (Sfdisc_t *) saslout)
154	{
155		sm_free(saslin);
156		sm_free(saslout);
157		return -1;
158	}
159	return 0;
160}
161#endif /* SASL && SFIO */
162
163#if STARTTLS && (SFIO || _FFR_TLS_TOREK)
164/*
165**  STARTTLS
166*/
167
168# include "sfsasl.h"
169#  include <openssl/err.h>
170
171# if SFIO
172static ssize_t
173tls_read(f, buf, size, disc)
174	Sfio_t *f;
175	Void_t *buf;
176	size_t size;
177	Sfdisc_t *disc;
178# else /* SFIO */
179static int
180tls_read(disc, buf, size)
181	void *disc;
182	char *buf;
183	int size;
184# endif /* SFIO */
185{
186	int r;
187	Tlsdisc_t *sd;
188
189	/* Cast back to correct type */
190	sd = (Tlsdisc_t *) disc;
191
192	r = SSL_read(sd->con, (char *) buf, size);
193	if (r < 0 && LogLevel > 7)
194	{
195		char *err;
196
197		err = NULL;
198		switch (SSL_get_error(sd->con, r))
199		{
200			case SSL_ERROR_NONE:
201				break;
202			case SSL_ERROR_WANT_WRITE:
203				err = "write W BLOCK";
204				break;
205			case SSL_ERROR_WANT_READ:
206				err = "write R BLOCK";
207				break;
208			case SSL_ERROR_WANT_X509_LOOKUP:
209				err = "write X BLOCK";
210				break;
211			case SSL_ERROR_ZERO_RETURN:
212				break;
213			case SSL_ERROR_SYSCALL:
214				err = "syscall error";
215/*
216				get_last_socket_error());
217*/
218				break;
219			case SSL_ERROR_SSL:
220				err = "generic SSL error";
221				break;
222		}
223		if (err != NULL)
224			sm_syslog(LOG_WARNING, NOQID, "TLS: read error:  %s",
225				  err);
226	}
227	return r;
228}
229
230# if SFIO
231static ssize_t
232tls_write(f, buf, size, disc)
233	Sfio_t *f;
234	const Void_t *buf;
235	size_t size;
236	Sfdisc_t *disc;
237# else /* SFIO */
238static int
239tls_write(disc, buf, size)
240	void *disc;
241	const char *buf;
242	int size;
243# endif /* SFIO */
244{
245	int r;
246	Tlsdisc_t *sd;
247
248	/* Cast back to correct type */
249	sd = (Tlsdisc_t *) disc;
250
251	r = SSL_write(sd->con, (char *)buf, size);
252	if (r < 0 && LogLevel > 7)
253	{
254		char *err;
255
256		err = NULL;
257		switch (SSL_get_error(sd->con, r))
258		{
259			case SSL_ERROR_NONE:
260				break;
261			case SSL_ERROR_WANT_WRITE:
262				err = "write W BLOCK";
263				break;
264			case SSL_ERROR_WANT_READ:
265				err = "write R BLOCK";
266				break;
267			case SSL_ERROR_WANT_X509_LOOKUP:
268				err = "write X BLOCK";
269				break;
270			case SSL_ERROR_ZERO_RETURN:
271				break;
272			case SSL_ERROR_SYSCALL:
273				err = "syscall error";
274/*
275				get_last_socket_error());
276*/
277				break;
278			case SSL_ERROR_SSL:
279				err = "generic SSL error";
280/*
281				ERR_GET_REASON(ERR_peek_error()));
282*/
283				break;
284		}
285		if (err != NULL)
286			sm_syslog(LOG_WARNING, NOQID, "TLS: write error:  %s",
287				  err);
288	}
289	return r;
290}
291
292# if !SFIO
293static int
294tls_close(cookie)
295	void *cookie;
296{
297	int retval = 0;
298	Tlsdisc_t *tc;
299
300	/* Cast back to correct type */
301	tc = (Tlsdisc_t *)cookie;
302
303	if (tc->fp != NULL)
304	{
305		retval = fclose(tc->fp);
306		tc->fp = NULL;
307	}
308
309	sm_free(tc);
310	return retval;
311}
312# endif /* !SFIO */
313
314int
315sfdctls(fin, fout, con)
316# if SFIO
317	Sfio_t *fin;
318	Sfio_t *fout;
319# else /* SFIO */
320	FILE **fin;
321	FILE **fout;
322# endif /* SFIO */
323	SSL *con;
324{
325	Tlsdisc_t *tlsin, *tlsout;
326# if !SFIO
327	FILE *fp;
328# else /* !SFIO */
329	int rfd, wfd;
330# endif /* !SFIO */
331
332	if (con == NULL)
333		return 0;
334
335	tlsin = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
336	tlsout = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
337# if SFIO
338	tlsin->disc.readf = tls_read;
339	tlsin->disc.writef = tls_write;
340	tlsin->disc.seekf = NULL;
341	tlsin->disc.exceptf = NULL;
342	tlsin->con = con;
343
344	tlsout->disc.readf = tls_read;
345	tlsout->disc.writef = tls_write;
346	tlsout->disc.seekf = NULL;
347	tlsout->disc.exceptf = NULL;
348	tlsout->con = con;
349
350	rfd = fileno(fin);
351	wfd = fileno(fout);
352	if (rfd < 0 || wfd < 0 ||
353	    SSL_set_rfd(con, rfd) <= 0 || SSL_set_wfd(con, wfd) <= 0)
354	{
355		sm_free(tlsin);
356		sm_free(tlsout);
357		return -1;
358	}
359	if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin ||
360	    sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout)
361	{
362		sm_free(tlsin);
363		sm_free(tlsout);
364		return -1;
365	}
366# else /* SFIO */
367	tlsin->fp = *fin;
368	tlsin->con = con;
369	fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close);
370	if (fp == NULL)
371	{
372		sm_free(tlsin);
373		return -1;
374	}
375	*fin = fp;
376
377	tlsout->fp = *fout;
378	tlsout->con = con;
379	fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close);
380	if (fp == NULL)
381	{
382		FILE *save;
383
384		/* Hack: Don't close underlying fp */
385		save = tlsin->fp;
386		tlsin->fp = NULL;
387		fclose(*fin);
388		*fin = save;
389		sm_free(tlsout);
390		return -1;
391	}
392	*fout = fp;
393	SSL_set_rfd(con, fileno(tlsin->fp));
394	SSL_set_wfd(con, fileno(tlsout->fp));
395# endif /* SFIO */
396	return 0;
397}
398#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
399