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