]> diplodocus.org Git - nmh/blobdiff - uip/popsbr.c
Added context_find_prefix().
[nmh] / uip / popsbr.c
index ff28705f07af58d4b3d931042279aaaca0f7f886..ccc76c7c40ead4ba3eb72196f1e6314889928fb6 100644 (file)
 #ifdef CYRUS_SASL
 # include <sasl/sasl.h>
 # include <sasl/saslutil.h>
+# if SASL_VERSION_FULL < 0x020125
+    /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
+       which has an explicit void parameter list, according to best
+       practice.  So we need to cast to avoid compile warnings.
+       Provide this prototype for earlier versions. */
+    typedef int (*sasl_callback_ft)();
+# endif /* SASL_VERSION_FULL < 0x020125 */
 #endif /* CYRUS_SASL */
 
 #include <h/popsbr.h>
 #include <h/signals.h>
-#include <signal.h>
-#include <errno.h>
 
 #define        TRM     "."
 #define        TRMLEN  (sizeof TRM - 1)
@@ -39,13 +44,14 @@ static int sasl_get_user(void *, int, const char **, unsigned *);
 static int sasl_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
 struct pass_context {
     char *user;
+    char *password;
     char *host;
 };
 
 static sasl_callback_t callbacks[] = {
-    { SASL_CB_USER, sasl_get_user, NULL },
+    { SASL_CB_USER, (sasl_callback_ft) sasl_get_user, NULL },
 #define POP_SASL_CB_N_USER 0
-    { SASL_CB_PASS, sasl_get_pass, NULL },
+    { SASL_CB_PASS, (sasl_callback_ft) sasl_get_pass, NULL },
 #define POP_SASL_CB_N_PASS 1
     { SASL_CB_LOG, NULL, NULL },
     { SASL_CB_LIST_END, NULL, NULL },
@@ -83,12 +89,20 @@ static int putline (char *, FILE *);
  * layer.
  */
 
+#define CHECKB64SIZE(insize, outbuf, outsize) \
+    { size_t wantout = (((insize + 2) / 3) * 4) + 32; \
+      if (wantout > outsize) { \
+          outbuf = mh_xrealloc(outbuf, outsize = wantout); \
+      } \
+    }
+
 int
 pop_auth_sasl(char *user, char *host, char *mech)
 {
     int result, status, sasl_capability = 0;
     unsigned int buflen, outlen;
-    char server_mechs[256], *buf, outbuf[BUFSIZ];
+    char server_mechs[256], *buf, *outbuf = NULL;
+    size_t outbufsize = 0;
     const char *chosen_mech;
     sasl_security_properties_t secprops;
     struct pass_context p_context;
@@ -203,10 +217,13 @@ pop_auth_sasl(char *user, char *host, char *mech)
     }
 
     if (buflen) {
-       status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+       CHECKB64SIZE(buflen, outbuf, outbufsize);
+       status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
        if (status != SASL_OK) {
            snprintf(response, sizeof(response), "SASL base64 encode "
                     "failed: %s", sasl_errstring(status, NULL, NULL));
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
        }
 
@@ -215,8 +232,13 @@ pop_auth_sasl(char *user, char *host, char *mech)
        status = command("AUTH %s", chosen_mech);
 
     while (result == SASL_CONTINUE) {
-       if (status == NOTOK)
+       size_t inlen;
+
+       if (status == NOTOK) {
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
+       }
        
        /*
         * If we get a "+OK" prefix to our response, then we should
@@ -235,16 +257,31 @@ pop_auth_sasl(char *user, char *host, char *mech)
            command("*");
            snprintf(response, sizeof(response),
                     "Malformed authentication message from server");
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
        }
 
+       /*
+        * For decode, it will always be shorter, so just make sure
+        * that outbuf is as at least as big as the encoded response.
+        */
+
+       inlen = strlen(response + 2);
+
+       if (inlen > outbufsize) {
+           outbuf = mh_xrealloc(outbuf, outbufsize = inlen);
+       }
+
        result = sasl_decode64(response + 2, strlen(response + 2),
-                              outbuf, sizeof(outbuf), &outlen);
+                              outbuf, outbufsize, &outlen);
        
        if (result != SASL_OK) {
            command("*");
            snprintf(response, sizeof(response), "SASL base64 decode "
                     "failed: %s", sasl_errstring(result, NULL, NULL));
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
        }
 
@@ -255,21 +292,30 @@ pop_auth_sasl(char *user, char *host, char *mech)
            command("*");
            snprintf(response, sizeof(response), "SASL client negotiaton "
                     "failed: %s", sasl_errdetail(conn));
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
        }
 
-       status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
+       CHECKB64SIZE(buflen, outbuf, outbufsize);
+
+       status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
 
        if (status != SASL_OK) {
            command("*");
            snprintf(response, sizeof(response), "SASL base64 encode "
                     "failed: %s", sasl_errstring(status, NULL, NULL));
+           if (outbuf)
+               free(outbuf);
            return NOTOK;
        }
 
        status = command(outbuf);
     }
 
+    if (outbuf)
+       free(outbuf);
+
     /*
      * If we didn't get a positive final response, then error out
      * (that probably means we failed an authorization check).
@@ -343,23 +389,34 @@ sasl_get_user(void *context, int id, const char **result, unsigned *len)
 static int
 sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
 {
-    NMH_UNUSED (conn);
-
     struct pass_context *p_context = (struct pass_context *) context;
-    char *pass = NULL;
+    struct nmh_creds creds = { 0, 0, 0 };
     int len;
 
+    NMH_UNUSED (conn);
+
     if (! psecret || id != SASL_CB_PASS)
        return SASL_BADPARAM;
 
-    ruserpass(p_context->user, &(p_context->host), &pass);
+    if (creds.password == NULL) {
+        /*
+         * Pass the 0 third argument to nmh_get_credentials() so
+         * that the default password isn't used.  With legacy/.netrc
+         * credentials support, we'll only get here if the -user
+         * switch to send(1)/post(8) wasn't used.
+         */
+        if (nmh_get_credentials (p_context->host, p_context->user, 0, &creds)
+            != OK) {
+            return SASL_BADPARAM;
+        }
+    }
 
-    len = strlen(pass);
+    len = strlen (creds.password);
 
     *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
 
     (*psecret)->len = len;
-    strcpy((char *) (*psecret)->data, pass);
+    strcpy((char *) (*psecret)->data, creds.password);
 
     return SASL_OK;
 }
@@ -410,6 +467,7 @@ parse_proxy(char *proxy, char *host)
        } else if (!isspace(*cur))
            *c++ = *cur;
     }
+    *c = '\0';
     *++p = NULL;
     return pargv;
 }
@@ -430,11 +488,12 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy,
        int inpipe[2];    /* for reading from the server */
        int outpipe[2];    /* for sending to the server */
 
-       /* first give up any root priviledges we may have for rpop */
-       setuid(getuid());
-
-       pipe(inpipe);
-       pipe(outpipe);
+       if (pipe(inpipe) < 0) {
+          adios ("inpipe", "pipe");
+       }
+       if (pipe(outpipe) < 0) {
+          adios ("outpipe", "pipe");
+       }
 
        pid=fork();
        if (pid==0) {
@@ -739,9 +798,10 @@ command(const char *fmt, ...)
 static int
 vcommand (const char *fmt, va_list ap)
 {
-    char *cp, buffer[BUFSIZ];
+    char *cp, buffer[65536];
 
     vsnprintf (buffer, sizeof(buffer), fmt, ap);
+
     if (poprint) {
 #ifdef CYRUS_SASL
        if (sasl_ssf)
@@ -840,7 +900,7 @@ sasl_getline (char *s, int n, FILE *iop)
     *p = 0;
     if (*--p == '\n')
        *p = 0;
-    if (*--p == '\r')
+    if (p > s  &&  *--p == '\r')
        *p = 0;
 
     return OK;
@@ -877,7 +937,9 @@ putline (char *s, FILE *iop)
            return NOTOK;
        }
 
-       fwrite(buf, buflen, 1, iop);
+       if (fwrite(buf, buflen, 1, iop) < 1) {
+           advise ("putline", "fwrite");
+       }
     }
 #endif /* CYRUS_SASL */