]> diplodocus.org Git - nmh/blobdiff - sbr/netsec.c
Fix invalid pointer arithmetic.
[nmh] / sbr / netsec.c
index e0dd0c28d1f1a77c25e4fdee534bb1d6fe6184c1..ab49046745bbb4040e3ebb2053749950b323c040 100644 (file)
@@ -12,6 +12,7 @@
 #include <h/oauth.h>
 #include <stdarg.h>
 #include <sys/select.h>
 #include <h/oauth.h>
 #include <stdarg.h>
 #include <sys/select.h>
+#include "base64.h"
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
@@ -54,10 +55,11 @@ static SSL_CTX *sslctx = NULL;              /* SSL Context */
 struct _netsec_context {
     int ns_readfd;             /* Read descriptor for network connection */
     int ns_writefd;            /* Write descriptor for network connection */
 struct _netsec_context {
     int ns_readfd;             /* Read descriptor for network connection */
     int ns_writefd;            /* Write descriptor for network connection */
+    int ns_noclose;            /* Do not close file descriptors if set */
     int ns_snoop;              /* If true, display network data */
     int ns_snoop;              /* If true, display network data */
-    int ns_snoop_noend;                /* If true, didn't get a CR/LF on last line */
     netsec_snoop_callback *ns_snoop_cb; /* Snoop output callback */
     void *ns_snoop_context;    /* Context data for snoop function */
     netsec_snoop_callback *ns_snoop_cb; /* Snoop output callback */
     void *ns_snoop_context;    /* Context data for snoop function */
+    char *ns_snoop_savebuf;    /* Save buffer for snoop data */
     int ns_timeout;            /* Network read timeout, in seconds */
     char *ns_userid;           /* Userid for authentication */
     char *ns_hostname;         /* Hostname we've connected to */
     int ns_timeout;            /* Network read timeout, in seconds */
     char *ns_userid;           /* Userid for authentication */
     char *ns_hostname;         /* Hostname we've connected to */
@@ -72,6 +74,7 @@ struct _netsec_context {
     char *sasl_mech;           /* User-requested mechanism */
     char *sasl_chosen_mech;    /* Mechanism chosen by SASL */
     netsec_sasl_callback sasl_proto_cb; /* SASL callback we use */
     char *sasl_mech;           /* User-requested mechanism */
     char *sasl_chosen_mech;    /* Mechanism chosen by SASL */
     netsec_sasl_callback sasl_proto_cb; /* SASL callback we use */
+    void *sasl_proto_context;  /* Context to be used by SASL callback */
 #ifdef OAUTH_SUPPORT
     char *oauth_service;       /* OAuth2 service name */
 #endif /* OAUTH_SUPPORT */
 #ifdef OAUTH_SUPPORT
     char *oauth_service;       /* OAuth2 service name */
 #endif /* OAUTH_SUPPORT */
@@ -140,10 +143,11 @@ netsec_init(void)
     NEW(nsc);
     nsc->ns_readfd = -1;
     nsc->ns_writefd = -1;
     NEW(nsc);
     nsc->ns_readfd = -1;
     nsc->ns_writefd = -1;
+    nsc->ns_noclose = 0;
     nsc->ns_snoop = 0;
     nsc->ns_snoop = 0;
-    nsc->ns_snoop_noend = 0;
     nsc->ns_snoop_cb = NULL;
     nsc->ns_snoop_context = NULL;
     nsc->ns_snoop_cb = NULL;
     nsc->ns_snoop_context = NULL;
+    nsc->ns_snoop_savebuf = NULL;
     nsc->ns_userid = NULL;
     nsc->ns_hostname = NULL;
     nsc->ns_timeout = 60;      /* Our default */
     nsc->ns_userid = NULL;
     nsc->ns_hostname = NULL;
     nsc->ns_timeout = 60;      /* Our default */
@@ -158,6 +162,7 @@ netsec_init(void)
     nsc->sasl_mech = NULL;
     nsc->sasl_chosen_mech = NULL;
     nsc->sasl_proto_cb = NULL;
     nsc->sasl_mech = NULL;
     nsc->sasl_chosen_mech = NULL;
     nsc->sasl_proto_cb = NULL;
+    nsc->sasl_proto_context = NULL;
 #ifdef OAUTH_SUPPORT
     nsc->oauth_service = NULL;
 #endif /* OAUTH_SUPPORT */
 #ifdef OAUTH_SUPPORT
     nsc->oauth_service = NULL;
 #endif /* OAUTH_SUPPORT */
@@ -180,25 +185,25 @@ netsec_init(void)
 
 /*
  * Shutdown the connection completely and free all resources.
 
 /*
  * Shutdown the connection completely and free all resources.
- * The connection is only closed if the flag is given.
  */
 
 void
  */
 
 void
-netsec_shutdown(netsec_context *nsc, int closeflag)
+netsec_shutdown(netsec_context *nsc)
 {
 {
-    mh_xfree(nsc->ns_userid);
-    mh_xfree(nsc->ns_hostname);
-    mh_xfree(nsc->ns_inbuffer);
-    mh_xfree(nsc->ns_outbuffer);
-    mh_xfree(nsc->sasl_mech);
-    mh_xfree(nsc->sasl_chosen_mech);
+    free(nsc->ns_userid);
+    free(nsc->ns_hostname);
+    free(nsc->ns_inbuffer);
+    free(nsc->ns_outbuffer);
+    free(nsc->sasl_mech);
+    free(nsc->sasl_chosen_mech);
+    free(nsc->ns_snoop_savebuf);
 #ifdef OAUTH_SERVICE
 #ifdef OAUTH_SERVICE
-    mh_xfree(nsc->oauth_service);
+    free(nsc->oauth_service);
 #endif /* OAUTH_SERVICE */
 #ifdef CYRUS_SASL
     if (nsc->sasl_conn)
        sasl_dispose(&nsc->sasl_conn);
 #endif /* OAUTH_SERVICE */
 #ifdef CYRUS_SASL
     if (nsc->sasl_conn)
        sasl_dispose(&nsc->sasl_conn);
-    mh_xfree(nsc->sasl_cbs);
+    free(nsc->sasl_cbs);
     if (nsc->sasl_creds)
        nmh_credentials_free(nsc->sasl_creds);
     if (nsc->sasl_secret) {
     if (nsc->sasl_creds)
        nmh_credentials_free(nsc->sasl_creds);
     if (nsc->sasl_secret) {
@@ -207,7 +212,7 @@ netsec_shutdown(netsec_context *nsc, int closeflag)
        }
        free(nsc->sasl_secret);
     }
        }
        free(nsc->sasl_secret);
     }
-    mh_xfree(nsc->sasl_tmpbuf);
+    free(nsc->sasl_tmpbuf);
 #endif /* CYRUS_SASL */
 #ifdef TLS_SUPPORT
     if (nsc->ssl_io)
 #endif /* CYRUS_SASL */
 #ifdef TLS_SUPPORT
     if (nsc->ssl_io)
@@ -218,7 +223,7 @@ netsec_shutdown(netsec_context *nsc, int closeflag)
        BIO_free_all(nsc->ssl_io);
 #endif /* TLS_SUPPORT */
 
        BIO_free_all(nsc->ssl_io);
 #endif /* TLS_SUPPORT */
 
-    if (closeflag) {
+    if (! nsc->ns_noclose) {
        if (nsc->ns_readfd != -1)
            close(nsc->ns_readfd);
        if (nsc->ns_writefd != -1 && nsc->ns_writefd != nsc->ns_readfd)
        if (nsc->ns_readfd != -1)
            close(nsc->ns_readfd);
        if (nsc->ns_writefd != -1 && nsc->ns_writefd != nsc->ns_readfd)
@@ -391,7 +396,7 @@ netsec_read(netsec_context *nsc, void *buffer, size_t size, char **errstr)
      * assume here that this has something in it.
      */
 
      * assume here that this has something in it.
      */
 
-    retlen = size > nsc->ns_inbuflen ? nsc->ns_inbuflen : size;
+    retlen = min(size, nsc->ns_inbuflen);
 
     memcpy(buffer, nsc->ns_inptr, retlen);
 
 
     memcpy(buffer, nsc->ns_inptr, retlen);
 
@@ -459,8 +464,10 @@ retry:
                if (nsc->ns_snoop_cb)
                    nsc->ns_snoop_cb(nsc, sptr, strlen(sptr),
                                     nsc->ns_snoop_context);
                if (nsc->ns_snoop_cb)
                    nsc->ns_snoop_cb(nsc, sptr, strlen(sptr),
                                     nsc->ns_snoop_context);
-               else
-                   fprintf(stderr, "%s\n", sptr);
+               else {
+                    fputs(sptr, stderr);
+                    putc('\n', stderr);
+                }
            }
            return sptr;
        }
            }
            return sptr;
        }
@@ -472,7 +479,7 @@ retry:
      */
 
     if (count >= nsc->ns_inbufsize / 2) {
      */
 
     if (count >= nsc->ns_inbufsize / 2) {
-       netsec_err(errstr, "Unable to find a line terminator after %d bytes",
+       netsec_err(errstr, "Unable to find a line terminator after %zu bytes",
                   count);
        return NULL;
     }
                   count);
        return NULL;
     }
@@ -840,35 +847,6 @@ retry:
         goto retry;
     }
 
         goto retry;
     }
 
-    if (nsc->ns_snoop) {
-       int outlen = rc;
-       if (outlen > 0 && nsc->ns_outptr[outlen - 1] == '\n') {
-           outlen--;
-           if (outlen > 0 && nsc->ns_outptr[outlen - 1] == '\r')
-               outlen--;
-       } else {
-           nsc->ns_snoop_noend = 1;
-       }
-       if (outlen > 0 || nsc->ns_snoop_noend == 0) {
-#ifdef CYRUS_SASL
-           if (nsc->sasl_seclayer)
-               fprintf(stderr, "(sasl-encrypted) ");
-#endif /* CYRUS_SASL */
-#ifdef TLS_SUPPORT
-           if (nsc->tls_active)
-               fprintf(stderr, "(tls-encrypted) ");
-#endif /* TLS_SUPPORT */
-           fprintf(stderr, "=> ");
-           if (nsc->ns_snoop_cb)
-               nsc->ns_snoop_cb(nsc, (char *) nsc->ns_outptr, outlen,
-                                nsc->ns_snoop_context);
-           else
-                fprintf(stderr, "%.*s\n", outlen, nsc->ns_outptr); 
-       } else {
-           nsc->ns_snoop_noend = 0;
-       }
-    }
-
     nsc->ns_outptr += rc;
     nsc->ns_outbuflen += rc;
 
     nsc->ns_outptr += rc;
     nsc->ns_outbuflen += rc;
 
@@ -894,6 +872,95 @@ netsec_flush(netsec_context *nsc, char **errstr)
     if (netoutlen == 0)
        return OK;
 
     if (netoutlen == 0)
        return OK;
 
+    /*
+     * If we have snoop turned on, output the data.
+     *
+     * Note here; if we don't have a CR or LF at the end, save the data
+     * in ns_snoop_savebuf for later and print it next time.
+     */
+
+    if (nsc->ns_snoop) {
+       unsigned int snoopoutlen = netoutlen;
+       const char *snoopoutbuf = (const char *) nsc->ns_outbuffer;
+
+       while (snoopoutlen > 0) {
+           const char *end = strpbrk(snoopoutbuf, "\r\n");
+           unsigned int outlen;
+
+           if (! end) {
+               if (nsc->ns_snoop_savebuf) {
+                   nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf,
+                                               strlen(nsc->ns_snoop_savebuf) +
+                                               snoopoutlen + 1);
+                   strncat(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen);
+               } else {
+                   nsc->ns_snoop_savebuf = mh_xmalloc(snoopoutlen + 1);
+                   strncpy(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen);
+                   nsc->ns_snoop_savebuf[snoopoutlen] = '\0';
+               }
+               break;
+           }
+
+           outlen = end - snoopoutbuf;
+
+#ifdef CYRUS_SASL
+           if (nsc->sasl_seclayer)
+               fprintf(stderr, "(sasl-encrypted) ");
+#endif /* CYRUS_SASL */
+#ifdef TLS_SUPPORT
+           if (nsc->tls_active)
+               fprintf(stderr, "(tls-encrypted) ");
+#endif /* TLS_SUPPORT */
+           fprintf(stderr, "=> ");
+           if (nsc->ns_snoop_cb) {
+               const char *ptr;
+               unsigned int cb_len = outlen;
+
+               if (nsc->ns_snoop_savebuf) {
+                   cb_len += strlen(nsc->ns_snoop_savebuf);
+                   nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf,
+                                               outlen);
+                   ptr = nsc->ns_snoop_savebuf;
+               } else {
+                   ptr = snoopoutbuf;
+               }
+
+               nsc->ns_snoop_cb(nsc, ptr, cb_len, nsc->ns_snoop_context);
+
+               if (nsc->ns_snoop_savebuf) {
+                   free(nsc->ns_snoop_savebuf);
+                   nsc->ns_snoop_savebuf = NULL;
+               }
+           } else {
+               if (nsc->ns_snoop_savebuf) {
+                   fprintf(stderr, "%s", nsc->ns_snoop_savebuf);
+                   free(nsc->ns_snoop_savebuf);
+                   nsc->ns_snoop_savebuf = NULL;
+               }
+               fprintf(stderr, "%.*s\n", outlen, snoopoutbuf);
+           }
+
+           /*
+            * Alright, hopefully any previous leftover data is done,
+            * and we have the current line output.  Move things past the
+            * next CR/LF.
+            */
+
+           snoopoutlen -= outlen;
+           snoopoutbuf += outlen;
+
+           if (snoopoutlen > 0 && *snoopoutbuf == '\r') {
+               snoopoutlen--;
+               snoopoutbuf++;
+           }
+
+           if (snoopoutlen > 0 && *snoopoutbuf == '\n') {
+               snoopoutlen--;
+               snoopoutbuf++;
+           }
+       }
+    }
+
     /*
      * If SASL security layers are in effect, run the data through
      * sasl_encode() first.
     /*
      * If SASL security layers are in effect, run the data through
      * sasl_encode() first.
@@ -947,7 +1014,7 @@ netsec_flush(netsec_context *nsc, char **errstr)
 int
 netsec_set_sasl_params(netsec_context *nsc, const char *service,
                       const char *mechanism, netsec_sasl_callback callback,
 int
 netsec_set_sasl_params(netsec_context *nsc, const char *service,
                       const char *mechanism, netsec_sasl_callback callback,
-                      char **errstr)
+                      void *context, char **errstr)
 {
 #ifdef CYRUS_SASL
     sasl_callback_t *sasl_cbs;
 {
 #ifdef CYRUS_SASL
     sasl_callback_t *sasl_cbs;
@@ -1020,15 +1087,12 @@ netsec_set_sasl_params(netsec_context *nsc, const char *service,
      */
 
     if (mechanism) {
      */
 
     if (mechanism) {
-       char *p;
        nsc->sasl_mech = mh_xstrdup(mechanism);
        nsc->sasl_mech = mh_xstrdup(mechanism);
-
-       for (p = nsc->sasl_mech; *p; p++)
-           if (isascii((unsigned char) *p)) /* Leave non-ASCII lower alone. */
-               *p = toupper((unsigned char) *p);
+       to_upper(nsc->sasl_mech);
     }
 
     nsc->sasl_proto_cb = callback;
     }
 
     nsc->sasl_proto_cb = callback;
+    nsc->sasl_proto_context = context;
 
     return OK;
 }
 
     return OK;
 }
@@ -1081,7 +1145,7 @@ netsec_get_password(sasl_conn_t *conn, void *context, int id,
      * us room for a terminating NUL
      */
 
      * us room for a terminating NUL
      */
 
-    *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
+    *psecret = malloc(sizeof(sasl_secret_t) + len);
 
     if (! *psecret)
        return SASL_NOMEM;
 
     if (! *psecret)
        return SASL_NOMEM;
@@ -1173,7 +1237,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
        }
 
        rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res,
        }
 
        rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res,
-                               xoauth_client_res_len, NULL, 0, errstr);
+                               xoauth_client_res_len, NULL, 0,
+                               nsc->sasl_proto_context, errstr);
        free(xoauth_client_res);
 
        if (rc != OK)
        free(xoauth_client_res);
 
        if (rc != OK)
@@ -1186,7 +1251,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
         * error.
         */
 
         * error.
         */
 
-       rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, errstr);
+       rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
+                               nsc->sasl_proto_context, errstr);
 
        if (rc != OK) {
            /*
 
        if (rc != OK) {
            /*
@@ -1196,9 +1262,10 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
             * NOT get a success message at this point.
             */
            free(*errstr);
             * NOT get a success message at this point.
             */
            free(*errstr);
-           nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, NULL);
+           nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0,
+                              nsc->sasl_proto_context, NULL);
            rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
            rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
-                                   errstr);
+                                   nsc->sasl_proto_context, errstr);
            if (rc == 0) {
                netsec_err(errstr, "Unexpected success after OAuth failure!");
            }
            if (rc == 0) {
                netsec_err(errstr, "Unexpected success after OAuth failure!");
            }
@@ -1216,7 +1283,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
      * messages.
      */
 
      * messages.
      */
 
-    memset(&secprops, 0, sizeof(secprops));
+    ZERO(&secprops);
     secprops.maxbufsize = SASL_MAXRECVBUF;
 
     /*
     secprops.maxbufsize = SASL_MAXRECVBUF;
 
     /*
@@ -1256,7 +1323,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
     nsc->sasl_chosen_mech = getcpy(chosen_mech);
 
     if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0,
     nsc->sasl_chosen_mech = getcpy(chosen_mech);
 
     if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0,
-                          errstr) != OK)
+                          nsc->sasl_proto_context, errstr) != OK)
        return NOTOK;
 
     /*
        return NOTOK;
 
     /*
@@ -1270,26 +1337,30 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
         */
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen,
         */
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen,
-                              errstr) != OK) {
-           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL);
+                              nsc->sasl_proto_context, errstr) != OK) {
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
+                              nsc->sasl_proto_context, NULL);
            return NOTOK;
        }
 
        rc = sasl_client_step(nsc->sasl_conn, (char *) outbuf, outbuflen, NULL,
                              (const char **) &saslbuf, &saslbuflen);
 
            return NOTOK;
        }
 
        rc = sasl_client_step(nsc->sasl_conn, (char *) outbuf, outbuflen, NULL,
                              (const char **) &saslbuf, &saslbuflen);
 
-        mh_xfree(outbuf);
+        free(outbuf);
 
        if (rc != SASL_OK && rc != SASL_CONTINUE) {
            netsec_err(errstr, "SASL client negotiation failed: %s",
                       sasl_errdetail(nsc->sasl_conn));
 
        if (rc != SASL_OK && rc != SASL_CONTINUE) {
            netsec_err(errstr, "SASL client negotiation failed: %s",
                       sasl_errdetail(nsc->sasl_conn));
-           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL);
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
+                              nsc->sasl_proto_context, NULL);
            return NOTOK;
        }
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_WRITE, saslbuf, saslbuflen,
            return NOTOK;
        }
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_WRITE, saslbuf, saslbuflen,
-                              NULL, 0, errstr) != OK) {
-           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL);
+                              NULL, 0, nsc->sasl_proto_context,
+                              errstr) != OK) {
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
+                              nsc->sasl_proto_context, NULL);
            return NOTOK;
        }
     }
            return NOTOK;
        }
     }
@@ -1300,7 +1371,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
      */
 
     if (nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
      */
 
     if (nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
-                          errstr) != OK) {
+                          nsc->sasl_proto_context, errstr) != OK) {
        /*
         * At this point we can't really send an abort since the SASL dialog
         * has completed, so just bubble back up the error message.
        /*
         * At this point we can't really send an abort since the SASL dialog
         * has completed, so just bubble back up the error message.
@@ -1408,6 +1479,20 @@ netsec_get_sasl_mechanism(netsec_context *nsc)
     return nsc->sasl_chosen_mech;
 }
 
     return nsc->sasl_chosen_mech;
 }
 
+/*
+ * Return the negotiated SASL strength security factor (SSF)
+ */
+
+int
+netsec_get_sasl_ssf(netsec_context *nsc)
+{
+#ifdef CYRUS_SASL
+    return nsc->sasl_ssf;
+#else /* CYRUS_SASL */
+    return 0;
+#endif /* CYRUS_SASL */
+}
+
 /*
  * Set an OAuth2 service name, if we support it.
  */
 /*
  * Set an OAuth2 service name, if we support it.
  */
@@ -1435,7 +1520,7 @@ netsec_set_tls(netsec_context *nsc, int tls, int noverify, char **errstr)
 #ifdef TLS_SUPPORT
     if (tls) {
        SSL *ssl;
 #ifdef TLS_SUPPORT
     if (tls) {
        SSL *ssl;
-       BIO *rbio, *wbio, *ssl_bio;;
+        BIO *rbio, *wbio, *ssl_bio;
 
        if (! tls_initialized) {
            SSL_library_init();
 
        if (! tls_initialized) {
            SSL_library_init();
@@ -1507,7 +1592,7 @@ netsec_set_tls(netsec_context *nsc, int tls, int noverify, char **errstr)
         * SSL BIO -> socket BIO.
         */
 
         * SSL BIO -> socket BIO.
         */
 
-       rbio = BIO_new_socket(nsc->ns_readfd, BIO_NOCLOSE);
+       rbio = BIO_new_socket(nsc->ns_readfd, BIO_CLOSE);
 
        if (! rbio) {
            netsec_err(errstr, "Unable to create a read socket BIO: %s",
 
        if (! rbio) {
            netsec_err(errstr, "Unable to create a read socket BIO: %s",
@@ -1516,7 +1601,7 @@ netsec_set_tls(netsec_context *nsc, int tls, int noverify, char **errstr)
            return NOTOK;
        }
 
            return NOTOK;
        }
 
-       wbio = BIO_new_socket(nsc->ns_writefd, BIO_NOCLOSE);
+       wbio = BIO_new_socket(nsc->ns_writefd, BIO_CLOSE);
 
        if (! wbio) {
            netsec_err(errstr, "Unable to create a write socket BIO: %s",
 
        if (! wbio) {
            netsec_err(errstr, "Unable to create a write socket BIO: %s",
@@ -1577,6 +1662,13 @@ netsec_set_tls(netsec_context *nsc, int tls, int noverify, char **errstr)
        BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
        nsc->ssl_io = ssl_bio;
 
        BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
        nsc->ssl_io = ssl_bio;
 
+       /*
+        * Since SSL now owns these file descriptors, have it handle the
+        * closing of them instead of netsec_shutdown().
+        */
+
+       nsc->ns_noclose = 1;
+
        return OK;
     }
     BIO_free_all(nsc->ssl_io);
        return OK;
     }
     BIO_free_all(nsc->ssl_io);
@@ -1623,7 +1715,7 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr)
            if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
                netsec_err(errstr, "Certificate verification failed, but "
                           "cannot retrieve SSL handle: %s",
            if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
                netsec_err(errstr, "Certificate verification failed, but "
                           "cannot retrieve SSL handle: %s",
-                          ERR_error_string(ERR_get_error(), NULL));
+                           ERR_error_string(ERR_get_error(), NULL));
            } else {
                netsec_err(errstr, "Server certificate verification failed: %s",
                           X509_verify_cert_error_string(
            } else {
                netsec_err(errstr, "Server certificate verification failed: %s",
                           X509_verify_cert_error_string(
@@ -1631,7 +1723,7 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr)
            }
        } else {
            netsec_err(errstr, "TLS negotiation failed: %s",
            }
        } else {
            netsec_err(errstr, "TLS negotiation failed: %s",
-                      ERR_error_string(ERR_get_error(), NULL));
+                       ERR_error_string(errcode, NULL));
        }
 
        /*
        }
 
        /*