]> diplodocus.org Git - nmh/blobdiff - sbr/netsec.c
Garbage collect the saslmaxssf flag, and update documentation for
[nmh] / sbr / netsec.c
index 5bfbea60ccc7b4d0f9f303b025eb2c8a1b2c951c..895892a29d29e1346c2ea4454d731bbfc78e027b 100644 (file)
@@ -54,8 +54,12 @@ static SSL_CTX *sslctx = NULL;               /* SSL Context */
  */
 
 struct _netsec_context {
  */
 
 struct _netsec_context {
-    int ns_fd;                 /* Descriptor for network connection */
+    int ns_readfd;             /* Read descriptor for network connection */
+    int ns_writefd;            /* Write descriptor for network connection */
     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 */
     int ns_timeout;            /* Network read timeout, in seconds */
     char *ns_userid;           /* Userid for authentication */
     unsigned char *ns_inbuffer;        /* Our read input buffer */
     int ns_timeout;            /* Network read timeout, in seconds */
     char *ns_userid;           /* Userid for authentication */
     unsigned char *ns_inbuffer;        /* Our read input buffer */
@@ -120,8 +124,12 @@ netsec_init(void)
 {
     netsec_context *nsc = mh_xmalloc(sizeof(*nsc));
 
 {
     netsec_context *nsc = mh_xmalloc(sizeof(*nsc));
 
-    nsc->ns_fd = -1;
+    nsc->ns_readfd = -1;
+    nsc->ns_writefd = -1;
     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_userid = NULL;
     nsc->ns_timeout = 60;      /* Our default */
     nsc->ns_inbufsize = NETSEC_BUFSIZE;
     nsc->ns_userid = NULL;
     nsc->ns_timeout = 60;      /* Our default */
     nsc->ns_inbufsize = NETSEC_BUFSIZE;
@@ -208,8 +216,12 @@ 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 && nsc->ns_fd != -1)
-       close(nsc->ns_fd);
+    if (closeflag) {
+       if (nsc->ns_readfd != -1)
+           close(nsc->ns_readfd);
+       if (nsc->ns_writefd != -1 && nsc->ns_writefd != nsc->ns_readfd)
+           close(nsc->ns_writefd);
+    }
 
     free(nsc);
 }
 
     free(nsc);
 }
@@ -219,9 +231,10 @@ netsec_shutdown(netsec_context *nsc, int closeflag)
  */
 
 void
  */
 
 void
-netsec_set_fd(netsec_context *nsc, int fd)
+netsec_set_fd(netsec_context *nsc, int readfd, int writefd)
 {
 {
-    nsc->ns_fd = fd;
+    nsc->ns_readfd = readfd;
+    nsc->ns_writefd = writefd;
 }
 
 /*
 }
 
 /*
@@ -254,6 +267,52 @@ netsec_set_snoop(netsec_context *nsc, int snoop)
     nsc->ns_snoop = snoop;
 }
 
     nsc->ns_snoop = snoop;
 }
 
+/*
+ * Set the snoop callback for this connection.
+ */
+
+void netsec_set_snoop_callback(netsec_context *nsc,
+                              netsec_snoop_callback callback, void *context)
+{
+    nsc->ns_snoop_cb = callback;
+    nsc->ns_snoop_context = context;
+}
+
+/*
+ * A base64-decoding snoop callback
+ */
+
+void
+netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len,
+                        void *context)
+{
+    const char *decoded;
+    size_t decodedlen;
+    NMH_UNUSED(nsc);
+    int offset;
+
+    offset = context ? *((int *) context) : 0;
+
+    if (offset > 0) {
+       /*
+        * Output non-base64 data first.
+        */
+       fprintf(stderr, "%.*s", offset, string);
+       string += offset;
+       len -= offset;
+    }
+
+    if (decodeBase64(string, &decoded, &decodedlen, 1, NULL) == OK) {
+       char *hexified;
+       hexify((const unsigned char *) decoded, decodedlen, &hexified);
+       fprintf(stderr, "b64<%s>\n", hexified);
+       free(hexified);
+       free((char *) decoded);
+    } else {
+       fprintf(stderr, "%.*s\n", (int) len, string);
+    }
+}
+
 /*
  * Set the read timeout for this connection
  */
 /*
  * Set the read timeout for this connection
  */
@@ -342,6 +401,22 @@ retry:
                *len = ptr - nsc->ns_inptr;
            nsc->ns_inptr += count;
            nsc->ns_inbuflen -= count;
                *len = ptr - nsc->ns_inptr;
            nsc->ns_inptr += count;
            nsc->ns_inbuflen -= count;
+           if (nsc->ns_snoop) {
+#ifdef CYRUS_SASL
+               if (nsc->sasl_seclayer)
+                   fprintf(stderr, "(sasl-decrypted) ");
+#endif /* CYRUS_SASL */
+#ifdef TLS_SUPPORT
+               if (nsc->tls_active)
+                   fprintf(stderr, "(tls-decrypted) ");
+#endif /* TLS_SUPPORT */
+               fprintf(stderr, "<= ");
+               if (nsc->ns_snoop_cb)
+                   nsc->ns_snoop_cb(nsc, sptr, strlen(sptr),
+                                    nsc->ns_snoop_context);
+               else
+                   fprintf(stderr, "%s\n", sptr);
+           }
            return sptr;
        }
     }
            return sptr;
        }
     }
@@ -408,12 +483,12 @@ retry:
        fd_set rfds;
 
        FD_ZERO(&rfds);
        fd_set rfds;
 
        FD_ZERO(&rfds);
-       FD_SET(nsc->ns_fd, &rfds);
+       FD_SET(nsc->ns_readfd, &rfds);
 
        tv.tv_sec = nsc->ns_timeout;
        tv.tv_usec = 0;
 
 
        tv.tv_sec = nsc->ns_timeout;
        tv.tv_usec = 0;
 
-       rc = select(nsc->ns_fd + 1, &rfds, NULL, NULL, &tv);
+       rc = select(nsc->ns_readfd + 1, &rfds, NULL, NULL, &tv);
 
        if (rc == -1) {
            netsec_err(errstr, "select() while reading failed: %s",
 
        if (rc == -1) {
            netsec_err(errstr, "select() while reading failed: %s",
@@ -485,7 +560,7 @@ retry:
      * select() above) so this read SHOULDN'T block.  Hopefully.
      */
 
      * select() above) so this read SHOULDN'T block.  Hopefully.
      */
 
-    rc = read(nsc->ns_fd, readbuf, readbufsize);
+    rc = read(nsc->ns_readfd, readbuf, readbufsize);
 
     if (rc == 0) {
        netsec_err(errstr, "Received EOF on network read");
 
     if (rc == 0) {
        netsec_err(errstr, "Received EOF on network read");
@@ -568,25 +643,6 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size,
     if (size == 0)
        return OK;
 
     if (size == 0)
        return OK;
 
-    /*
-     * If TLS is active, then bypass all of our buffering logic; just
-     * write it directly to our BIO.  We have a buffering BIO first in
-     * our stack, so buffering will take place there.
-     */
-#ifdef TLS_SUPPORT
-    if (nsc->tls_active) {
-       rc = BIO_write(nsc->ssl_io, buffer, size);
-
-       if (rc <= 0) {
-           netsec_err(errstr, "Error writing to TLS connection: %s",
-                      ERR_error_string(ERR_get_error(), NULL));
-           return NOTOK;
-       }
-
-       return OK;
-    }
-#endif /* TLS_SUPPORT */
-
     /*
      * Run a loop copying in data to our local buffer; when we're done with
      * any buffer overflows then just copy any remaining data in.
     /*
      * Run a loop copying in data to our local buffer; when we're done with
      * any buffer overflows then just copy any remaining data in.
@@ -653,23 +709,6 @@ netsec_vprintf(netsec_context *nsc, char **errstr, const char *format,
 {
     int rc;
 
 {
     int rc;
 
-    /*
-     * Again, if we're using TLS, then bypass our local buffering
-     */
-#ifdef TLS_SUPPORT
-    if (nsc->tls_active) {
-       rc = BIO_vprintf(nsc->ssl_io, format, ap);
-
-       if (rc <= 0) {
-           netsec_err(errstr, "Error writing to TLS connection: %s",
-                      ERR_error_string(ERR_get_error(), NULL));
-           return NOTOK;
-       }
-
-       return OK;
-    }
-#endif /* TLS_SUPPORT */
-
     /*
      * Cheat a little.  If we can fit the data into our outgoing buffer,
      * great!  If not, generate a flush and retry once.
     /*
      * Cheat a little.  If we can fit the data into our outgoing buffer,
      * great!  If not, generate a flush and retry once.
@@ -711,6 +750,35 @@ 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;
 
@@ -729,24 +797,6 @@ netsec_flush(netsec_context *nsc, char **errstr)
     unsigned int netoutlen = nsc->ns_outbuflen;
     int rc;
 
     unsigned int netoutlen = nsc->ns_outbuflen;
     int rc;
 
-    /*
-     * For TLS connections, just call BIO_flush(); we'll let TLS handle
-     * all of our output buffering.
-     */
-#ifdef TLS_SUPPORT
-    if (nsc->tls_active) {
-       rc = BIO_flush(nsc->ssl_io);
-
-       if (rc <= 0) {
-           netsec_err(errstr, "Error flushing TLS connection: %s",
-                      ERR_error_string(ERR_get_error(), NULL));
-           return NOTOK;
-       }
-
-       return OK;
-    }
-#endif /* TLS_SUPPORT */
-
     /*
      * Small optimization
      */
     /*
      * Small optimization
      */
@@ -756,7 +806,7 @@ netsec_flush(netsec_context *nsc, char **errstr)
 
     /*
      * If SASL security layers are in effect, run the data through
 
     /*
      * If SASL security layers are in effect, run the data through
-     * sasl_encode() first and then write it.
+     * sasl_encode() first.
      */
 #ifdef CYRUS_SASL
     if (nsc->sasl_seclayer) {
      */
 #ifdef CYRUS_SASL
     if (nsc->sasl_seclayer) {
@@ -771,11 +821,27 @@ netsec_flush(netsec_context *nsc, char **errstr)
 
     }
 #endif /* CYRUS_SASL */
 
     }
 #endif /* CYRUS_SASL */
-    rc = write(nsc->ns_fd, netoutbuf, netoutlen);
 
 
-    if (rc < 0) {
-       netsec_err(errstr, "write() failed: %s", strerror(errno));
-       return NOTOK;
+    /*
+     * If TLS is active, then use those functions to write out the
+     * data.
+     */
+#ifdef TLS_SUPPORT
+    if (nsc->tls_active) {
+       if (BIO_write(nsc->ssl_io, netoutbuf, netoutlen) <= 0) {
+           netsec_err(errstr, "Error writing to TLS connection: %s",
+                      ERR_error_string(ERR_get_error(), NULL));
+           return NOTOK;
+       }
+    } else
+#endif /* TLS_SUPPORT */
+    {
+       rc = write(nsc->ns_writefd, netoutbuf, netoutlen);
+
+       if (rc < 0) {
+           netsec_err(errstr, "write() failed: %s", strerror(errno));
+           return NOTOK;
+       }
     }
 
     nsc->ns_outptr = nsc->ns_outbuffer;
     }
 
     nsc->ns_outptr = nsc->ns_outbuffer;
@@ -841,7 +907,21 @@ netsec_set_sasl_params(netsec_context *nsc, const char *hostname,
        return NOTOK;
     }
 
        return NOTOK;
     }
 
-    nsc->sasl_mech = mechanism ? getcpy(mechanism) : NULL;
+    /*
+     * According to the RFC, mechanisms can only be uppercase letter, numbers,
+     * and a hypen or underscore.  So make sure we uppercase any letters
+     * in case the user passed in lowercase.
+     */
+
+    if (mechanism) {
+       char *p;
+       nsc->sasl_mech = getcpy(mechanism);
+
+       for (p = nsc->sasl_mech; *p; p++)
+           if (isascii((unsigned char) *p))    /* Just in case */
+               *p = toupper((unsigned char) *p);
+    }
+
     nsc->sasl_proto_cb = callback;
     nsc->sasl_hostname = getcpy(hostname);
 
     nsc->sasl_proto_cb = callback;
     nsc->sasl_hostname = getcpy(hostname);
 
@@ -974,7 +1054,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
     int *outbufmax;
 #endif
 #ifdef OAUTH_SUPPORT
     int *outbufmax;
 #endif
 #ifdef OAUTH_SUPPORT
-    const char *xoauth_client_res;
+    unsigned char *xoauth_client_res;
+    size_t xoauth_client_res_len;
 #endif /* OAUTH_SUPPORT */
     int rc;
 
 #endif /* OAUTH_SUPPORT */
     int rc;
 
@@ -997,7 +1078,6 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
 
        i = (str[i] == NULL);
 
 
        i = (str[i] == NULL);
 
-       free(str);
        free(mlist);
 
        if (i) {
        free(mlist);
 
        if (i) {
@@ -1008,11 +1088,11 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
     }
 
 #ifdef OAUTH_SUPPORT
     }
 
 #ifdef OAUTH_SUPPORT
-    if (strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) {
+    if (nsc->sasl_mech && strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) {
        /*
         * This should be relatively straightforward, but requires some
         * help from the plugin.  Basically, if XOAUTH2 is a success,
        /*
         * This should be relatively straightforward, but requires some
         * help from the plugin.  Basically, if XOAUTH2 is a success,
-        * the plugin has to return success, but no output data.  If
+        * the callback has to return success, but no output data.  If
         * there is output data, it will be assumed that it is the JSON
         * error message.
         */
         * there is output data, it will be assumed that it is the JSON
         * error message.
         */
@@ -1024,19 +1104,46 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
 
        nsc->sasl_chosen_mech = getcpy(nsc->sasl_mech);
 
 
        nsc->sasl_chosen_mech = getcpy(nsc->sasl_mech);
 
-       xoauth_client_res = mh_oauth_do_xoauth(nsc->ns_userid,
-                                              nsc->oauth_service,
-                                              nsc->ns_snoop ? stderr : NULL);
-
-       if (xoauth_client_res == NULL) {
-           netsec_err(errstr, "Internal error: mh_oauth_do_xoauth() "
-                      "returned NULL");
+       if (mh_oauth_do_xoauth(nsc->ns_userid, nsc->oauth_service,
+                              &xoauth_client_res, &xoauth_client_res_len,
+                              nsc->ns_snoop ? stderr : NULL) != OK) {
+           netsec_err(errstr, "Internal error: Unable to get OAuth2 "
+                      "bearer token");
            return NOTOK;
        }
 
            return NOTOK;
        }
 
-#if 0
-       rc = nsc->sasl_proto_cb(NETSEC_SASL_START, 
-#endif
+       rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res,
+                               xoauth_client_res_len, NULL, 0, errstr);
+       free(xoauth_client_res);
+
+       if (rc != OK)
+           return NOTOK;
+
+       /*
+        * Okay, we need to do a NETSEC_SASL_FINISH now.  If we return
+        * success, we indicate that with no output data.  But if we
+        * fail, then send a blank message and get the resulting
+        * error.
+        */
+
+       rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, errstr);
+
+       if (rc != OK) {
+           /*
+            * We're going to assume the error here is a JSON response;
+            * we ignore it and send a blank message in response.  We should
+            * then get either an +OK or -ERR
+            */
+           free(*errstr);
+           nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, NULL);
+           rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
+                                   errstr);
+           if (rc == 0) {
+               netsec_err(errstr, "Unexpected success after OAuth failure!");
+           }
+           return NOTOK;
+       }
+       return OK;
     }
 #endif /* OAUTH_SUPPORT */
 
     }
 #endif /* OAUTH_SUPPORT */
 
@@ -1087,21 +1194,10 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
 
     nsc->sasl_chosen_mech = getcpy(chosen_mech);
 
 
     nsc->sasl_chosen_mech = getcpy(chosen_mech);
 
-    if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, &outbuf,
-                          &outbuflen, errstr) != OK)
+    if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0,
+                          errstr) != OK)
        return NOTOK;
 
        return NOTOK;
 
-    if (outbuflen > 0) {
-       if (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) {
-           free(outbuf);
-           return NOTOK;
-       }
-       free(outbuf);
-       if (netsec_flush(nsc, errstr) != OK)
-           return NOTOK;
-    }
-
-
     /*
      * We've written out our first message; enter in the step loop
      */
     /*
      * We've written out our first message; enter in the step loop
      */
@@ -1114,14 +1210,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen,
                               errstr) != OK) {
 
        if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen,
                               errstr) != OK) {
-           if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf,
-                                  &outbuflen, NULL) == OK) {
-               if (outbuflen > 0) {
-                   netsec_write(nsc, outbuf, outbuflen, NULL);
-                   netsec_flush(nsc, NULL);
-                   free(outbuf);
-               }
-           }
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL);
            return NOTOK;
        }
 
            return NOTOK;
        }
 
@@ -1134,39 +1223,15 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
        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));
-           if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf,
-                                  &outbuflen, NULL) == OK) {
-               if (outbuflen > 0) {
-                   netsec_write(nsc, outbuf, outbuflen, NULL);
-                   netsec_flush(nsc, NULL);
-                   free(outbuf);
-               }
-           }
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, 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,
-                              &outbuf, &outbuflen, errstr) != OK) {
-           if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf,
-                                  &outbuflen, NULL) == OK) {
-               if (outbuflen > 0) {
-                   netsec_write(nsc, outbuf, outbuflen, NULL);
-                   netsec_flush(nsc, NULL);
-                   free(outbuf);
-               }
-           }
+                              NULL, 0, errstr) != OK) {
+           nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL);
            return NOTOK;
        }
            return NOTOK;
        }
-
-       if (outbuflen > 0) {
-           if (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) {
-               free(outbuf);
-               return NOTOK;
-           }
-           free(outbuf);
-           if (netsec_flush(nsc, errstr) != OK)
-               return NOTOK;
-       }
     }
 
     /*
     }
 
     /*
@@ -1301,7 +1366,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr)
     if (tls) {
 #ifdef TLS_SUPPORT
        SSL *ssl;
     if (tls) {
 #ifdef TLS_SUPPORT
        SSL *ssl;
-       BIO *sbio, *ssl_bio;;
+       BIO *rbio, *wbio, *ssl_bio;;
 
        if (! tls_initialized) {
            SSL_library_init();
 
        if (! tls_initialized) {
            SSL_library_init();
@@ -1327,7 +1392,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr)
            tls_initialized++;
        }
 
            tls_initialized++;
        }
 
-       if (nsc->ns_fd == -1) {
+       if (nsc->ns_readfd == -1 || nsc->ns_writefd == -1) {
            netsec_err(errstr, "Invalid file descriptor in netsec context");
            return NOTOK;
        }
            netsec_err(errstr, "Invalid file descriptor in netsec context");
            return NOTOK;
        }
@@ -1367,27 +1432,28 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr)
         * So writes and reads are buffered (we mostly care about writes).
         */
 
         * So writes and reads are buffered (we mostly care about writes).
         */
 
-       sbio = BIO_new_socket(nsc->ns_fd, BIO_NOCLOSE);
+       rbio = BIO_new_socket(nsc->ns_readfd, BIO_NOCLOSE);
 
 
-       if (! sbio) {
-           netsec_err(errstr, "Unable to create a socket BIO: %s",
+       if (! rbio) {
+           netsec_err(errstr, "Unable to create a read socket BIO: %s",
                       ERR_error_string(ERR_get_error(), NULL));
            SSL_free(ssl);
            return NOTOK;
        }
 
                       ERR_error_string(ERR_get_error(), NULL));
            SSL_free(ssl);
            return NOTOK;
        }
 
-       SSL_set_bio(ssl, sbio, sbio);
-       SSL_set_connect_state(ssl);
-
-       nsc->ssl_io = BIO_new(BIO_f_buffer());
+       wbio = BIO_new_socket(nsc->ns_writefd, BIO_NOCLOSE);
 
 
-       if (! nsc->ssl_io) {
-           netsec_err(errstr, "Unable to create a buffer BIO: %s",
+       if (! wbio) {
+           netsec_err(errstr, "Unable to create a write socket BIO: %s",
                       ERR_error_string(ERR_get_error(), NULL));
            SSL_free(ssl);
                       ERR_error_string(ERR_get_error(), NULL));
            SSL_free(ssl);
+           BIO_free(rbio);
            return NOTOK;
        }
 
            return NOTOK;
        }
 
+       SSL_set_bio(ssl, rbio, wbio);
+       SSL_set_connect_state(ssl);
+
        ssl_bio = BIO_new(BIO_f_ssl());
 
        if (! ssl_bio) {
        ssl_bio = BIO_new(BIO_f_ssl());
 
        if (! ssl_bio) {
@@ -1398,7 +1464,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr)
        }
 
        BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
        }
 
        BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
-       BIO_push(nsc->ssl_io, ssl_bio);
+       nsc->ssl_io = ssl_bio;
 
        return OK;
     } else {
 
        return OK;
     } else {
@@ -1441,7 +1507,7 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr)
            fprintf(stderr, "WARNING: cannot determine SSL ciphers\n");
        } else {
            const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
            fprintf(stderr, "WARNING: cannot determine SSL ciphers\n");
        } else {
            const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
-           fprintf(stderr, "TLS negotation successful: %s(%d) %s\n",
+           fprintf(stderr, "TLS negotiation successful: %s(%d) %s\n",
                    SSL_CIPHER_get_name(cipher),
                    SSL_CIPHER_get_bits(cipher, NULL),
                    SSL_CIPHER_get_version(cipher));
                    SSL_CIPHER_get_name(cipher),
                    SSL_CIPHER_get_bits(cipher, NULL),
                    SSL_CIPHER_get_version(cipher));
@@ -1450,16 +1516,6 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr)
 
     nsc->tls_active = 1;
 
 
     nsc->tls_active = 1;
 
-    /*
-     * At this point, TLS has been activated; we're not going to use
-     * the output buffer, so free it now to save a little bit of memory.
-     */
-
-    if (nsc->ns_outbuffer) {
-       free(nsc->ns_outbuffer);
-       nsc->ns_outbuffer = NULL;
-    }
-
     return OK;
 #else /* TLS_SUPPORT */
     netsec_err(errstr, "TLS not supported");
     return OK;
 #else /* TLS_SUPPORT */
     netsec_err(errstr, "TLS not supported");