X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/15c89585836a98f6340619a58a1ec0e7e1d83dfc..ec173fd2c:/uip/popsbr.c diff --git a/uip/popsbr.c b/uip/popsbr.c index 8e1394b9..237e80cc 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -1,24 +1,25 @@ -/* - * popsbr.c -- POP client subroutines +/* popsbr.c -- POP client subroutines * * This code is Copyright (c) 2002, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ -#include -#include -#include -#include +#include "h/mh.h" +#include "sbr/credentials.h" +#include "sbr/client.h" +#include "sbr/error.h" +#include "h/utils.h" +#include "h/oauth.h" +#include "h/netsec.h" -#include -#include +#include "popsbr.h" +#include "h/signals.h" +#include "sbr/base64.h" #define TRM "." -#define TRMLEN (sizeof TRM - 1) static int poprint = 0; -static int pophack = 0; char response[BUFSIZ]; static netsec_context *nsc = NULL; @@ -27,20 +28,22 @@ static netsec_context *nsc = NULL; * static prototypes */ -static int command(const char *, ...); +static int command(const char *, ...) CHECK_PRINTF(1, 2); static int multiline(void); -static int traverse (int (*)(char *), const char *, ...); -static int vcommand(const char *, va_list); +static int traverse(int (*)(void *, char *), void *closure, + const char *, ...) CHECK_PRINTF(3, 4); +static int vcommand(const char *, va_list) CHECK_PRINTF(1, 0); static int pop_getline (char *, int, netsec_context *); static int pop_sasl_callback(enum sasl_message_type, unsigned const char *, unsigned int, unsigned char **, unsigned int *, - char **); + void *, char **); static int check_mech(char *server_mechs, size_t server_mechs_size) { - int status, sasl_capability = 0; + int status; + bool sasl_capability = false; /* * First off, we're going to send the CAPA command to see if we can @@ -57,23 +60,16 @@ check_mech(char *server_mechs, size_t server_mechs_size) return NOTOK; } - while ((status = multiline()) != DONE) - switch (status) { - case NOTOK: + while ((status = multiline()) != DONE) { + if (status == NOTOK) return NOTOK; - break; - case DONE: /* Shouldn't be possible, but just in case */ - break; - case OK: - if (strncasecmp(response, "SASL ", 5) == 0) { - /* - * We've seen the SASL capability. Grab the mech list - */ - sasl_capability++; - strncpy(server_mechs, response + 5, server_mechs_size); - } - break; - } + + if (strncasecmp(response, "SASL ", 5) == 0) { + /* We've seen the SASL capability. Grab the mech list. */ + sasl_capability = true; + strncpy(server_mechs, response + 5, server_mechs_size); + } + } if (!sasl_capability) { snprintf(response, sizeof(response), "POP server does not support " @@ -84,12 +80,64 @@ check_mech(char *server_mechs, size_t server_mechs_size) return OK; } +/* + * If capable, issue the STLS command and start the TLS negotiation + */ + +static int +pop_start_tls(void) +{ + int status; + bool stls = false; + char *errstr; + + /* + * Issue the CAPA command and see if we have the STLS capability + */ + + if (command("CAPA") == NOTOK) { + snprintf(response, sizeof(response), + "The POP CAPA command failed; POP server does not " + "support STLS"); + return NOTOK; + } + + while ((status = multiline()) != DONE) { + if (status == NOTOK) + return NOTOK; + + if (strcasecmp(response, "STLS") == 0) + stls = true; + } + + if (!stls) { + snprintf(response, sizeof(response), "POP server does not support " + "STLS"); + return NOTOK; + } + + /* + * Issue STLS and then start the actual TLS negotiation + */ + + if (command("STLS") == NOTOK) + return NOTOK; + + if (netsec_negotiate_tls(nsc, &errstr) != OK) { + snprintf(response, sizeof(response), "%s", errstr); + free(errstr); + return NOTOK; + } + + return OK; +} + /* * Split string containing proxy command into an array of arguments * suitable for passing to exec. Returned array must be freed. Shouldn't * be possible to call this with host set to NULL. */ -char ** +static char ** parse_proxy(char *proxy, char *host) { char **pargv, **p; @@ -116,7 +164,7 @@ parse_proxy(char *proxy, char *host) /* put together list of arguments */ p = pargv = mh_xmalloc(pargc * sizeof(char *)); - c = *pargv = mh_xmalloc(plen * sizeof(char)); + c = *pargv = mh_xmalloc(plen); for (cur = pro; *cur; cur++) { if (isspace(*cur) && cur[1] && !isspace(cur[1])) { *c++ = '\0'; @@ -134,8 +182,8 @@ parse_proxy(char *proxy, char *host) } int -pop_init (char *host, char *port, char *user, char *pass, char *proxy, - int snoop, int sasl, char *mech, int tls, const char *oauth_svc) +pop_init (char *host, char *port, char *user, char *proxy, int snoop, + int sasl, char *mech, int tls, const char *oauth_svc) { int fd1, fd2; char buffer[BUFSIZ]; @@ -146,6 +194,8 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, if (user) netsec_set_userid(nsc, user); + netsec_set_hostname(nsc, host); + if (oauth_svc != NULL) { if (netsec_set_oauth_service(nsc, oauth_svc) != OK) { snprintf(response, sizeof(response), "OAuth2 not supported"); @@ -188,7 +238,7 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, close(1); free(*argv); free(argv); - exit(10); + exit(1); } /* okay in the parent we do some stuff */ @@ -213,23 +263,25 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, netsec_set_fd(nsc, fd1, fd2); netsec_set_snoop(nsc, snoop); - if (tls) { - if (netsec_set_tls(nsc, 1, &errstr) != OK) { + if (tls & P_TLSENABLEMASK) { + if (netsec_set_tls(nsc, 1, tls & P_NOVERIFY, &errstr) != OK) { snprintf(response, sizeof(response), "%s", errstr); free(errstr); return NOTOK; } - if (netsec_negotiate_tls(nsc, &errstr) != OK) { - snprintf(response, sizeof(response), "%s", errstr); - free(errstr); - return NOTOK; + if (tls & P_INITTLS) { + if (netsec_negotiate_tls(nsc, &errstr) != OK) { + snprintf(response, sizeof(response), "%s", errstr); + free(errstr); + return NOTOK; + } } } if (sasl) { - if (netsec_set_sasl_params(nsc, host, "pop", mech, - pop_sasl_callback, &errstr) != OK) { + if (netsec_set_sasl_params(nsc, "pop", mech, pop_sasl_callback, + NULL, &errstr) != OK) { snprintf(response, sizeof(response), "%s", errstr); free(errstr); return NOTOK; @@ -241,6 +293,12 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, if (poprint) fprintf (stderr, "<--- %s\n", response); if (*response == '+') { + nmh_creds_t creds; + + if (tls & P_STARTTLS) + if (pop_start_tls() != OK) + return NOTOK; + if (sasl) { char server_mechs[256]; if (check_mech(server_mechs, sizeof(server_mechs)) != OK) @@ -253,22 +311,32 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, return NOTOK; } return OK; - } else - if (command ("USER %s", user) != NOTOK - && command ("%s %s", (pophack++, "PASS"), - pass) != NOTOK) - return OK; + } + + if (!(creds = nmh_get_credentials(host, user))) + return NOTOK; + if (command ("USER %s", nmh_cred_get_user(creds)) + != NOTOK) { + if (command("PASS %s", nmh_cred_get_password(creds)) + != NOTOK) { + nmh_credentials_free(creds); + return OK; + } + } + nmh_credentials_free(creds); } strncpy (buffer, response, sizeof(buffer)); command ("QUIT"); strncpy (response, buffer, sizeof(response)); - /* and fall */ + /* FALLTHRU */ case NOTOK: case DONE: - if (poprint) - fprintf (stderr, "%s\n", response); - netsec_shutdown(nsc, 1); + if (poprint) { + fputs(response, stderr); + putc('\n', stderr); + } + netsec_shutdown(nsc); nsc = NULL; return NOTOK; } @@ -286,11 +354,12 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, static int pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, unsigned int indatalen, unsigned char **outdata, - unsigned int *outdatalen, char **errstr) + unsigned int *outdatalen, void *context, char **errstr) { int rc, snoopoffset; char *mech, *line; size_t len, b64len; + NMH_UNUSED(context); switch (mtype) { case NETSEC_SASL_START: @@ -332,25 +401,27 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, "for initial challenge response"); return NOTOK; } - netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL); rc = netsec_printf(nsc, errstr, "%s\r\n", b64data); - netsec_set_snoop_callback(nsc, NULL, NULL); free(b64data); if (rc != OK) return NOTOK; - if (netsec_flush(nsc, errstr) != OK) + netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL); + rc = netsec_flush(nsc, errstr); + netsec_set_snoop_callback(nsc, NULL, NULL); + if (rc != OK) return NOTOK; } else { - netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, - &snoopoffset); - snoopoffset = 6 + strlen(mech); rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data); free(b64data); - netsec_set_snoop_callback(nsc, NULL, NULL); if (rc != OK) return NOTOK; - if (netsec_flush(nsc, errstr) != OK) + netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, + &snoopoffset); + snoopoffset = 6 + strlen(mech); + rc = netsec_flush(nsc, errstr); + netsec_set_snoop_callback(nsc, NULL, NULL); + if (rc != OK) return NOTOK; } } else { @@ -403,16 +474,18 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, unsigned char *b64data; b64data = mh_xmalloc(BASE64SIZE(indatalen)); writeBase64raw(indata, indatalen, b64data); - netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL); rc = netsec_printf(nsc, errstr, "%s\r\n", b64data); - netsec_set_snoop_callback(nsc, NULL, NULL); free(b64data); } if (rc != OK) return NOTOK; - if (netsec_flush(nsc, errstr) != OK) + if (indatalen > 0) + netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL); + rc = netsec_flush(nsc, errstr); + netsec_set_snoop_callback(nsc, NULL, NULL); + if (rc != OK) return NOTOK; break; @@ -425,7 +498,7 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, if (line == NULL) return NOTOK; - if (strncmp(line, "+OK", 3) != 0) { + if (!has_prefix(line, "+OK")) { netsec_err(errstr, "Authentication failed: %s", line); return NOTOK; } @@ -472,68 +545,16 @@ pop_stat (int *nmsgs, int *nbytes) int -pop_list (int msgno, int *nmsgs, int *msgs, int *bytes) -{ - int i; - int *ids = NULL; - - if (msgno) { - if (command ("LIST %d", msgno) == NOTOK) - return NOTOK; - *msgs = *bytes = 0; - if (ids) { - *ids = 0; - sscanf (response, "+OK %d %d %d", msgs, bytes, ids); - } - else - sscanf (response, "+OK %d %d", msgs, bytes); - return OK; - } - - if (command ("LIST") == NOTOK) - return NOTOK; - - for (i = 0; i < *nmsgs; i++) - switch (multiline ()) { - case NOTOK: - return NOTOK; - case DONE: - *nmsgs = ++i; - return OK; - case OK: - *msgs = *bytes = 0; - if (ids) { - *ids = 0; - sscanf (response, "%d %d %d", - msgs++, bytes++, ids++); - } - else - sscanf (response, "%d %d", msgs++, bytes++); - break; - } - for (;;) - switch (multiline ()) { - case NOTOK: - return NOTOK; - case DONE: - return OK; - case OK: - break; - } -} - - -int -pop_retr (int msgno, int (*action)(char *)) +pop_retr (int msgno, int (*action)(void *, char *), void *closure) { - return traverse (action, "RETR %d", msgno); + return traverse (action, closure, "RETR %d", msgno); } static int -traverse (int (*action)(char *), const char *fmt, ...) +traverse (int (*action)(void *, char *), void *closure, const char *fmt, ...) { - int result; + int result, snoopstate; va_list ap; char buffer[sizeof(response)]; @@ -545,19 +566,24 @@ traverse (int (*action)(char *), const char *fmt, ...) return NOTOK; strncpy (buffer, response, sizeof(buffer)); - for (;;) - switch (multiline ()) { - case NOTOK: - return NOTOK; - - case DONE: - strncpy (response, buffer, sizeof(response)); - return OK; + if ((snoopstate = netsec_get_snoop(nsc))) + netsec_set_snoop(nsc, 0); + + for (;;) { + result = multiline(); + if (result == OK) { + result = (*action)(closure, response); + if (result == OK) + continue; + } else if (result == DONE) { + strncpy(response, buffer, sizeof(response)); + result = OK; + } + break; + } - case OK: - (*action) (response); - break; - } + netsec_set_snoop(nsc, snoopstate); + return result; } @@ -568,27 +594,6 @@ pop_dele (int msgno) } -int -pop_noop (void) -{ - return command ("NOOP"); -} - - -int -pop_rset (void) -{ - return command ("RSET"); -} - - -int -pop_top (int msgno, int lines, int (*action)(char *)) -{ - return traverse (action, "TOP %d %d", msgno, lines); -} - - int pop_quit (void) { @@ -605,13 +610,13 @@ int pop_done (void) { if (nsc) - netsec_shutdown(nsc, 1); + netsec_shutdown(nsc); return OK; } -int +static int command(const char *fmt, ...) { va_list ap; @@ -656,12 +661,14 @@ vcommand (const char *fmt, va_list ap) case OK: if (poprint) fprintf (stderr, "<--- %s\n", response); - return (*response == '+' ? OK : NOTOK); + return *response == '+' ? OK : NOTOK; case NOTOK: case DONE: - if (poprint) - fprintf (stderr, "%s\n", response); + if (poprint) { + fputs(response, stderr); + putc('\n', stderr); + } return NOTOK; } @@ -669,18 +676,17 @@ vcommand (const char *fmt, va_list ap) } -int +static int multiline (void) { - char buffer[BUFSIZ + TRMLEN]; + char buffer[BUFSIZ + LEN(TRM)]; if (pop_getline (buffer, sizeof buffer, nsc) != OK) return NOTOK; - if (strncmp (buffer, TRM, TRMLEN) == 0) { - if (buffer[TRMLEN] == 0) + if (has_prefix(buffer, TRM)) { + if (buffer[LEN(TRM)] == 0) return DONE; - else - strncpy (response, buffer + TRMLEN, sizeof(response)); + strncpy (response, buffer + LEN(TRM), sizeof(response)); } else strncpy (response, buffer, sizeof(response)); @@ -717,11 +723,11 @@ pop_getline (char *s, int n, netsec_context *ns) * * We get a length back from netsec_readline, but the rest of the POP * code doesn't handle it; the assumptions are that everything from - * the network can be respresented as C strings. That should get fixed + * the network can be represented as C strings. That should get fixed * someday. */ - destlen = len < ((size_t) (n - 1)) ? len : (size_t) (n - 1); + destlen = min(len, (size_t)(n - 1)); memcpy(s, p, destlen); s[destlen] = '\0';