#include <h/mh.h>
#include <h/utils.h>
#include <h/netsec.h>
+#include <h/oauth.h>
#include <stdarg.h>
#include <sys/select.h>
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 */
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;
/*
* 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);
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) {
BIO_free_all(nsc->ssl_io);
#endif /* TLS_SUPPORT */
+ if (closeflag && nsc->ns_fd != -1)
+ close(nsc->ns_fd);
+
free(nsc);
}
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';
offset = ptr - nsc->ns_inptr;
if (netsec_fillread(nsc, errstr) != OK)
- return NOTOK;
+ return NULL;
ptr = nsc->ns_inptr + offset;
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.
*
*/
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;
/*
*/
#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",
*/
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)) {
/*
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
* 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);
#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
*/
#include <h/mh.h>
#include <h/utils.h>
#include <h/oauth.h>
-
-#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 */
-
-#ifdef TLS_SUPPORT
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#endif /* TLS_SUPPORT */
+#include <h/netsec.h>
#include <h/popsbr.h>
#include <h/signals.h>
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
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 *);
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);
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 */
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);
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
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;
}
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;
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 */