xref: /freebsd/contrib/sendmail/src/sfsasl.c (revision 13058a916175518dfbac6ce66b9b8e22ecf43155)
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
12 static 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 
33 static ssize_t
34 sasl_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 
98 static ssize_t
99 sasl_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 
123 int
124 sfdcsasl(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
172 static ssize_t
173 tls_read(f, buf, size, disc)
174 	Sfio_t *f;
175 	Void_t *buf;
176 	size_t size;
177 	Sfdisc_t *disc;
178 # else /* SFIO */
179 static int
180 tls_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
231 static ssize_t
232 tls_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 */
238 static int
239 tls_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
293 static int
294 tls_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 
314 int
315 sfdctls(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