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