sfsasl.c revision 77349
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.14 2001/05/03 17:24:16 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
171static ssize_t
172# if SFIO
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 */
179tls_read(disc, buf, size)
180	void *disc;
181	void *buf;
182	size_t size;
183# endif /* SFIO */
184{
185	int r;
186	Tlsdisc_t *sd;
187
188	/* Cast back to correct type */
189	sd = (Tlsdisc_t *) disc;
190
191	r = SSL_read(sd->con, (char *) buf, size);
192	if (r < 0 && LogLevel > 7)
193	{
194		char *err;
195
196		err = NULL;
197		switch (SSL_get_error(sd->con, r))
198		{
199			case SSL_ERROR_NONE:
200				break;
201			case SSL_ERROR_WANT_WRITE:
202				err = "write W BLOCK";
203				break;
204			case SSL_ERROR_WANT_READ:
205				err = "write R BLOCK";
206				break;
207			case SSL_ERROR_WANT_X509_LOOKUP:
208				err = "write X BLOCK";
209				break;
210			case SSL_ERROR_ZERO_RETURN:
211				break;
212			case SSL_ERROR_SYSCALL:
213				err = "syscall error";
214/*
215				get_last_socket_error());
216*/
217				break;
218			case SSL_ERROR_SSL:
219				err = "generic SSL error";
220				break;
221		}
222		if (err != NULL)
223			sm_syslog(LOG_WARNING, NOQID, "TLS: read error:  %s",
224				  err);
225	}
226	return r;
227}
228
229static ssize_t
230# if SFIO
231tls_write(f, buf, size, disc)
232	Sfio_t *f;
233	const Void_t *buf;
234	size_t size;
235	Sfdisc_t *disc;
236# else /* SFIO */
237tls_write(disc, buf, size)
238	void *disc;
239	const void *buf;
240	size_t size;
241# endif /* SFIO */
242{
243	int r;
244	Tlsdisc_t *sd;
245
246	/* Cast back to correct type */
247	sd = (Tlsdisc_t *) disc;
248
249	r = SSL_write(sd->con, (char *)buf, size);
250	if (r < 0 && LogLevel > 7)
251	{
252		char *err;
253
254		err = NULL;
255		switch (SSL_get_error(sd->con, r))
256		{
257			case SSL_ERROR_NONE:
258				break;
259			case SSL_ERROR_WANT_WRITE:
260				err = "write W BLOCK";
261				break;
262			case SSL_ERROR_WANT_READ:
263				err = "write R BLOCK";
264				break;
265			case SSL_ERROR_WANT_X509_LOOKUP:
266				err = "write X BLOCK";
267				break;
268			case SSL_ERROR_ZERO_RETURN:
269				break;
270			case SSL_ERROR_SYSCALL:
271				err = "syscall error";
272/*
273				get_last_socket_error());
274*/
275				break;
276			case SSL_ERROR_SSL:
277				err = "generic SSL error";
278/*
279				ERR_GET_REASON(ERR_peek_error()));
280*/
281				break;
282		}
283		if (err != NULL)
284			sm_syslog(LOG_WARNING, NOQID, "TLS: write error:  %s",
285				  err);
286	}
287	return r;
288}
289
290# if !SFIO
291static int
292tls_close(cookie)
293	void *cookie;
294{
295	int retval = 0;
296	Tlsdisc_t *tc;
297
298	/* Cast back to correct type */
299	tc = (Tlsdisc_t *)cookie;
300
301	if (tc->fp != NULL)
302	{
303		retval = fclose(tc->fp);
304		tc->fp = NULL;
305	}
306
307	sm_free(tc);
308	return retval;
309}
310# endif /* !SFIO */
311
312int
313sfdctls(fin, fout, con)
314# if SFIO
315	Sfio_t *fin;
316	Sfio_t *fout;
317# else /* SFIO */
318	FILE **fin;
319	FILE **fout;
320# endif /* SFIO */
321	SSL *con;
322{
323	Tlsdisc_t *tlsin, *tlsout;
324# if !SFIO
325	FILE *fp;
326# else /* !SFIO */
327	int rfd, wfd;
328# endif /* !SFIO */
329
330	if (con == NULL)
331		return 0;
332
333	tlsin = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
334	tlsout = (Tlsdisc_t *) xalloc(sizeof(Tlsdisc_t));
335# if SFIO
336	tlsin->disc.readf = tls_read;
337	tlsin->disc.writef = tls_write;
338	tlsin->disc.seekf = NULL;
339	tlsin->disc.exceptf = NULL;
340	tlsin->con = con;
341
342	tlsout->disc.readf = tls_read;
343	tlsout->disc.writef = tls_write;
344	tlsout->disc.seekf = NULL;
345	tlsout->disc.exceptf = NULL;
346	tlsout->con = con;
347
348	rfd = fileno(fin);
349	wfd = fileno(fout);
350	if (rfd < 0 || wfd < 0 ||
351	    SSL_set_rfd(con, rfd) <= 0 || SSL_set_wfd(con, wfd) <= 0)
352	{
353		sm_free(tlsin);
354		sm_free(tlsout);
355		return -1;
356	}
357	if (sfdisc(fin, (Sfdisc_t *) tlsin) != (Sfdisc_t *) tlsin ||
358	    sfdisc(fout, (Sfdisc_t *) tlsout) != (Sfdisc_t *) tlsout)
359	{
360		sm_free(tlsin);
361		sm_free(tlsout);
362		return -1;
363	}
364# else /* SFIO */
365	tlsin->fp = *fin;
366	tlsin->con = con;
367	fp = funopen(tlsin, tls_read, tls_write, NULL, tls_close);
368	if (fp == NULL)
369	{
370		sm_free(tlsin);
371		return -1;
372	}
373	*fin = fp;
374
375	tlsout->fp = *fout;
376	tlsout->con = con;
377	fp = funopen(tlsout, tls_read, tls_write, NULL, tls_close);
378	if (fp == NULL)
379	{
380		FILE *save;
381
382		/* Hack: Don't close underlying fp */
383		save = tlsin->fp;
384		tlsin->fp = NULL;
385		fclose(*fin);
386		*fin = save;
387		sm_free(tlsout);
388		return -1;
389	}
390	*fout = fp;
391	SSL_set_rfd(con, fileno(tlsin->fp));
392	SSL_set_wfd(con, fileno(tlsout->fp));
393# endif /* SFIO */
394	return 0;
395}
396#endif /* STARTTLS && (SFIO || _FFR_TLS_TOREK) */
397