From: Ken Hornstein Date: Mon, 19 Sep 2016 02:03:18 +0000 (-0400) Subject: Okay, we're finally getting to implementation stage! Hopefully we'll be X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/4ea2f92f36fcf39727438cd55580c99a5a6e6ad8?ds=sidebyside;hp=-c Okay, we're finally getting to implementation stage! Hopefully we'll be able to make it all work with POP first, then SMTP. --- 4ea2f92f36fcf39727438cd55580c99a5a6e6ad8 diff --git a/h/netsec.h b/h/netsec.h index ad50d9f7..65b867bd 100644 --- a/h/netsec.h +++ b/h/netsec.h @@ -22,9 +22,10 @@ netsec_context *netsec_init(void); * Arguments: * * ns_context - Network security context + * closeflag - If set to 1, close the socket descriptor as well. */ -void netsec_shutdown(netsec_context *ns_context); +void netsec_shutdown(netsec_context *ns_context, int closeflag); /* * Sets the file descriptor for this connection. This will be used by @@ -151,6 +152,22 @@ int netsec_write(netsec_context *ns_context, const void *buffer, size_t size, int netsec_printf(netsec_context *ns_context, char **errstr, const char *format, ...); +/* + * Write bytes using a va_list argument. + * + * Arguments: + * + * ns_context - Network security context + * errstr - Error string + * format - Format string + * ap - stdarg list. + * + * Returns OK on success, NOTOK on error. + */ + +int netsec_vprintf(netsec_context *ns_context, char **errstr, + const char *format, va_list ap); + /* * Flush any buffered bytes to the network. * @@ -244,7 +261,6 @@ typedef int (*netsec_sasl_callback)(enum sasl_message_type mtype, * ns_context - Network security context * hostname - Fully qualified hostname of remote host. * service - Service name (set to NULL to disable SASL). - * mechlist - A space-separated list of mechanisms supported by the server. * mechanism - The mechanism desired by the user. If NULL, the SASL * library will attempt to negotiate the best mechanism. * callback - SASL protocol callbacks diff --git a/sbr/netsec.c b/sbr/netsec.c index cb297cc1..372732c3 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -65,9 +66,12 @@ struct _netsec_context { unsigned char *ns_outptr; /* Output buffer pointer */ unsigned int ns_outbuflen; /* Output buffer data length */ unsigned int ns_outbufsize; /* Output buffer size */ + char *sasl_mech; /* User-requested mechanism */ +#ifdef OAUTH_SUPPORT + char *oauth_service; /* OAuth2 service name */ +#endif /* OAUTH_SUPPORT */ #ifdef CYRUS_SASL char *sasl_hostname; /* Hostname we've connected to */ - char *sasl_mech; /* User-requested mechanism */ sasl_conn_t *sasl_conn; /* SASL connection context */ sasl_ssf_t sasl_ssf; /* SASL Security Strength Factor */ netsec_sasl_callback sasl_proto_cb; /* SASL callback we use */ @@ -134,10 +138,13 @@ netsec_init(void) nsc->ns_outbuffer = mh_xmalloc(nsc->ns_outbufsize); nsc->ns_outptr = nsc->ns_outbuffer; nsc->ns_outbuflen = 0; + nsc->sasl_mech = NULL; +#ifdef OAUTH_SUPPORT + nsc->oauth_service = NULL; +#endif /* OAUTH_SUPPORT */ #ifdef CYRUS_SASL nsc->sasl_conn = NULL; nsc->sasl_hostname = NULL; - nsc->sasl_mech = NULL; nsc->sasl_cbs = NULL; nsc->sasl_creds = NULL; nsc->sasl_secret = NULL; @@ -156,11 +163,11 @@ netsec_init(void) /* * Shutdown the connection completely and free all resources. - * The connection is not closed, however. + * The connection is only closed if the flag is given. */ void -netsec_shutdown(netsec_context *nsc) +netsec_shutdown(netsec_context *nsc, int closeflag) { if (nsc->ns_userid) free(nsc->ns_userid); @@ -168,13 +175,17 @@ netsec_shutdown(netsec_context *nsc) free(nsc->ns_inbuffer); if (nsc->ns_outbuffer) free(nsc->ns_outbuffer); + if (nsc->sasl_mech) + free(nsc->sasl_mech); +#ifdef OAUTH_SERVICE + if (nsc->oauth_service) + free(nsc->oauth_service); +#endif /* OAUTH_SERVICE */ #ifdef CYRUS_SASL if (nsc->sasl_conn) sasl_dispose(&nsc->sasl_conn); if (nsc->sasl_hostname) free(nsc->sasl_hostname); - if (nsc->sasl_mech) - free(nsc->sasl_mech); if (nsc->sasl_cbs) free(nsc->sasl_cbs); if (nsc->sasl_creds) { @@ -203,6 +214,9 @@ netsec_shutdown(netsec_context *nsc) BIO_free_all(nsc->ssl_io); #endif /* TLS_SUPPORT */ + if (closeflag && nsc->ns_fd != -1) + close(nsc->ns_fd); + free(nsc); } @@ -326,7 +340,7 @@ retry: while (count < nsc->ns_inbuflen) { count++; if (*ptr++ == '\n') { - char *sptr = nsc->ns_inptr; + char *sptr = (char *) nsc->ns_inptr; if (count > 1 && *(ptr - 2) == '\r') ptr--; *--ptr = '\0'; @@ -355,7 +369,7 @@ retry: offset = ptr - nsc->ns_inptr; if (netsec_fillread(nsc, errstr) != OK) - return NOTOK; + return NULL; ptr = nsc->ns_inptr + offset; @@ -608,6 +622,22 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size, return OK; } +/* + * Our network printf() routine, which really just calls netsec_vprintf(). + +int +netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) +{ + va_list ap; + int rc; + + va_start(format, ap); + rc = netsec_vprintf(nsc, errstr, format, ap); + va_end(ap); + + return rc; +} + /* * Write bytes to the network using printf()-style formatting. * @@ -616,9 +646,9 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size, */ int -netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) +netsec_vprintf(netsec_context *nsc, char **errstr, const char *format, + va_list ap) { - va_list ap; int rc; /* @@ -626,9 +656,7 @@ netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) */ #ifdef TLS_SUPPORT if (nsc->tls_active) { - va_start(ap, format); rc = BIO_vprintf(nsc->ssl_io, format, ap); - va_end(ap); if (rc <= 0) { netsec_err(errstr, "Error writing to TLS connection: %s", @@ -646,10 +674,8 @@ netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) */ retry: - va_start(ap, format); rc = vsnprintf((char *) nsc->ns_outptr, nsc->ns_outbufsize - nsc->ns_outbuflen, format, ap); - va_end(ap); if (rc >= (int) (nsc->ns_outbufsize - nsc->ns_outbuflen)) { /* @@ -936,8 +962,76 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) unsigned char *outbuf; unsigned int saslbuflen, outbuflen; sasl_ssf_t *ssf; - int rc, *outbufmax; + int *outbufmax; +#endif +#ifdef OAUTH_SUPPORT + const char *xoauth_client_res; +#endif /* OAUTH_SUPPORT */ + int rc; + /* + * If we've been passed a requested mechanism, check our mechanism + * list from the protocol. If it's not supported, return an error. + */ + + if (nsc->sasl_mech) { + char **str, *mlist = getcpy(mechlist); + int i; + + str = brkstring(mlist, " ", NULL); + + for (i = 0; str[i] != NULL; i++) { + if (strcasecmp(nsc->sasl_mech, str[i]) == 0) { + break; + } + } + + i = (str[i] == NULL); + + free(str); + free(mlist); + + if (i) { + netsec_err(errstr, "Chosen mechanism %s not supported by server", + nsc->sasl_mech); + return NOTOK; + } + } + +#ifdef OAUTH_SUPPORT + if (strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) { + /* + * 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 + * there is output data, it will be assumed that it is the JSON + * error message. + */ + + if (! nsc->oauth_service) { + netsec_err(errstr, "Internal error: OAuth2 service name not given"); + return NOTOK; + } + + 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"); + return NOTOK; + } + +#if 0 + rc = nsc->sasl_proto_cb(NETSEC_SASL_START, +#endif + } +#endif /* OAUTH_SUPPORT */ + +#ifdef CYRUS_SASL /* * In netsec_set_sasl_params, we've already done all of our setup with * sasl_client_init() and sasl_client_new(). So time to set security @@ -971,7 +1065,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) * sasl_client_step() loop (after sasl_client_start, of course). */ - rc = sasl_client_start(nsc->sasl_conn, mechlist, NULL, + rc = sasl_client_start(nsc->sasl_conn, + nsc->sasl_mech ? nsc->sasl_mech : mechlist, NULL, (const char **) &saslbuf, &saslbuflen, &chosen_mech); @@ -1164,6 +1259,21 @@ netsec_get_sasl_mechanism(netsec_context *nsc) #endif /* CYRUS_SASL */ } +/* + * Set an OAuth2 service name, if we support it. + */ + +int +netsec_set_oauth_service(netsec_context *nsc, const char *service) +{ +#ifdef OAUTH_SUPPORT + nsc->oauth_service = getcpy(service); + return OK; +#else /* OAUTH_SUPPORT */ + return NOTOK; +#endif /* OAUTH_SUPPORT */ +} + /* * Initialize (and enable) TLS for this connection */ diff --git a/uip/popsbr.c b/uip/popsbr.c index b8e5a448..0153dcb1 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -9,23 +9,7 @@ #include #include #include - -#ifdef CYRUS_SASL -# include -# include -# 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 */ - -#ifdef TLS_SUPPORT -#include -#include -#endif /* TLS_SUPPORT */ +#include #include #include @@ -37,34 +21,7 @@ static int poprint = 0; static int pophack = 0; char response[BUFSIZ]; - -static FILE *input; -static FILE *output; - -#ifdef CYRUS_SASL -static sasl_conn_t *conn; /* SASL connection state */ -static int sasl_complete = 0; /* Has sasl authentication succeeded? */ -static int maxoutbuf; /* Maximum output buffer size */ -static sasl_ssf_t sasl_ssf = 0; /* Security strength factor */ -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_callback_ft) sasl_get_user, NULL }, -#define POP_SASL_CB_N_USER 0 - { 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 }, - -#define SASL_BUFFER_SIZE 262144 -}; -#endif /* CYRUS_SASL */ +static netsec_context *nsc = NULL; /* * static prototypes @@ -73,25 +30,9 @@ static sasl_callback_t callbacks[] = { static int command(const char *, ...); static int multiline(void); -#ifdef CYRUS_SASL -static int pop_auth_sasl(char *, char *, char *); -#endif /* CYRUS_SASL */ - -#ifdef TLS_SUPPORT -static SSL_CTX *sslctx = NULL; -static SSL *ssl = NULL; -static BIO *sbior = NULL; -static BIO *sbiow = NULL; -static BIO *io = NULL; - -static int tls_negotiate(void); -#endif /* TLS_SUPPORT */ - -static int tls_active = 0; - static int traverse (int (*)(char *), const char *, ...); static int vcommand(const char *, va_list); -static int sasl_getline (char *, int, FILE *); +static int getline (char *, int, FILE *); static int sasl_fgetc(FILE *); static int putline (char *, FILE *); @@ -524,9 +465,9 @@ 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) { - int fd1, fd2; + int fd1; char buffer[BUFSIZ]; - const char *xoauth_client_res = NULL; + char *errstr; #ifndef CYRUS_SASL NMH_UNUSED (sasl); NMH_UNUSED (mech); @@ -542,6 +483,26 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, NMH_UNUSED (xoauth_client_res); #endif /* OAUTH_SUPPORT */ + nsc = netsec_init(); + + if (user) + netsec_set_userid(nsc, user); + + if (tls) { + if (netsec_set_tls(nsc, 1, &errstr) != OK) { + snprintf(response, sizeof(response), "%s", errstr); + free(errstr); + return NOTOK; + } + } + + if (oauth_svc != NULL) { + if (netsec_set_oauth_service(nsc, oauth_svc) != OK) { + snprintf(response, sizeof(response), "OAuth2 not supported"); + return NOTOK; + } + } + if (proxy && *proxy) { int pid; int inpipe[2]; /* for reading from the server */ @@ -590,34 +551,35 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, fd2=outpipe[1]; } else { - if ((fd1 = client (host, port ? port : "pop3", response, sizeof(response), snoop)) == NOTOK) { return NOTOK; } + } + + SIGNAL (SIGPIPE, SIG_IGN); - if ((fd2 = dup (fd1)) == NOTOK) { - char *s; + netsec_set_fd(nsc, fd1); + netsec_set_snoop(nsc, snoop); - if ((s = strerror(errno))) - snprintf (response, sizeof(response), - "unable to dup connection descriptor: %s", s); - else - snprintf (response, sizeof(response), - "unable to dup connection descriptor: unknown error"); - close (fd1); + if (tls) { + if (netsec_negotiate_tls(nsc, &errstr) != OK) { + snprintf(response, sizeof(response), "%s", errstr); + free(errstr); return NOTOK; } } - if (pop_set (fd1, fd2, snoop) == NOTOK) - return NOTOK; - - SIGNAL (SIGPIPE, SIG_IGN); - if (tls && tls_negotiate()) - return NOTOK; + if (sasl) { + if (netsec_set_sasl_params(nsc, host, "pop", mech, + pop_sasl_callback, &errstr) != OK) { + snprintf(response, sizeof(response), "%s", errstr); + free(errstr); + return NOTOK; + } + } - switch (sasl_getline (response, sizeof response, input)) { + switch (getline (response, sizeof response, input)) { case OK: if (poprint) fprintf (stderr, "<--- %s\n", response); @@ -656,35 +618,6 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, return NOTOK; /* NOTREACHED */ } -int -pop_set (int in, int out, int snoop) -{ - - if ((input = fdopen (in, "r")) == NULL - || (output = fdopen (out, "w")) == NULL) { - strncpy (response, "fdopen failed on connection descriptor", sizeof(response)); - if (input) - fclose (input); - else - close (in); - close (out); - return NOTOK; - } - - poprint = snoop; - - return OK; -} - - -int -pop_fd (char *in, int inlen, char *out, int outlen) -{ - snprintf (in, inlen, "%d", fileno (input)); - snprintf (out, outlen, "%d", fileno (output)); - return OK; -} - /* * Find out number of messages available @@ -838,12 +771,8 @@ pop_quit (void) int pop_done (void) { -#ifdef CYRUS_SASL - if (conn) - sasl_dispose(&conn); -#endif /* CYRUS_SASL */ - fclose (input); - fclose (output); + if (nsc) + netsec_shutdown(nsc, 1); return OK; } @@ -871,14 +800,6 @@ vcommand (const char *fmt, va_list ap) vsnprintf (buffer, sizeof(buffer), fmt, ap); if (poprint) { -#ifdef CYRUS_SASL - if (sasl_ssf) - fprintf(stderr, "(sasl-encrypted) "); -#endif /* CYRUS_SASL */ -#ifdef TLS_SUPPORT - if (tls_active) - fprintf(stderr, "(tls-encrypted) "); -#endif /* TLS_SUPPORT */ if (pophack) { if ((cp = strchr (buffer, ' '))) *cp = 0; @@ -1118,102 +1039,3 @@ sasl_fgetc(FILE *f) return (int) buffer[0]; } #endif /* CYRUS_SASL */ - -#ifdef TLS_SUPPORT -/* - * Negotiate TLS at this point. Will call pop_done() on failure. - */ - -static int -tls_negotiate(void) -{ - BIO *ssl_bio; - - if (! sslctx) { - SSL_library_init(); - SSL_load_error_strings(); - - sslctx = SSL_CTX_new(SSLv23_client_method()); - - if (! sslctx) { - pop_done(); - advise(NULL, "Unable to initialize OpenSSL context: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_TLSv1); - } - - ssl = SSL_new(sslctx); - - if (! ssl) { - pop_done(); - advise(NULL, "Unable to create SSL connection: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - sbior = BIO_new_socket(fileno(input), BIO_NOCLOSE); - sbiow = BIO_new_socket(fileno(output), BIO_NOCLOSE); - - if (sbior == NULL || sbiow == NULL) { - pop_done(); - advise(NULL, "Unable to create BIO endpoints: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - SSL_set_bio(ssl, sbior, sbiow); - SSL_set_connect_state(ssl); - - /* - * Set up a BIO to do buffering for us - */ - - io = BIO_new(BIO_f_buffer()); - - if (! io) { - pop_done(); - advise(NULL, "Unable to create a buffer BIO: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - ssl_bio = BIO_new(BIO_f_ssl()); - - if (! ssl_bio) { - pop_done(); - advise(NULL, "Unable to create a SSL BIO: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); - BIO_push(io, ssl_bio); - - /* - * Try doing the handshake now - */ - - if (BIO_do_handshake(io) < 1) { - pop_done(); - advise(NULL, "Unable to negotiate SSL connection: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NOTOK; - } - - if (poprint) { - const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); - printf("SSL negotiation successful: %s(%d) %s\n", - SSL_CIPHER_get_name(cipher), - SSL_CIPHER_get_bits(cipher, NULL), - SSL_CIPHER_get_version(cipher)); - } - - tls_active = 1; - - return OK; -} -#endif /* TLS_SUPPORT */