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