diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 | 
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/imaplogin.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'imap/imaplogin.c')
| -rw-r--r-- | imap/imaplogin.c | 694 | 
1 files changed, 694 insertions, 0 deletions
| diff --git a/imap/imaplogin.c b/imap/imaplogin.c new file mode 100644 index 0000000..c3dfa7f --- /dev/null +++ b/imap/imaplogin.c @@ -0,0 +1,694 @@ +/* +** Copyright 1998 - 2011 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<ctype.h> +#include	<fcntl.h> +#include	<time.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if	HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#include	<sys/socket.h> +#include	<netinet/in.h> +#include	<arpa/inet.h> +#include	<netdb.h> +#if HAVE_SYS_SELECT_H +#include	<sys/select.h> +#endif +#include	<sys/time.h> + +#include	"imaptoken.h" +#include	"imapwrite.h" +#include	"proxy.h" + +#include	<courierauth.h> +#include	<courierauthdebug.h> +#include	"tcpd/spipe.h" +#include	"numlib/numlib.h" +#include	"tcpd/tlsclient.h" + + +FILE *debugfile=0; +extern void initcapability(); +extern void mainloop(); +extern void imapcapability(); +extern int have_starttls(); +extern int tlsrequired(); +extern int authenticate(const char *, char *, int); +unsigned long header_count=0, body_count=0;	/* Dummies */ + +extern unsigned long bytes_received_count; /* counter for received bytes (imaptoken.c) */ +extern unsigned long bytes_sent_count; /* counter for sent bytes (imapwrite.c) */ + +int main_argc; +char **main_argv; +extern time_t start_time; + +static const char *imapd; +static const char *defaultmaildir; + +void rfc2045_error(const char *p) +{ +	if (write(2, p, strlen(p)) < 0) +		_exit(1); +	_exit(0); +} + +extern void cmdfail(const char *, const char *); +extern void cmdsuccess(const char *, const char *); + +static int	starttls(const char *tag) +{ +	char *argvec[4]; + +	char localfd_buf[NUMBUFSIZE+40]; +	char buf2[NUMBUFSIZE]; +	struct couriertls_info cinfo; +	int pipefd[2]; + +	if (libmail_streampipe(pipefd)) +	{ +		cmdfail(tag, "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; + +	cmdsuccess(tag, "Begin SSL/TLS negotiation now.\r\n"); +	writeflush(); + +	if (couriertls_start(argvec, &cinfo)) +	{ +		close(pipefd[0]); +		close(pipefd[1]); +		cmdfail(tag, "STARTTLS failed: "); +		writes(cinfo.errmsg); +		writes("\r\n"); +		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]); + +	/* We use select() with a timeout, so use non-blocking filedescs */ + +	if (fcntl(0, F_SETFL, O_NONBLOCK) || +	    fcntl(1, F_SETFL, O_NONBLOCK)) +	{ +		perror("fcntl"); +		exit(1); +	} +	return (0); +} + +struct imapproxyinfo { +	const char *tag; +	const char *uid; +	const char *pwd; +}; + +static int login_imap(int, const char *, void *); + +int login_callback(struct authinfo *ainfo, void *dummy) +{ +	int rc; +	const char *tag=(const char *)dummy; +	char *p; + +	p=getenv("IMAP_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 imapproxyinfo ipi; +			struct servent *se; +			int fd; + +			se=getservbyname("imap", NULL); + +			pi.host=p; +			pi.port=se ? ntohs(se->s_port):143; + +			ipi.uid=ainfo->address; +			ipi.pwd=ainfo->clearpasswd; +			ipi.tag=tag; + +			pi.connected_func=login_imap; +			pi.void_arg=&ipi; + +			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) +	{ +		p=malloc(sizeof("OPTIONS=") + strlen(ainfo->options ? +						     ainfo->options:"")); + +		if (p) +		{ +			strcat(strcpy(p, "OPTIONS="), +			       ainfo->options ? ainfo->options:""); +			putenv(p); + +			p=malloc(sizeof("IMAPLOGINTAG=")+strlen(tag)); +			if (p) +			{ +				strcat(strcpy(p, "IMAPLOGINTAG="), tag); +				putenv(p); + +				p=malloc(sizeof("AUTHENTICATED=")+ +					 strlen(ainfo->address)); +				if (p) +				{ +					strcat(strcpy(p, "AUTHENTICATED="), +					       ainfo->address); +					putenv(p); +					alarm(0); +					execl(imapd, imapd, +					      ainfo->maildir ? +					      ainfo->maildir:defaultmaildir, +					      NULL); +					fprintf(stderr, "ERR: exec(%s) failed!!\n", +								 imapd); +				} +			} +		} +	} + +	return(rc); +} + +int do_imap_command(const char *tag) +{ +	struct	imaptoken *curtoken=nexttoken(); +	char authservice[40]; + +#if SMAP +	if (strcmp(tag, "\\SMAP1") == 0) +	{ +		const char *p=getenv("SMAP_CAPABILITY"); + +		if (p && *p) +			putenv("PROTOCOL=SMAP1"); +		else +			return -1; +	} +#endif + +	courier_authdebug_login( 1, "command=%s", curtoken->tokenbuf ); + +	if (strcmp(curtoken->tokenbuf, "LOGOUT") == 0) +	{ +		if (nexttoken()->tokentype != IT_EOL)   return (-1); +		writes("* BYE Courier-IMAP server shutting down\r\n"); +		cmdsuccess(tag, "LOGOUT completed\r\n"); +		writeflush(); +		fprintf(stderr, "INFO: LOGOUT, ip=[%s], rcvd=%lu, sent=%lu\n", +			getenv("TCPREMOTEIP"), bytes_received_count, bytes_sent_count); +		exit(0); +	} +	if (strcmp(curtoken->tokenbuf, "NOOP") == 0) +	{ +		if (nexttoken()->tokentype != IT_EOL)	return (-1); +		cmdsuccess(tag, "NOOP completed\r\n"); +		return (0); +	} +	if (strcmp(curtoken->tokenbuf, "CAPABILITY") == 0) +	{ +		if (nexttoken()->tokentype != IT_EOL)	return (-1); + +		writes("* CAPABILITY "); +		imapcapability(); +		writes("\r\n"); +		cmdsuccess(tag, "CAPABILITY completed\r\n"); +		return (0); +	} + +	if (strcmp(curtoken->tokenbuf, "STARTTLS") == 0) +	{ +		if (!have_starttls())	return (-1); +		if (starttls(tag))		return (-2); +		putenv("IMAP_STARTTLS=NO"); +		putenv("IMAP_TLS_REQUIRED=0"); +		putenv("IMAP_TLS=1"); + +		return (0); +	} + +	if (strcmp(curtoken->tokenbuf, "LOGIN") == 0) +	{ +	struct imaptoken *tok=nexttoken_nouc(); +	char	*userid; +	char	*passwd; +	const char *p; +	int	rc; + +		if (have_starttls() && tlsrequired())	/* Not yet */ +		{ +			cmdfail(tag, "STARTTLS required\r\n"); +			return (0); +		} + +		switch (tok->tokentype)	{ +		case IT_ATOM: +		case IT_NUMBER: +		case IT_QUOTED_STRING: +			break; +		default: +			return (-1); +		} + +		userid=strdup(tok->tokenbuf); +		if (!userid) +			write_error_exit(0); +		tok=nexttoken_nouc_okbracket(); +		switch (tok->tokentype)	{ +		case IT_ATOM: +		case IT_NUMBER: +		case IT_QUOTED_STRING: +			break; +		default: +			free(userid); +			return (-1); +		} + +		passwd=my_strdup(tok->tokenbuf); + +		if (nexttoken()->tokentype != IT_EOL) +		{ +			free(userid); +			free(passwd); +			return (-1); +		} + +		strcat(strcpy(authservice, "AUTHSERVICE"), +		       getenv("TCPLOCALPORT")); + +		p=getenv(authservice); + +		if (!p || !*p) +			p="imap"; + +		rc=auth_login(p, userid, passwd, login_callback, (void *)tag); +		courier_safe_printf("INFO: LOGIN FAILED, user=%s, ip=[%s]", +				  userid, getenv("TCPREMOTEIP")); +		free(userid); +		free(passwd); +		if (rc > 0) +		{ +			perror("ERR: authentication error"); +			writes("* BYE Temporary problem, please try again later\r\n"); +			writeflush(); +			exit(1); +		} +		sleep(5); +		cmdfail(tag, "Login failed.\r\n"); +		return (0); +	} + +	if (strcmp(curtoken->tokenbuf, "AUTHENTICATE") == 0) +	{ +	char	method[32]; +	int	rc; + +		if (have_starttls() && tlsrequired())	/* Not yet */ +		{ +			cmdfail(tag, "STARTTLS required\r\n"); +			return (0); +		} +		rc=authenticate(tag, method, sizeof(method)); +		courier_safe_printf("INFO: LOGIN FAILED, method=%s, ip=[%s]", +				  method, getenv("TCPREMOTEIP")); +		if (rc > 0) +		{ +			perror("ERR: authentication error"); +			writes("* BYE Temporary problem, please try again later\r\n"); +			writeflush(); +			exit(1); +		} +		sleep(5); +		cmdfail(tag, "Login failed.\r\n"); +		writeflush(); +		return (-2); +	} + +	return (-1); +} + +extern void ignorepunct(); + +int main(int argc, char **argv) +{ +	const char	*ip; + +#ifdef HAVE_SETVBUF_IOLBF +	setvbuf(stderr, NULL, _IOLBF, BUFSIZ); +#endif + +	if (argc != 3) +	{ +		printf("* BYE imaplogin expected exactly two arguments.\r\n"); +		fflush(stdout); +		exit(1); +	} + +	alarm(60); +	imapd=argv[1]; +	defaultmaildir=argv[2]; +	initcapability(); + +	ip=getenv("TCPREMOTEIP"); +	if (!ip) +		putenv("TCPREMOTEIP=127.0.0.1"); +	ip=getenv("TCPREMOTEIP"); + +	if (!getenv("TCPLOCALPORT")) +		putenv("TCPLOCALPORT=143"); + +	time(&start_time); + +#if	IMAP_CLIENT_BUGS + +	ignorepunct(); + +#endif + +	courier_authdebug_login_init(); + +	/* We use select() with a timeout, so use non-blocking filedescs */ + +	if (fcntl(0, F_SETFL, O_NONBLOCK) || +	    fcntl(1, F_SETFL, O_NONBLOCK)) +	{ +		perror("fcntl"); +		exit(1); +	} + +	writes("* OK [CAPABILITY "); +	imapcapability(); +	writes("] Courier-IMAP ready. " +	       "Copyright 1998-2011 Double Precision, Inc.  " +	       "See COPYING for distribution information.\r\n"); +	fprintf(stderr, "DEBUG: Connection, ip=[%s]\n", ip); +	writeflush(); +	main_argc=argc; +	main_argv=argv; + +	putenv("PROTOCOL=IMAP"); + +	mainloop(); +	return (0); +} + +void bye() +{ +	exit(0); +} + +static void imap_write_str(const char *c, +			   void (*cb_func)(const char *, +					   size_t, +					   void *), +			   void *cb_arg) +{ +	if (c == NULL) +	{ +		(*cb_func)("NIL", 3, cb_arg); +	} + +	(*cb_func)("\"", 1, cb_arg); + +	while (*c) +	{ +		size_t n; + +		for (n=0; c[n]; n++) +			if (c[n] == '"' || c[n] == '\\') +				break; + +		if (n) +		{ +			(*cb_func)(c, n, cb_arg); + +			c += n; +		} + +		if (*c) +		{ +			(*cb_func)("\\", 1, cb_arg); +			(*cb_func)(c, 1, cb_arg); +			++c; +		} +	} +	(*cb_func)("\"", 1, cb_arg); +} + +static void imap_login_cmd(struct imapproxyinfo *ipi, +			   void (*cb_func)(const char *, +					   size_t, +					   void *), +			   void *cb_arg) +{ +	(*cb_func)(ipi->tag, strlen(ipi->tag), cb_arg); +	(*cb_func)(" LOGIN ", 7, cb_arg); +	imap_write_str(ipi->uid, cb_func, cb_arg); +	(*cb_func)(" ", 1, cb_arg); +	imap_write_str(ipi->pwd, cb_func, cb_arg); +	(*cb_func)("\r\n", 2, cb_arg); +} + +static void imap_capability_cmd(struct imapproxyinfo *ipi, +			       void (*cb_func)(const char *, +					       size_t, +					       void *), +			       void *cb_arg) +{ +	(*cb_func)(ipi->tag, strlen(ipi->tag), cb_arg); +	(*cb_func)(" CAPABILITY\r\n", 13, cb_arg); +} + +static void cb_cnt(const char *c, size_t l, +		   void *arg) +{ +	*(size_t *)arg += l; +} + +static void cb_cpy(const char *c, size_t l, +		   void *arg) +{ +	char **p=(char **)arg; + +	memcpy(*p, c, l); +	*p += l; +} + +static char *get_imap_cmd(struct imapproxyinfo *ipi, +			  void (*cmd)(struct imapproxyinfo *ipi, +				      void (*cb_func)(const char *, +						      size_t, +						      void *), +				      void *cb_arg)) + +{ +	size_t cnt=1; +	char *buf; +	char *p; + +	(*cmd)(ipi, &cb_cnt, &cnt); + +	buf=malloc(cnt); + +	if (!buf) +	{ +		fprintf(stderr, "CRIT: Out of memory!\n"); +		return NULL; +	} + +	p=buf; +	(*cmd)(ipi, &cb_cpy, &p); +	*p=0; +	return buf; +} + +static int login_imap(int fd, const char *hostname, void *void_arg) +{ +	struct imapproxyinfo *ipi=(struct imapproxyinfo *)void_arg; +	struct proxybuf pb; +	char linebuf[256]; +	const char *p; +	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 ((p=strtok(linebuf, " \t")) == NULL || +	    strcmp(p, "*") || +	    (p=strtok(NULL, " \t")) == NULL || +	    strcasecmp(p, "OK")) +	{ +		fprintf(stderr, "WARN: Did not receive greeting from %s\n", +			hostname); +		return -1; +	} + +	cmd=get_imap_cmd(ipi, imap_login_cmd); + +	if (!cmd) +		return -1; + +	if (proxy_write(fd, hostname, cmd, strlen(cmd))) +	{ +		free(cmd); +		return -1; +	} +	free(cmd); + +#if SMAP +	if (strcmp(ipi->tag, "\\SMAP1") == 0) +	{ +		do +		{ +			if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), +					   0) < 0) +				return -1; + +			DPRINTF("%s: %s", hostname, linebuf); + +		} while (linebuf[0] != '+' && linebuf[0] != '-'); + + +		if (linebuf[0] != '+') +		{ +			fprintf(stderr, "WARN: Login to %s failed\n", hostname); +			return -1; +		} + +		if (fcntl(1, F_SETFL, 0) < 0 || +		    (printf("+OK connected to proxy server.\r\n"), +		     fflush(stdout)) < 0) +			return -1; +		return (0); +	} +#endif + + +	for (;;) +	{ +		if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0) +			return -1; + +		DPRINTF("%s: %s", hostname, linebuf); + +		if ((p=strtok(linebuf, " \t")) == NULL || +		    strcmp(p, ipi->tag) || +		    (p=strtok(NULL, " \t")) == NULL) +			continue; + +		if (strcasecmp(p, "OK")) +		{ +			fprintf(stderr, "WARN: Login to %s failed\n", hostname); +			return -1; +		} +		break; +	} + +	p=getenv("IMAP_PROXY_FOREIGN"); + +	if (p && atoi(p)) +	{ +		cmd=get_imap_cmd(ipi, imap_capability_cmd); + +		if (!cmd) +			return -1; + +		if (proxy_write(fd, hostname, cmd, strlen(cmd))) +		{ +			free(cmd); +			return -1; +		} +		free(cmd); +	} +	else +	{ +		if (fcntl(1, F_SETFL, 0) < 0 || +		    (printf("%s OK connected to proxy server.\r\n", +			    ipi->tag), fflush(stdout)) < 0) +			return -1; +	} + +	return 0; +} + | 
