diff options
Diffstat (limited to 'imap/pop3login.c')
| -rw-r--r-- | imap/pop3login.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/imap/pop3login.c b/imap/pop3login.c new file mode 100644 index 0000000..2c6e599 --- /dev/null +++ b/imap/pop3login.c @@ -0,0 +1,529 @@ +/* +** Copyright 1998 - 2008 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#undef PACKAGE +#undef VERSION +#include "config.h" +#endif +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <ctype.h> +#include <fcntl.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netinet/in.h> +#include <netdb.h> +#include "waitlib/waitlib.h" +#include "proxy.h" + +#include <courierauth.h> +#include <courierauthdebug.h> +#include <courierauthsasl.h> +#include "tcpd/spipe.h" +#include "numlib/numlib.h" +#include "tcpd/tlsclient.h" + + +extern void pop3dcapa(); +extern int have_starttls(); +extern int tls_required(); +extern const char *pop3_externalauth(); + +static const char *pop3d; +static const char *defaultmaildir; + +static int starttls() +{ + char *argvec[4]; + + char localfd_buf[NUMBUFSIZE+40]; + char buf2[NUMBUFSIZE]; + struct couriertls_info cinfo; + int pipefd[2]; + + if (libmail_streampipe(pipefd)) + { + printf("-ERR libmail_streampipe() failed.\r\n"); + return (-1); + } + + couriertls_init(&cinfo); + fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); + + strcat(strcpy(localfd_buf, "-localfd="), + libmail_str_size_t(pipefd[1], buf2)); + + argvec[0]=localfd_buf; + argvec[1]="-tcpd"; + argvec[2]="-server"; + argvec[3]=NULL; + + printf("+OK Begin SSL/TLS negotiation now.\r\n"); + fflush(stdout); + + if (couriertls_start(argvec, &cinfo)) + { + close(pipefd[0]); + close(pipefd[1]); + printf("-ERR STARTTLS failed: %s\r\n", + cinfo.errmsg); + couriertls_destroy(&cinfo); + return (-1); + } + + couriertls_export_subject_environment(&cinfo); + couriertls_destroy(&cinfo); + + close(pipefd[1]); + close(0); + close(1); + if (dup(pipefd[0]) != 0 || dup(pipefd[0]) != 1) + { + perror("dup"); + exit(1); + } + close(pipefd[0]); + + putenv("POP3_STARTTLS=NO"); + putenv("POP3_TLS_REQUIRED=0"); + putenv("POP3_TLS=1"); + return (0); +} + +static char *authresp(const char *s, void *dummy) +{ +char *p; +char buf[BUFSIZ]; + + printf("+ %s\r\n", s); + fflush(stdout); + + if (fgets(buf, sizeof(buf), stdin) == 0) return (0); + if ((p=strchr(buf, '\n')) == 0) return (0); + if (p > buf && p[-1] == '\r') --p; + *p=0; + + p=strdup(buf); + if (!p) + { + perror("malloc"); + return (0); + } + return (p); +} + +struct pop3proxyinfo { + const char *uid; + const char *pwd; +}; + +static int login_pop3(int, const char *, void *); + +static int login_callback(struct authinfo *ainfo, void *dummy) +{ + int rc; + char *p; + + p=getenv("POP3_PROXY"); + + if (p && atoi(p)) + { + if (ainfo->options == NULL || + (p=auth_getoption(ainfo->options, + "mailhost")) == NULL) + { + fprintf(stderr,"WARN: proxy enabled, but no proxy" + " host defined for %s\n", + ainfo->address); + + /* Fallthru to account login */ + + } + else if (ainfo->clearpasswd == NULL) + { + free(p); + fprintf(stderr, "WARN: proxy enabled, but no password" + " for %s\n", ainfo->address); + return -1; + } + else + { + struct proxyinfo pi; + struct pop3proxyinfo ppi; + struct servent *se; + int fd; + + se=getservbyname("pop3", NULL); + + pi.host=p; + pi.port=se ? ntohs(se->s_port):110; + + ppi.uid=ainfo->address; + ppi.pwd=ainfo->clearpasswd; + + pi.connected_func=login_pop3; + pi.void_arg=&ppi; + + if ((fd=connect_proxy(&pi)) < 0) + { + free(p); + return -1; + } + free(p); + if (fd > 0) + { + alarm(0); + proxyloop(fd); + exit(0); + } + + /* FALLTHRU */ + } + } + + rc=auth_callback_default(ainfo); + + if (rc == 0) + { + char *p=malloc(sizeof("OPTIONS=") + strlen(ainfo->options ? + ainfo->options:"")); + + if (p) + { + strcat(strcpy(p, "OPTIONS="), + ainfo->options ? ainfo->options:""); + putenv(p); + + p=malloc(sizeof("AUTHENTICATED=")+ + strlen(ainfo->address)); + if (p) + { + strcat(strcpy(p, "AUTHENTICATED="), + ainfo->address); + putenv(p); + + alarm(0); + execl(pop3d, pop3d, + ainfo->maildir ? + ainfo->maildir:defaultmaildir, + NULL); + fprintf(stderr, "ERR: exec(%s) failed!!\n", + pop3d); + } + } + } + + return (rc); +} + +int main(int argc, char **argv) +{ +char *user=0; +char *p; +char buf[BUFSIZ]; +int c; +const char *ip=getenv("TCPREMOTEIP"); +char authservice[40]; +char *q ; + +#ifdef HAVE_SETVBUF_IOLBF + setvbuf(stderr, NULL, _IOLBF, BUFSIZ); +#endif + + if (!ip || !*ip) + { + ip="127.0.0.1"; + } + + if (argc != 3) + { + printf("-ERR pop3login requires exactly two arguments.\r\n"); + fflush(stdout); + exit(1); + } + + pop3d=argv[1]; + defaultmaildir=argv[2]; + + courier_authdebug_login_init(); + + fprintf(stderr, "DEBUG: Connection, ip=[%s]\n", ip); + printf("+OK Hello there.\r\n"); + + fflush(stdout); + fflush(stderr); + alarm(60); + while (fgets(buf, sizeof(buf), stdin)) + { + c=1; + for (p=buf; *p; p++) + { + if (*p == '\n') + break; + + if (*p == ' ' || *p == '\t') c=0; + if (c) + *p=toupper((int)(unsigned char)*p); + } + + if (*p) + *p=0; + else while ((c=getchar()) != EOF && c != '\n') + ; + p=strtok(buf, " \t\r"); + if (p) + { + courier_authdebug_login( 1, "command=%s", p ); + + if ( strcmp(p, "QUIT") == 0) + { + fprintf(stderr, "INFO: LOGOUT, ip=[%s]\n", + ip); + fflush(stderr); + printf("+OK Better luck next time.\r\n"); + fflush(stdout); + break; + } + + if ( strcmp(p, "USER") == 0) + { + if (tls_required()) + { + printf("-ERR TLS required to log in.\r\n"); + fflush(stdout); + continue; + } + + p=strtok(0, "\r\n"); + if (p) + { + if (user) free(user); + if ((user=malloc(strlen(p)+1)) == 0) + { + printf("-ERR Server out of memory, aborting connection.\r\n"); + fflush(stdout); + perror("malloc"); + exit(1); + } + strcpy(user, p); + printf("+OK Password required.\r\n"); + fflush(stdout); + continue; + } + } else if (strcmp(p, "CAPA") == 0) + { + pop3dcapa(); + continue; + } else if (strcmp(p, "STLS") == 0) + { + if (!have_starttls()) + { + printf("-ERR TLS support not available.\r\n"); + fflush(stdout); + continue; + } + starttls(); + fflush(stdout); + continue; + } else if (strcmp(p, "AUTH") == 0) + { + char *authtype, *authdata; + char *method=strtok(0, " \t\r"); + + if (tls_required()) + { + printf("-ERR TLS required to log in.\r\n"); + fflush(stdout); + continue; + } + + if (method) + { + char *initreply=strtok(0, " \t\r"); + int rc; + char *p; + + for (p=method; *p; p++) + *p=toupper(*p); + + if (initreply && + strcmp(initreply, "=") == 0) + initreply=""; + + rc=auth_sasl_ex(method, initreply, + pop3_externalauth(), + authresp, + NULL, + &authtype, + &authdata); + + if (rc == 0) + { + strcat(strcpy(authservice, "AUTHSERVICE"),getenv("TCPLOCALPORT")); + q=getenv(authservice); + if (!q || !*q) + q="pop3"; + + rc=auth_generic(q, + authtype, + authdata, + login_callback, + NULL); + free(authtype); + free(authdata); + } + + courier_safe_printf("INFO: LOGIN " + "FAILED, method=%s, ip=[%s]", + method, ip); + if (rc == AUTHSASL_ABORTED) + printf("-ERR Authentication aborted.\r\n"); + else if (rc > 0) + { + perror("ERR: authentication error"); + printf("-ERR Temporary problem, please try again later\r\n"); + fflush(stdout); + exit(1); + } + else + { + sleep(5); + printf("-ERR Authentication failed.\r\n"); + } + + fflush(stdout); + continue; + } + } else if (strcmp(p, "PASS") == 0) + { + int rc; + + p=strtok(0, "\r\n"); + + if (!user || p == 0) + { + printf("-ERR USER/PASS required.\r\n"); + fflush(stdout); + continue; + } + + strcat(strcpy(authservice, "AUTHSERVICE"),getenv("TCPLOCALPORT")); + q=getenv(authservice); + if (!q || !*q) + q="pop3"; + + rc=auth_login(q, user, p, login_callback, NULL); + courier_safe_printf("INFO: LOGIN " + "FAILED, user=%s, ip=[%s]", + user, ip); + if (rc > 0) + { + perror("ERR: authentication error"); + printf("-ERR Temporary problem, please try again later\r\n"); + fflush(stdout); + exit(1); + } + sleep(5); + printf("-ERR Login failed.\r\n"); + fflush(stdout); + continue; + } + } + printf("-ERR Invalid command.\r\n"); + fflush(stdout); + } + fprintf(stderr, "DEBUG: Disconnected, ip=[%s]\n", ip); + exit(0); + return (0); +} + +static int login_pop3(int fd, const char *hostname, void *void_arg) +{ + struct pop3proxyinfo *ppi=(struct pop3proxyinfo *)void_arg; + struct proxybuf pb; + char linebuf[256]; + char *cmd; + + DPRINTF("Proxy connected to %s", hostname); + + memset(&pb, 0, sizeof(pb)); + + if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0) + return -1; + + DPRINTF("%s: %s", hostname, linebuf); + + if (linebuf[0] != '+') + { + fprintf(stderr, "WARN: Did not receive greeting from %s\n", + hostname); + return -1; + } + + cmd=malloc(strlen(ppi->uid) + strlen(ppi->pwd)+100); + /* Should be enough */ + + if (!cmd) + return -1; + + sprintf(cmd, "USER %s\r\n", ppi->uid); + + if (proxy_write(fd, hostname, cmd, strlen(cmd))) + { + free(cmd); + return -1; + } + + if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0) + { + free(cmd); + return -1; + } + + DPRINTF("%s: %s", hostname, linebuf); + + if (linebuf[0] != '+') + { + free(cmd); + fprintf(stderr, "WARN: Login userid rejected by %s\n", + hostname); + return -1; + } + + sprintf(cmd, "PASS %s\r\n", ppi->pwd); + + if (proxy_write(fd, hostname, cmd, strlen(cmd))) + { + free(cmd); + return -1; + } + + if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0) + { + free(cmd); + return -1; + } + + DPRINTF("%s: %s", hostname, linebuf); + + if (linebuf[0] != '+') + { + free(cmd); + fprintf(stderr, "WARN: Login password rejected by %s\n", + hostname); + return -1; + } + + free(cmd); + if (fcntl(1, F_SETFL, 0) < 0 || + (printf("+OK Connected to proxy server.\r\n"), fflush(stdout)) < 0) + return -1; + + return 0; +} |
