xref: /freebsd/contrib/sendmail/src/sfsasl.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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.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 
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 static ssize_t
172 # if SFIO
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 tls_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 
229 static ssize_t
230 # if SFIO
231 tls_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 */
237 tls_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
291 static int
292 tls_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 
312 int
313 sfdctls(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