X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/36672e5977cdfee454f5ccb1946a19cd43ec5ba0..ef0725fd97369e801a56febfdb7a6ec2eaff73c8:/uip/popsbr.c diff --git a/uip/popsbr.c b/uip/popsbr.c index d79c8b3d..d61ce767 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -5,13 +5,16 @@ * complete copyright information. */ -#include -#include -#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 "popsbr.h" +#include "h/signals.h" #include "sbr/base64.h" #define TRM "." @@ -34,12 +37,13 @@ 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 @@ -62,7 +66,7 @@ check_mech(char *server_mechs, size_t server_mechs_size) if (strncasecmp(response, "SASL ", 5) == 0) { /* We've seen the SASL capability. Grab the mech list. */ - sasl_capability++; + sasl_capability = true; strncpy(server_mechs, response + 5, server_mechs_size); } } @@ -76,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; @@ -207,23 +263,25 @@ pop_init (char *host, char *port, char *user, char *proxy, int snoop, netsec_set_fd(nsc, fd1, fd2); netsec_set_snoop(nsc, snoop); - if (tls & P_INITTLS) { + 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, "pop", mech, pop_sasl_callback, - &errstr) != OK) { + NULL, &errstr) != OK) { snprintf(response, sizeof(response), "%s", errstr); free(errstr); return NOTOK; @@ -237,6 +295,10 @@ pop_init (char *host, char *port, char *user, char *proxy, int snoop, 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) @@ -292,11 +354,12 @@ pop_init (char *host, char *port, char *user, char *proxy, int snoop, 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: @@ -338,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 { @@ -389,7 +454,7 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, *outdata = NULL; *outdatalen = 0; } else { - rc = decodeBase64(line + 2, outdata, &len, 0, NULL); + rc = decodeBase64(line + 2, outdata, &len, 0); *outdatalen = len; if (rc != OK) { netsec_err(errstr, "Unable to decode base64 response"); @@ -409,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; @@ -549,7 +616,7 @@ pop_done (void) } -int +static int command(const char *fmt, ...) { va_list ap; @@ -609,7 +676,7 @@ vcommand (const char *fmt, va_list ap) } -int +static int multiline (void) { char buffer[BUFSIZ + LEN(TRM)];