]> diplodocus.org Git - nmh/blobdiff - sbr/netsec.c
Fix invalid pointer arithmetic.
[nmh] / sbr / netsec.c
index eb92dfcdfe0382ae81854dc5a307bf96cf7a58a4..ab49046745bbb4040e3ebb2053749950b323c040 100644 (file)
@@ -57,9 +57,9 @@ struct _netsec_context {
     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_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 */
+    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 */
@@ -74,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 */
+    void *sasl_proto_context;  /* Context to be used by SASL callback */
 #ifdef OAUTH_SUPPORT
     char *oauth_service;       /* OAuth2 service name */
 #endif /* OAUTH_SUPPORT */
@@ -144,9 +145,9 @@ netsec_init(void)
     nsc->ns_writefd = -1;
     nsc->ns_noclose = 0;
     nsc->ns_snoop = 0;
-    nsc->ns_snoop_noend = 0;
     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 */
@@ -161,6 +162,7 @@ netsec_init(void)
     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 */
@@ -194,6 +196,7 @@ netsec_shutdown(netsec_context *nsc)
     free(nsc->ns_outbuffer);
     free(nsc->sasl_mech);
     free(nsc->sasl_chosen_mech);
+    free(nsc->ns_snoop_savebuf);
 #ifdef OAUTH_SERVICE
     free(nsc->oauth_service);
 #endif /* OAUTH_SERVICE */
@@ -844,35 +847,6 @@ 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;
 
@@ -898,6 +872,95 @@ netsec_flush(netsec_context *nsc, char **errstr)
     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.
@@ -951,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,
-                      char **errstr)
+                      void *context, char **errstr)
 {
 #ifdef CYRUS_SASL
     sasl_callback_t *sasl_cbs;
@@ -1024,15 +1087,12 @@ netsec_set_sasl_params(netsec_context *nsc, const char *service,
      */
 
     if (mechanism) {
-       char *p;
        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_context = context;
 
     return OK;
 }
@@ -1177,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,
-                               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)
@@ -1190,7 +1251,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
         * 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) {
            /*
@@ -1200,9 +1262,10 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **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,
-                                   errstr);
+                                   nsc->sasl_proto_context, errstr);
            if (rc == 0) {
                netsec_err(errstr, "Unexpected success after OAuth failure!");
            }
@@ -1260,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,
-                          errstr) != OK)
+                          nsc->sasl_proto_context, errstr) != OK)
        return NOTOK;
 
     /*
@@ -1274,8 +1337,9 @@ 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) {
-           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;
        }
 
@@ -1287,13 +1351,16 @@ 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));
-           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,
-                              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;
        }
     }
@@ -1304,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,
-                          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.
@@ -1412,6 +1479,20 @@ netsec_get_sasl_mechanism(netsec_context *nsc)
     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.
  */