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