Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117754334
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
82 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/doc/specs.html b/doc/specs.html
index 8915bf4c9..770efaa8e 100644
--- a/doc/specs.html
+++ b/doc/specs.html
@@ -1,128 +1,130 @@
-<!-- $Id: specs.html,v 1.7 2001/08/31 18:42:47 ken3 Exp $ -->
+<!-- $Id: specs.html,v 1.8 2001/12/01 04:03:16 ken3 Exp $ -->
<HTML>
<HEAD>
<TITLE>Cyrus IMAP Server Protocol Specifications</TITLE>
</HEAD>
<BODY>
<h1>Cyrus IMAP Server Protocol Specifications</h1>
<TABLE BORDER="1" CELLSPACING="1" CELLPADDING="2">
<TR><TD COLSPAN=2><br><h2>IMAP</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2060">RFC 2060</A></TD>
<TD>Internet Message Access Protocol - version 4rev1
<BR><I>being updated by</I> <A HREF="http://www.imc.org/draft-crispin-imapv">
draft-crispin-imapv</A></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1730">RFC 1730</A></TD>
<TD>Internet Message Access Protocol - version 4</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1731">RFC 1731</A></TD>
<TD>IMAP4 Authentication Mechanisms</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2086">RFC 2086</A></TD>
<TD>IMAP4 ACL extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2087">RFC 2087</A></TD>
<TD>IMAP4 QUOTA extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2088">RFC 2088</A></TD>
<TD>IMAP4 non-synchronizing literals</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2177">RFC 2177</A></TD>
<TD>IMAP4 IDLE command</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2342">RFC 2342</A></TD>
<TD>IMAP4 Namespace</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2359">RFC 2359</A></TD>
<TD>IMAP4 UIDPLUS extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2971">RFC 2971</A></TD>
<TD>IMAP4 ID extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-crispin-imap-multiappend">
draft-crispin-imap-multiappend</A></TD>
<TD>IMAP MULTIAPPEND extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-ietf-imapext-sort">
draft-ietf-imapext-sort</A></TD>
<TD>IMAP SORT extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-ietf-imapext-thread">
draft-ietf-imapext-thread</A></TD>
<TD>IMAP THREAD extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-gahrns-imap-child-mailbox">
draft-gahrns-imap-child-mailbox</A></TD>
<TD>IMAP4 Child Mailbox Extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-ietf-imapext-list-extensions">
draft-ietf-imapext-list-extensions</A></TD>
<TD>IMAP4 LIST Command Extensions</TD></TR>
<TR><TD COLSPAN=2><br><h2>POP</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1939">RFC 1939</A></TD>
<TD>Post Office Protocol - Version 3 (POP3)</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1734">RFC 1734</A></TD>
<TD>POP3 AUTHentication command</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2449">RFC 2449</A></TD>
<TD>POP3 Extension Mechanism</TD></TR>
+<TR><TD><A HREF="http://www.imc.org/draft-gellens-pop-err">
+draft-gellens-pop-err</A></TD><TD>SYS and AUTH POP Response Codes</TD></TR>
<TR><TD COLSPAN=2><br><h2>SASL</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2222">RFC 2222</A></TD>
<TD>Simple Authentication and Security Layer (SASL)
<BR><I>being revised by</I> <A HREF="http://www.imc.org/draft-myers-saslrev">
draft-myers-saslrev</A></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2195">RFC 2195</A></TD>
<TD>IMAP/POP AUTHorize Extension for Simple Challenge/Response</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2245">RFC 2245</A></TD>
<TD>Anonymous SASL Mechanism</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2831">RFC 2831</A></TD>
<TD>Using Digest Authentication as a SASL Mechanism</TD></TR>
<TR><TD COLSPAN=2><br><h2>TLS/SSL</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2595">RFC 2595</A></TD>
<TD>Using TLS with IMAP, POP3 and ACAP</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2246">RFC 2246</A></TD>
<TD>TLS Protocol</TD></TR>
<TR><TD><A HREF="http://www.netscape.com/eng/ssl3/draft302.txt">
draft-freier-ssl-version3</A></TD>
<TD>The SSL Protocol Version 3.0</TD></TR>
<TR><TD COLSPAN=2><br><h2>LMTP</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2033">RFC 2033</A></TD>
<TD>Local Mail Transfer Protocol </TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2821">RFC 2821</A></TD>
<TD>Simple Mail Transfer Protocol (SMTP)</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1869">RFC 1869</A></TD>
<TD>SMTP Service Extensions</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1652">RFC 1652</A></TD>
<TD>SMTP Service Extension for 8bit-MIMEtransport</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1870">RFC 1870</A></TD>
<TD>SMTP Service Extension for Message Size Declaration</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2034">RFC 2034</A></TD>
<TD>SMTP Service Extension for Returning Enhanced Error Codes</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc1893">RFC 1893</A></TD>
<TD>Enhanced Mail System Status Codes
<BR><I>being updated by</I> <A HREF="http://www.imc.org/draft-vaudreuil-1893bis">
draft-vaudreuil-1893bis</A></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2487">RFC 2487</A></TD>
<TD>SMTP Service Extension for Secure SMTP over TLS
<BR><I>being updated by</I> <a href="http://www.imc.org/draft-hoffman-rfc2487bis">
draft-hoffman-rfc2487bis</a></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2554">RFC 2554</A></TD>
<TD>SMTP Service Extension for Authentication</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2920">RFC 2920</A></TD>
<TD>SMTP Service Extension for Command Pipelining</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-murchison-lmtp-ignorequota">
draft-murchison-lmtp-ignorequota</A></TD>
<TD>LMTP Service Extension for Ignoring Recipient Quotas</TD></TR>
<TR><TD COLSPAN=2><br><h2>Sieve</h2></TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc3028">RFC 3028</A></TD>
<TD>Sieve: A Mail Filtering Language</TD></TR>
<TR><TD><A HREF="http://www.imc.org/rfc2298">RFC 2298</A></TD>
<TD>Extensible Message Format for Message Disposition Notifications (MDNs)</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-showalter-sieve-vacation">
draft-showalter-sieve-vacation</A></TD>
<TD>Sieve -- Vacation Extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-murchison-sieve-regex">
draft-murchison-sieve-regex</A></TD>
<TD>Sieve -- Regular Expression Extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-murchison-sieve-subaddress">
draft-murchison-sieve-subaddress</A></TD>
<TD>Sieve -- Subaddress Extension</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-martin-sieve-notify">
draft-martin-sieve-notify</A></TD>
<TD>Sieve -- An extension for providing instant notifications</TD></TR>
<TR><TD><A HREF="http://www.imc.org/draft-martin-managesieve">
draft-martin-managesieve</A></TD>
<TD>Protocol for Remotely Managing Sieve Scripts</TD></TR>
</TABLE>
diff --git a/imap/pop3d.c b/imap/pop3d.c
index 3791d8c4a..a7be86780 100644
--- a/imap/pop3d.c
+++ b/imap/pop3d.c
@@ -1,1439 +1,1440 @@
/* pop3d.c -- POP3 server protocol parsing
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
- * $Id: pop3d.c,v 1.111 2001/11/27 02:24:59 ken3 Exp $
+ * $Id: pop3d.c,v 1.112 2001/12/01 04:03:16 ken3 Exp $
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/param.h>
#include <syslog.h>
#include <com_err.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "prot.h"
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#include "acl.h"
#include "util.h"
#include "auth.h"
#include "iptostring.h"
#include "imapconf.h"
#include "tls.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "version.h"
#include "xmalloc.h"
#include "mboxlist.h"
#include "idle.h"
#include "telemetry.h"
#ifdef HAVE_KRB
/* kerberos des is purported to conflict with OpenSSL DES */
#define DES_DEFS
#include <krb.h>
/* MIT's kpop authentication kludge */
char klrealm[REALM_SZ];
AUTH_DAT kdata;
#endif /* HAVE_KRB */
static int kflag = 0;
extern int optind;
extern char *optarg;
extern int opterr;
extern int errno;
#ifdef HAVE_SSL
static SSL *tls_conn;
#endif /* HAVE_SSL */
sasl_conn_t *popd_saslconn; /* the sasl connection context */
char *popd_userid = 0;
struct mailbox *popd_mailbox = 0;
struct sockaddr_in popd_localaddr, popd_remoteaddr;
int popd_haveaddr = 0;
char popd_clienthost[250] = "[local]";
struct protstream *popd_out, *popd_in;
unsigned popd_exists = 0;
unsigned popd_highest;
unsigned popd_login_time;
struct msg {
unsigned uid;
unsigned size;
int deleted;
} *popd_msg;
static int pop3s = 0;
int popd_starttls_done = 0;
static struct mailbox mboxstruct;
static mailbox_decideproc_t expungedeleted;
/* current namespace */
static struct namespace popd_namespace;
static void cmd_apop(char *response);
static int apop_enabled(void);
static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* <rand.time@hostname> */
static void cmd_auth();
static void cmd_capa();
static void cmd_pass();
static void cmd_user();
static void cmd_starttls(int pop3s);
static void blat(int msg,int lines);
int openinbox(void);
static void cmdloop(void);
void kpop(void);
static int parsenum(char **ptr);
void usage(void);
void shut_down(int code) __attribute__ ((noreturn));
extern void setproctitle_init(int argc, char **argv, char **envp);
extern int proc_register(const char *progname, const char *clienthost,
const char *userid, const char *mailbox);
extern void proc_cleanup(void);
/* Enable the resetting of a sasl_conn_t */
static int reset_saslconn(sasl_conn_t **conn);
static struct
{
char *ipremoteport;
char *iplocalport;
sasl_ssf_t ssf;
char *authid;
} saslprops = {NULL,NULL,0,NULL};
static struct sasl_callback mysasl_cb[] = {
{ SASL_CB_GETOPT, &mysasl_config, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
static void popd_reset(void)
{
proc_cleanup();
if (popd_mailbox) {
mailbox_close(popd_mailbox);
popd_mailbox = 0;
}
prot_flush(popd_out);
if (popd_in) prot_free(popd_in);
if (popd_out) prot_free(popd_out);
close(0);
close(1);
close(2);
strcpy(popd_clienthost, "[local]");
if (popd_userid != NULL) {
free(popd_userid);
popd_userid = NULL;
}
if (popd_saslconn) {
sasl_dispose(&popd_saslconn);
popd_saslconn = NULL;
}
popd_starttls_done = 0;
if(saslprops.iplocalport) {
free(saslprops.iplocalport);
saslprops.iplocalport = NULL;
}
if(saslprops.ipremoteport) {
free(saslprops.ipremoteport);
saslprops.ipremoteport = NULL;
}
if(saslprops.authid) {
free(saslprops.authid);
saslprops.authid = NULL;
}
saslprops.ssf = 0;
#ifdef HAVE_SSL
if (tls_conn) {
tls_reset_servertls(&tls_conn);
tls_conn = NULL;
}
#endif
popd_exists = 0;
}
/*
* run once when process is forked;
* MUST NOT exit directly; must return with non-zero error code
*/
int service_init(int argc, char **argv, char **envp)
{
int r;
int opt;
config_changeident("pop3d");
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
setproctitle_init(argc, argv, envp);
/* set signal handlers */
signals_set_shutdown(&shut_down);
signals_add_handlers();
signal(SIGPIPE, SIG_IGN);
/* set the SASL allocation functions */
sasl_set_alloc((sasl_malloc_t *) &xmalloc,
(sasl_calloc_t *) &calloc,
(sasl_realloc_t *) &xrealloc,
(sasl_free_t *) &free);
/* load the SASL plugins */
if ((r = sasl_server_init(mysasl_cb, "Cyrus")) != SASL_OK) {
syslog(LOG_ERR, "SASL failed initializing: sasl_server_init(): %s",
sasl_errstring(r, NULL, NULL));
return 2;
}
/* open the mboxlist, we'll need it for real work */
mboxlist_init(0);
mboxlist_open(NULL);
/* setup for sending IMAP IDLE notifications */
idle_enabled();
/* Set namespace */
if ((r = mboxname_init_namespace(&popd_namespace, 0)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
while ((opt = getopt(argc, argv, "C:sk")) != EOF) {
switch(opt) {
case 'C': /* alt config file - handled by service::main() */
break;
case 's': /* pop3s (do starttls right away) */
pop3s = 1;
if (!tls_enabled("pop3")) {
syslog(LOG_ERR, "pop3s: required OpenSSL options not present");
fatal("pop3s: required OpenSSL options not present",
EC_CONFIG);
}
break;
case 'k':
kflag++;
break;
default:
usage();
}
}
return 0;
}
/*
* run for each accepted connection
*/
int service_main(int argc, char **argv, char **envp)
{
socklen_t salen;
struct hostent *hp;
char localip[60], remoteip[60];
int timeout;
sasl_security_properties_t *secprops=NULL;
signals_poll();
popd_in = prot_new(0, 0);
popd_out = prot_new(1, 1);
/* Find out name of client host */
salen = sizeof(popd_remoteaddr);
if (getpeername(0, (struct sockaddr *)&popd_remoteaddr, &salen) == 0 &&
popd_remoteaddr.sin_family == AF_INET) {
hp = gethostbyaddr((char *)&popd_remoteaddr.sin_addr,
sizeof(popd_remoteaddr.sin_addr), AF_INET);
if (hp != NULL) {
strncpy(popd_clienthost, hp->h_name, sizeof(popd_clienthost)-30);
popd_clienthost[sizeof(popd_clienthost)-30] = '\0';
} else {
popd_clienthost[0] = '\0';
}
strcat(popd_clienthost, "[");
strcat(popd_clienthost, inet_ntoa(popd_remoteaddr.sin_addr));
strcat(popd_clienthost, "]");
salen = sizeof(popd_localaddr);
if (getsockname(0, (struct sockaddr *)&popd_localaddr, &salen) == 0) {
popd_haveaddr = 1;
}
}
/* other params should be filled in */
if (sasl_server_new("pop", config_servername, NULL, NULL, NULL,
NULL, 0, &popd_saslconn) != SASL_OK)
fatal("SASL failed initializing: sasl_server_new()",EC_TEMPFAIL);
/* will always return something valid */
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
sasl_setprop(popd_saslconn, SASL_SEC_PROPS, secprops);
if(iptostring((struct sockaddr *)&popd_localaddr,
sizeof(struct sockaddr_in), localip, 60) == 0) {
sasl_setprop(popd_saslconn, SASL_IPLOCALPORT, localip);
saslprops.iplocalport = xstrdup(localip);
}
if(iptostring((struct sockaddr *)&popd_remoteaddr,
sizeof(struct sockaddr_in), remoteip, 60) == 0) {
sasl_setprop(popd_saslconn, SASL_IPREMOTEPORT, remoteip);
saslprops.ipremoteport = xstrdup(remoteip);
}
proc_register("pop3d", popd_clienthost, NULL, NULL);
/* Set inactivity timer */
timeout = config_getint("poptimeout", 10);
if (timeout < 10) timeout = 10;
prot_settimeout(popd_in, timeout*60);
prot_setflushonread(popd_in, popd_out);
if (kflag) kpop();
/* we were connected on pop3s port so we should do
TLS negotiation immediatly */
if (pop3s == 1) cmd_starttls(1);
/* Create APOP challenge for banner */
if (!sasl_mkchal(popd_saslconn, popd_apop_chal, sizeof(popd_apop_chal), 1)) {
syslog(LOG_ERR, "APOP disabled: can't create challenge");
*popd_apop_chal = 0;
}
prot_printf(popd_out, "+OK %s Cyrus POP3 %s server ready %s\r\n",
config_servername, CYRUS_VERSION,
apop_enabled() ? popd_apop_chal : "");
cmdloop();
/* QUIT executed */
/* don't bother reusing KPOP connections */
if (kflag) shut_down(0);
/* cleanup */
popd_reset();
return 0;
}
/* called if 'service_init()' was called but not 'service_main()' */
void service_abort(int error)
{
mboxlist_close();
mboxlist_done();
}
void usage(void)
{
prot_printf(popd_out, "-ERR usage: pop3d [-C <alt_config>] [-k] [-s]\r\n");
prot_flush(popd_out);
exit(EC_USAGE);
}
/*
* Cleanly shut down and exit
*/
void shut_down(int code)
{
proc_cleanup();
if (popd_mailbox) {
mailbox_close(popd_mailbox);
}
#ifdef HAVE_SSL
tls_shutdown_serverengine();
#endif
prot_flush(popd_out);
exit(code);
}
void fatal(const char* s, int code)
{
static int recurse_code = 0;
if (recurse_code) {
/* We were called recursively. Just give up */
proc_cleanup();
exit(recurse_code);
}
recurse_code = code;
- prot_printf(popd_out, "-ERR Fatal error: %s\r\n", s);
+ prot_printf(popd_out, "-ERR [SYS/PERM] Fatal error: %s\r\n", s);
prot_flush(popd_out);
shut_down(code);
}
#ifdef HAVE_KRB
/*
* MIT's kludge of a kpop protocol
* Client does a krb_sendauth() first thing
*/
void kpop(void)
{
Key_schedule schedule;
KTEXT_ST ticket;
char instance[INST_SZ];
char version[9];
const char *srvtab;
int r;
if (!popd_haveaddr) {
fatal("Cannot get client's IP address", EC_OSERR);
}
srvtab = config_getstring("srvtab", "");
strcpy(instance, "*");
r = krb_recvauth(0L, 0, &ticket, "pop", instance,
&popd_remoteaddr, (struct sockaddr_in *) NULL,
&kdata, (char*) srvtab, schedule, version);
if (r) {
- prot_printf(popd_out, "-ERR Kerberos authentication failure: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] Kerberos authentication failure: %s\r\n",
krb_err_txt[r]);
syslog(LOG_NOTICE,
"badlogin: %s kpop ? %s%s%s@%s %s",
popd_clienthost, kdata.pname,
kdata.pinst[0] ? "." : "", kdata.pinst,
kdata.prealm, krb_err_txt[r]);
shut_down(0);
}
r = krb_get_lrealm(klrealm,1);
if (r) {
- prot_printf(popd_out, "-ERR Kerberos failure: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] Kerberos failure: %s\r\n",
krb_err_txt[r]);
syslog(LOG_NOTICE,
"badlogin: %s kpop ? %s%s%s@%s krb_get_lrealm: %s",
popd_clienthost, kdata.pname,
kdata.pinst[0] ? "." : "", kdata.pinst,
kdata.prealm, krb_err_txt[r]);
shut_down(0);
}
}
#else
void kpop(void)
{
usage();
}
#endif
/*
* Top-level command loop parsing
*/
static void cmdloop(void)
{
char inputbuf[8192];
char *p, *arg;
unsigned msg = 0;
for (;;) {
signals_poll();
if (!prot_fgets(inputbuf, sizeof(inputbuf), popd_in)) {
shut_down(0);
}
p = inputbuf + strlen(inputbuf);
if (p > inputbuf && p[-1] == '\n') *--p = '\0';
if (p > inputbuf && p[-1] == '\r') *--p = '\0';
/* Parse into keword and argument */
for (p = inputbuf; *p && !isspace((int) *p); p++);
if (*p) {
*p++ = '\0';
arg = p;
if (strcasecmp(inputbuf, "pass") != 0) {
while (*arg && isspace((int) *arg)) {
arg++;
}
}
if (!*arg) {
prot_printf(popd_out, "-ERR Syntax error\r\n");
continue;
}
}
else {
arg = 0;
}
lcase(inputbuf);
if (!strcmp(inputbuf, "quit")) {
if (!arg) {
if (popd_mailbox) {
if (!mailbox_lock_index(popd_mailbox)) {
popd_mailbox->pop3_last_login = popd_login_time;
mailbox_write_index_header(popd_mailbox);
mailbox_unlock_index(popd_mailbox);
}
for (msg = 1; msg <= popd_exists; msg++) {
if (popd_msg[msg].deleted) break;
}
if (msg <= popd_exists) {
(void) mailbox_expunge(popd_mailbox, 1, expungedeleted, 0);
}
}
prot_printf(popd_out, "+OK\r\n");
return;
}
else prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (!strcmp(inputbuf, "capa")) {
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
} else {
cmd_capa();
}
}
else if (!strcmp(inputbuf, "stls") && tls_enabled("pop3")) {
if (arg) {
prot_printf(popd_out,
"-ERR STLS doesn't take any arguments\r\n");
} else {
cmd_starttls(0);
}
}
else if (!popd_mailbox) {
if (!strcmp(inputbuf, "user")) {
if (!arg) {
prot_printf(popd_out, "-ERR Missing argument\r\n");
}
else {
cmd_user(arg);
}
}
else if (!strcmp(inputbuf, "pass")) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else cmd_pass(arg);
}
else if (!strcmp(inputbuf, "apop") && apop_enabled()) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else cmd_apop(arg);
}
else if (!strcmp(inputbuf, "auth")) {
cmd_auth(arg);
}
else {
prot_printf(popd_out, "-ERR Unrecognized command\r\n");
}
}
else if (!strcmp(inputbuf, "stat")) {
unsigned nmsgs = 0, totsize = 0;
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else {
for (msg = 1; msg <= popd_exists; msg++) {
if (!popd_msg[msg].deleted) {
nmsgs++;
totsize += popd_msg[msg].size;
}
}
prot_printf(popd_out, "+OK %u %u\r\n", nmsgs, totsize);
}
}
else if (!strcmp(inputbuf, "list")) {
if (arg) {
msg = parsenum(&arg);
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (msg < 1 || msg > popd_exists ||
popd_msg[msg].deleted) {
prot_printf(popd_out, "-ERR No such message\r\n");
}
else {
prot_printf(popd_out, "+OK %u %u\r\n", msg, popd_msg[msg].size);
}
}
else {
prot_printf(popd_out, "+OK scan listing follows\r\n");
for (msg = 1; msg <= popd_exists; msg++) {
if (!popd_msg[msg].deleted) {
prot_printf(popd_out, "%u %u\r\n", msg, popd_msg[msg].size);
}
}
prot_printf(popd_out, ".\r\n");
}
}
else if (!strcmp(inputbuf, "retr")) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else {
msg = parsenum(&arg);
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (msg < 1 || msg > popd_exists ||
popd_msg[msg].deleted) {
prot_printf(popd_out, "-ERR No such message\r\n");
}
else {
if (msg > popd_highest) popd_highest = msg;
blat(msg, -1);
}
}
}
else if (!strcmp(inputbuf, "dele")) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else {
msg = parsenum(&arg);
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (msg < 1 || msg > popd_exists ||
popd_msg[msg].deleted) {
prot_printf(popd_out, "-ERR No such message\r\n");
}
else {
popd_msg[msg].deleted = 1;
if (msg > popd_highest) popd_highest = msg;
prot_printf(popd_out, "+OK message deleted\r\n");
}
}
}
else if (!strcmp(inputbuf, "noop")) {
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else {
prot_printf(popd_out, "+OK\r\n");
}
}
else if (!strcmp(inputbuf, "last")) {
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else {
prot_printf(popd_out, "+OK %u\r\n", popd_highest);
}
}
else if (!strcmp(inputbuf, "rset")) {
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else {
popd_highest = 0;
for (msg = 1; msg <= popd_exists; msg++) {
popd_msg[msg].deleted = 0;
}
prot_printf(popd_out, "+OK\r\n");
}
}
else if (!strcmp(inputbuf, "top")) {
int lines;
if (arg) msg = parsenum(&arg);
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else {
lines = parsenum(&arg);
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (msg < 1 || msg > popd_exists ||
popd_msg[msg].deleted) {
prot_printf(popd_out, "-ERR No such message\r\n");
}
else if (lines < 0) {
prot_printf(popd_out, "-ERR Invalid number of lines\r\n");
}
else {
blat(msg, lines);
}
}
}
else if (!strcmp(inputbuf, "uidl")) {
if (arg) {
msg = parsenum(&arg);
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (msg < 1 || msg > popd_exists ||
popd_msg[msg].deleted) {
prot_printf(popd_out, "-ERR No such message\r\n");
}
else {
prot_printf(popd_out, "+OK %u %u\r\n", msg, popd_msg[msg].uid);
}
}
else {
prot_printf(popd_out, "+OK unique-id listing follows\r\n");
for (msg = 1; msg <= popd_exists; msg++) {
if (!popd_msg[msg].deleted) {
prot_printf(popd_out, "%u %u\r\n", msg,
popd_msg[msg].uid);
}
}
prot_printf(popd_out, ".\r\n");
}
}
else {
prot_printf(popd_out, "-ERR Unrecognized command\r\n");
}
}
}
#ifdef HAVE_SSL
static void cmd_starttls(int pop3s)
{
int result;
int *layerp;
sasl_ssf_t ssf;
char *auth_id;
/* SASL and openssl have different ideas about whether ssf is signed */
layerp = (int *) &ssf;
if (popd_starttls_done == 1)
{
prot_printf(popd_out, "-ERR %s\r\n",
"Already successfully executed STLS");
return;
}
result=tls_init_serverengine("pop3",
5, /* depth to verify */
!pop3s, /* can client auth? */
0, /* require client to auth? */
!pop3s); /* TLS only? */
if (result == -1) {
syslog(LOG_ERR, "[pop3d] error initializing TLS");
if (pop3s == 0)
- prot_printf(popd_out, "-ERR %s\r\n", "Error initializing TLS");
+ prot_printf(popd_out, "-ERR [SYS/PERM] %s\r\n", "Error initializing TLS");
else
fatal("tls_init() failed",EC_TEMPFAIL);
return;
}
if (pop3s == 0)
{
prot_printf(popd_out, "+OK %s\r\n", "Begin TLS negotiation now");
/* must flush our buffers before starting tls */
prot_flush(popd_out);
}
result=tls_start_servertls(0, /* read */
1, /* write */
layerp,
&auth_id,
&tls_conn);
/* if error */
if (result==-1) {
if (pop3s == 0) {
- prot_printf(popd_out, "-ERR Starttls failed\r\n");
+ prot_printf(popd_out, "-ERR [SYS/PERM] Starttls failed\r\n");
syslog(LOG_NOTICE, "[pop3d] STARTTLS failed: %s", popd_clienthost);
} else {
syslog(LOG_NOTICE, "pop3s failed: %s", popd_clienthost);
fatal("tls_start_servertls() failed", EC_TEMPFAIL);
}
return;
}
/* tell SASL about the negotiated layer */
result = sasl_setprop(popd_saslconn, SASL_SSF_EXTERNAL, &ssf);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
saslprops.ssf = ssf;
result = sasl_setprop(popd_saslconn, SASL_AUTH_EXTERNAL, auth_id);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
if(saslprops.authid) {
free(saslprops.authid);
saslprops.authid = NULL;
}
if(auth_id)
saslprops.authid = xstrdup(auth_id);
/* tell the prot layer about our new layers */
prot_settls(popd_in, tls_conn);
prot_settls(popd_out, tls_conn);
popd_starttls_done = 1;
}
#else
static void cmd_starttls(int pop3s __attribute__((unused)))
{
fatal("cmd_starttls() called, but no OpenSSL", EC_SOFTWARE);
}
#endif /* HAVE_SSL */
static int apop_enabled(void)
{
/* Check if pseudo APOP mechanism is enabled (challenge == NULL) */
if (sasl_checkapop(popd_saslconn, NULL, 0, NULL, 0) != SASL_OK) return 0;
/* Check if we have a challenge string */
if (!*popd_apop_chal) return 0;
return 1;
}
static void cmd_apop(char *response)
{
int fd;
struct protstream *shutdown_in;
char buf[1024];
char *p;
char shutdownfilename[1024];
int sasl_result;
char *canon_user;
assert(response != NULL);
if (popd_userid) {
- prot_printf(popd_out, "-ERR Must give PASS command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give PASS command\r\n");
return;
}
sprintf(shutdownfilename, "%s/msg/shutdown", config_dir);
if ((fd = open(shutdownfilename, O_RDONLY, 0)) != -1) {
shutdown_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), shutdown_in);
if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
if ((p = strchr(buf, '\n'))!=NULL) *p = 0;
for(p = buf; *p == '['; p++); /* can't have [ be first char */
- prot_printf(popd_out, "-ERR %s\r\n", p);
+ prot_printf(popd_out, "-ERR [SYS/TEMP] %s\r\n", p);
prot_flush(popd_out);
shut_down(0);
}
sasl_result = sasl_checkapop(popd_saslconn,
popd_apop_chal,
strlen(popd_apop_chal),
response,
strlen(response));
/* failed authentication */
if (sasl_result != SASL_OK)
{
sleep(3);
- prot_printf(popd_out, "-ERR authenticating: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] authenticating: %s\r\n",
sasl_errstring(sasl_result, NULL, NULL));
syslog(LOG_NOTICE, "badlogin: %s APOP (%s) %s",
popd_clienthost, popd_apop_chal,
sasl_errdetail(popd_saslconn));
return;
}
/* successful authentication */
/*
* get the userid from SASL --- already canonicalized from
* mysasl_authproc()
*/
sasl_result = sasl_getprop(popd_saslconn, SASL_USERNAME,
(const void **) &canon_user);
popd_userid = xstrdup(canon_user);
if (sasl_result != SASL_OK) {
prot_printf(popd_out,
- "-ERR weird SASL error %d getting SASL_USERNAME\r\n",
+ "-ERR [AUTH] weird SASL error %d getting SASL_USERNAME\r\n",
sasl_result);
return;
}
syslog(LOG_NOTICE, "login: %s %s APOP %s",
popd_clienthost, popd_userid, "User logged in");
openinbox();
}
void
cmd_user(user)
char *user;
{
int fd;
struct protstream *shutdown_in;
char buf[1024];
char *p;
char shutdownfilename[1024];
/* possibly disallow USER */
if (!(kflag || popd_starttls_done ||
config_getswitch("allowplaintext", 1))) {
prot_printf(popd_out,
- "-ERR USER command only available under a layer\r\n");
+ "-ERR [AUTH] USER command only available under a layer\r\n");
return;
}
if (popd_userid) {
- prot_printf(popd_out, "-ERR Must give PASS command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give PASS command\r\n");
return;
}
sprintf(shutdownfilename, "%s/msg/shutdown", config_dir);
if ((fd = open(shutdownfilename, O_RDONLY, 0)) != -1) {
shutdown_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), shutdown_in);
if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
if ((p = strchr(buf, '\n'))!=NULL) *p = 0;
for(p = buf; *p == '['; p++); /* can't have [ be first char */
- prot_printf(popd_out, "-ERR %s\r\n", p);
+ prot_printf(popd_out, "-ERR [SYS/TEMP] %s\r\n", p);
prot_flush(popd_out);
shut_down(0);
}
else if (!(p = auth_canonifyid(user,0)) ||
/* '.' isn't allowed if '.' is the hierarchy separator */
(popd_namespace.hier_sep == '.' && strchr(p, '.')) ||
strlen(p) + 6 > MAX_MAILBOX_PATH) {
- prot_printf(popd_out, "-ERR Invalid user\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid user\r\n");
syslog(LOG_NOTICE,
"badlogin: %s plaintext %s invalid user",
popd_clienthost, beautify_string(user));
}
else {
popd_userid = xstrdup(p);
prot_printf(popd_out, "+OK Name is a valid mailbox\r\n");
}
}
void cmd_pass(pass)
char *pass;
{
char *reply = 0;
int plaintextloginpause;
if (!popd_userid) {
- prot_printf(popd_out, "-ERR Must give USER command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give USER command\r\n");
return;
}
#ifdef HAVE_KRB
if (kflag) {
if (strcmp(popd_userid, kdata.pname) != 0 ||
kdata.pinst[0] ||
strcmp(klrealm, kdata.prealm) != 0) {
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
syslog(LOG_NOTICE,
"badlogin: %s kpop %s %s%s%s@%s access denied",
popd_clienthost, popd_userid,
kdata.pname, kdata.pinst[0] ? "." : "",
kdata.pinst, kdata.prealm);
return;
}
syslog(LOG_NOTICE, "login: %s %s kpop", popd_clienthost, popd_userid);
openinbox();
return;
}
#endif
if (!strcmp(popd_userid, "anonymous")) {
if (config_getswitch("allowanonymouslogin", 0)) {
pass = beautify_string(pass);
if (strlen(pass) > 500) pass[500] = '\0';
syslog(LOG_NOTICE, "login: %s anonymous %s",
popd_clienthost, pass);
}
else {
syslog(LOG_NOTICE, "badlogin: %s anonymous login refused",
popd_clienthost);
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
return;
}
}
else if (sasl_checkpass(popd_saslconn,
popd_userid,
strlen(popd_userid),
pass,
strlen(pass))!=SASL_OK) {
if (reply) {
syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
popd_clienthost, popd_userid, reply);
}
sleep(3);
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
free(popd_userid);
popd_userid = 0;
return;
}
else {
syslog(LOG_NOTICE, "login: %s %s plaintext %s",
popd_clienthost, popd_userid, reply ? reply : "");
if ((plaintextloginpause = config_getint("plaintextloginpause", 0))!=0) {
sleep(plaintextloginpause);
}
}
openinbox();
}
/* Handle the POP3 Extension extension.
*/
void
cmd_capa()
{
int minpoll = config_getint("popminpoll", 0) * 60;
int expire = config_getint("popexpiretime", -1);
unsigned mechcount;
const char *mechlist;
prot_printf(popd_out, "+OK List of capabilities follows\r\n");
/* SASL special case: print SASL, then a list of supported capabilities */
if (sasl_listmech(popd_saslconn,
NULL, /* should be id string */
"SASL ", " ", "\r\n",
&mechlist,
NULL, &mechcount) == SASL_OK && mechcount > 0) {
prot_write(popd_out, mechlist, strlen(mechlist));
}
if (tls_enabled("pop3")) {
prot_printf(popd_out, "STLS\r\n");
}
if (expire < 0) {
prot_printf(popd_out, "EXPIRE NEVER\r\n");
} else {
prot_printf(popd_out, "EXPIRE %d\r\n", expire);
}
prot_printf(popd_out, "LOGIN-DELAY %d\r\n", minpoll);
prot_printf(popd_out, "TOP\r\n");
prot_printf(popd_out, "UIDL\r\n");
prot_printf(popd_out, "PIPELINING\r\n");
prot_printf(popd_out, "RESP-CODES\r\n");
+ prot_printf(popd_out, "AUTH-RESP-CODE\r\n");
if (kflag || popd_starttls_done || config_getswitch("allowplaintext", 1)) {
prot_printf(popd_out, "USER\r\n");
}
prot_printf(popd_out,
"IMPLEMENTATION Cyrus POP3 server %s\r\n",
CYRUS_VERSION);
prot_printf(popd_out, ".\r\n");
prot_flush(popd_out);
}
/* according to RFC 2449, since we advertise the "SASL" capability, we
* must accept an optional second argument of the initial client
* response (base64 encoded!).
*/
void cmd_auth(char *arg)
{
int sasl_result;
static struct buf clientin;
unsigned int clientinlen=0;
char *authtype;
const char *serverout;
unsigned int serveroutlen;
char *cin;
char *canon_user;
/* if client didn't specify an argument we give them the list */
if (!arg) {
const char *sasllist;
unsigned int mechnum;
prot_printf(popd_out, "+OK List of supported mechanisms follows\r\n");
/* CRLF seperated, dot terminated */
if (sasl_listmech(popd_saslconn, NULL,
"", "\r\n", "\r\n",
&sasllist,
NULL, &mechnum) == SASL_OK) {
if (mechnum>0) {
prot_printf(popd_out,"%s",sasllist);
}
}
prot_printf(popd_out, ".\r\n");
return;
}
authtype = arg;
while (*arg && !isspace((int) *arg)) {
arg++;
}
if (isspace((int) *arg)) {
/* null terminate authtype, get argument */
*arg++ = '\0';
} else {
/* no optional client response */
arg = NULL;
}
/* if arg != NULL, it's an initial client response */
if (arg) {
int arglen = strlen(arg);
clientin.alloc = arglen + 1;
cin = clientin.s = xmalloc(clientin.alloc);
sasl_result = sasl_decode64(arg, arglen,
clientin.s, clientin.alloc, &clientinlen);
} else {
sasl_result = SASL_OK;
cin = NULL;
clientinlen = 0;
}
/* server did specify a command, so let's try to authenticate */
if (sasl_result == SASL_OK || sasl_result == SASL_CONTINUE)
sasl_result = sasl_server_start(popd_saslconn, authtype,
cin, clientinlen,
&serverout, &serveroutlen);
/* sasl_server_start will return SASL_OK or SASL_CONTINUE on success */
while (sasl_result == SASL_CONTINUE)
{
char c;
/* print the message to the user */
printauthready(popd_out, serveroutlen, (unsigned char *)serverout);
c = prot_getc(popd_in);
if(c == '*') {
eatline(popd_in,c);
prot_printf(popd_out,
- "-ERR Client canceled authentication\r\n");
+ "-ERR [AUTH] Client canceled authentication\r\n");
reset_saslconn(&popd_saslconn);
return;
} else {
prot_ungetc(c, popd_in);
}
/* get string from user */
clientinlen = getbase64string(popd_in, &clientin);
if (clientinlen == -1) {
reset_saslconn(&popd_saslconn);
- prot_printf(popd_out, "-ERR Invalid base64 string\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid base64 string\r\n");
return;
}
sasl_result = sasl_server_step(popd_saslconn,
clientin.s,
clientinlen,
&serverout, &serveroutlen);
}
/* failed authentication */
if (sasl_result != SASL_OK)
{
sleep(3);
reset_saslconn(&popd_saslconn);
- prot_printf(popd_out, "-ERR authenticating: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] authenticating: %s\r\n",
sasl_errstring(sasl_result, NULL, NULL));
if (authtype) {
syslog(LOG_NOTICE, "badlogin: %s %s %s",
popd_clienthost, authtype,
sasl_errstring(sasl_result, NULL, NULL));
} else {
syslog(LOG_NOTICE, "badlogin: %s %s",
popd_clienthost, authtype);
}
return;
}
/* successful authentication */
/* get the userid from SASL --- already canonicalized from
* mysasl_authproc()
*/
sasl_result = sasl_getprop(popd_saslconn, SASL_USERNAME,
(const void **) &canon_user);
popd_userid = xstrdup(canon_user);
if (sasl_result != SASL_OK) {
prot_printf(popd_out,
- "-ERR weird SASL error %d getting SASL_USERNAME\r\n",
+ "-ERR [AUTH] weird SASL error %d getting SASL_USERNAME\r\n",
sasl_result);
return;
}
if (openinbox()==0) {
proc_register("pop3d", popd_clienthost,
popd_userid, popd_mailbox->name);
syslog(LOG_NOTICE, "login: %s %s %s %s", popd_clienthost, popd_userid,
authtype, "User logged in");
prot_setsasl(popd_in, popd_saslconn);
prot_setsasl(popd_out, popd_saslconn);
}
}
/*
* Complete the login process by opening and locking the user's inbox
*/
int openinbox(void)
{
char inboxname[MAX_MAILBOX_PATH];
int r, msg;
struct index_record record;
int minpoll;
popd_login_time = time(0);
/* Translate any separators in userid */
mboxname_hiersep_tointernal(&popd_namespace, popd_userid);
strcpy(inboxname, "user.");
strcat(inboxname, popd_userid);
r = mailbox_open_header(inboxname, 0, &mboxstruct);
if (r) {
free(popd_userid);
popd_userid = 0;
sleep(3);
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [SYS/PERM] Unable to open maildrop\r\n");
return 1;
}
r = mailbox_open_index(&mboxstruct);
if (!r) r = mailbox_lock_pop(&mboxstruct);
if (r) {
mailbox_close(&mboxstruct);
free(popd_userid);
popd_userid = 0;
prot_printf(popd_out, "-ERR [IN-USE] Unable to lock maildrop\r\n");
return 1;
}
if ((minpoll = config_getint("popminpoll", 0)) &&
mboxstruct.pop3_last_login + 60*minpoll > popd_login_time) {
prot_printf(popd_out,
"-ERR [LOGIN-DELAY] Logins must be at least %d minute%s apart\r\n",
minpoll, minpoll > 1 ? "s" : "");
if (!mailbox_lock_index(&mboxstruct)) {
mboxstruct.pop3_last_login = popd_login_time;
mailbox_write_index_header(&mboxstruct);
}
mailbox_close(&mboxstruct);
free(popd_userid);
popd_userid = 0;
return 1;
}
if (chdir(mboxstruct.path)) {
syslog(LOG_ERR, "IOERROR: changing directory to %s: %m",
mboxstruct.path);
r = IMAP_IOERROR;
}
if (!r) {
popd_exists = mboxstruct.exists;
popd_highest = 0;
popd_msg = (struct msg *)xmalloc((popd_exists+1) * sizeof(struct msg));
for (msg = 1; msg <= popd_exists; msg++) {
if ((r = mailbox_read_index_record(&mboxstruct, msg, &record))!=0)
break;
popd_msg[msg].uid = record.uid;
popd_msg[msg].size = record.size;
popd_msg[msg].deleted = 0;
}
}
if (r) {
mailbox_close(&mboxstruct);
free(popd_userid);
popd_userid = 0;
free(popd_msg);
popd_msg = 0;
popd_exists = 0;
- prot_printf(popd_out, "-ERR Unable to read maildrop\r\n");
+ prot_printf(popd_out, "-ERR [SYS/PERM] Unable to read maildrop\r\n");
return 1;
}
popd_mailbox = &mboxstruct;
proc_register("pop3d", popd_clienthost, popd_userid, popd_mailbox->name);
/* Create telemetry log */
telemetry_log(popd_userid, popd_in, popd_out);
prot_printf(popd_out, "+OK Maildrop locked and ready\r\n");
return 0;
}
static void blat(int msg,int lines)
{
FILE *msgfile;
char buf[4096];
char fnamebuf[MAILBOX_FNAME_LEN];
int thisline = -2;
mailbox_message_get_fname(popd_mailbox, popd_msg[msg].uid, fnamebuf);
msgfile = fopen(fnamebuf, "r");
if (!msgfile) {
- prot_printf(popd_out, "-ERR Could not read message file\r\n");
+ prot_printf(popd_out, "-ERR [SYS/PERM] Could not read message file\r\n");
return;
}
prot_printf(popd_out, "+OK Message follows\r\n");
while (lines != thisline) {
if (!fgets(buf, sizeof(buf), msgfile)) break;
if (thisline < 0) {
if (buf[0] == '\r' && buf[1] == '\n') thisline = 0;
}
else thisline++;
if (buf[0] == '.') prot_putc('.', popd_out);
do {
prot_printf(popd_out, "%s", buf);
}
while (buf[strlen(buf)-1] != '\n' && fgets(buf, sizeof(buf), msgfile));
}
fclose(msgfile);
/* Protect against messages not ending in CRLF */
if (buf[strlen(buf)-1] != '\n') prot_printf(popd_out, "\r\n");
prot_printf(popd_out, ".\r\n");
}
static int parsenum(char **ptr)
{
char *p = *ptr;
int result = 0;
if (!isdigit((int) *p)) {
*ptr = 0;
return -1;
}
while (*p && isdigit((int) *p)) {
result = result * 10 + *p++ - '0';
}
if (*p) {
while (*p && isspace((int) *p)) p++;
*ptr = p;
}
else *ptr = 0;
return result;
}
static int expungedeleted(struct mailbox *mailbox, void *rock, char *index)
{
int msg;
int uid = ntohl(*((bit32 *)(index+OFFSET_UID)));
for (msg = 1; msg <= popd_exists; msg++) {
if (popd_msg[msg].uid == uid) {
return popd_msg[msg].deleted;
}
}
return 0;
}
/* Reset the given sasl_conn_t to a sane state */
static int reset_saslconn(sasl_conn_t **conn)
{
int ret;
sasl_security_properties_t *secprops = NULL;
sasl_dispose(conn);
/* do initialization typical of service_main */
ret = sasl_server_new("pop", config_servername,
NULL, NULL, NULL,
NULL, 0, conn);
if(ret != SASL_OK) return ret;
if(saslprops.ipremoteport)
ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
saslprops.ipremoteport);
if(ret != SASL_OK) return ret;
if(saslprops.iplocalport)
ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
saslprops.iplocalport);
if(ret != SASL_OK) return ret;
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
if(ret != SASL_OK) return ret;
/* end of service_main initialization excepting SSF */
/* If we have TLS/SSL info, set it */
if(saslprops.ssf) {
ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
}
if(ret != SASL_OK) return ret;
if(saslprops.authid) {
ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
if(ret != SASL_OK) return ret;
}
/* End TLS/SSL Info */
return SASL_OK;
}
diff --git a/imap/pop3proxyd.c b/imap/pop3proxyd.c
index 28b772d1b..6bdd7a288 100644
--- a/imap/pop3proxyd.c
+++ b/imap/pop3proxyd.c
@@ -1,1335 +1,1337 @@
/* pop3proxyd.c -- POP3 server protocol parsing (proxy)
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
- * $Id: pop3proxyd.c,v 1.26 2001/11/27 02:24:59 ken3 Exp $
+ * $Id: pop3proxyd.c,v 1.27 2001/12/01 04:03:16 ken3 Exp $
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/param.h>
#include <syslog.h>
#include <com_err.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include "prot.h"
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#include "acl.h"
#include "util.h"
#include "auth.h"
#include "imapconf.h"
#include "tls.h"
#include "iptostring.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "version.h"
#include "xmalloc.h"
#include "mboxlist.h"
#ifdef HAVE_KRB
/* kerberos des is purported to conflict with OpenSSL DES */
#define DES_DEFS
#include <krb.h>
/* MIT's kpop authentication kludge */
char klrealm[REALM_SZ];
AUTH_DAT kdata;
#endif /* HAVE_KRB */
static int kflag = 0;
extern int optind;
extern char *optarg;
extern int opterr;
extern int errno;
#ifdef HAVE_SSL
static SSL *tls_conn;
#endif /* HAVE_SSL */
sasl_conn_t *popd_saslconn; /* the sasl connection context */
char *popd_userid = 0;
struct sockaddr_in popd_localaddr, popd_remoteaddr;
int popd_haveaddr = 0;
char popd_clienthost[250] = "[local]";
struct protstream *popd_out, *popd_in;
int popd_starttls_done = 0;
int popd_auth_done = 0;
struct protstream *backend_out, *backend_in;
int backend_sock;
sasl_conn_t *backend_saslconn;
/* current namespace */
static struct namespace popd_namespace;
static void cmd_apop(char *response);
static int apop_enabled(void);
static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* <rand.time@hostname> */
static void cmd_auth();
static void cmd_capa();
static void cmd_pass();
static void cmd_user();
static void cmd_starttls(int pop3s);
static void cmdloop(void);
static void kpop(void);
static void usage(void);
static void openproxy(void);
static void bitpipe(void);
extern void setproctitle_init(int argc, char **argv, char **envp);
extern int proc_register(const char *progname, const char *clienthost,
const char *userid, const char *mailbox);
extern void proc_cleanup(void);
void shut_down(int code) __attribute__ ((noreturn));
/* Enable the resetting of a sasl_conn_t */
static int reset_saslconn(sasl_conn_t **conn);
static struct
{
char *ipremoteport;
char *iplocalport;
sasl_ssf_t ssf;
char *authid;
} saslprops = {NULL,NULL,0,NULL};
static struct sasl_callback mysasl_cb[] = {
{ SASL_CB_GETOPT, &mysasl_config, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
/*
* run once when process is forked;
* MUST NOT exit directly; must return with non-zero error code
*/
int service_init(int argc, char **argv, char **envp)
{
int r;
config_changeident("pop3d");
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
setproctitle_init(argc, argv, envp);
/* set signal handlers */
signals_set_shutdown(&shut_down);
signals_add_handlers();
signal(SIGPIPE, SIG_IGN);
/* set the SASL allocation functions */
sasl_set_alloc((sasl_malloc_t *) &xmalloc,
(sasl_calloc_t *) &calloc,
(sasl_realloc_t *) &xrealloc,
(sasl_free_t *) &free);
/* load the SASL plugins */
if ((r = sasl_server_init(mysasl_cb, "Cyrus")) != SASL_OK) {
syslog(LOG_ERR, "SASL failed initializing: sasl_server_init(): %s",
sasl_errstring(r, NULL, NULL));
return 2;
}
/* open the mboxlist, we'll need it for real work */
mboxlist_init(0);
mboxlist_open(NULL);
/* Set namespace */
if ((r = mboxname_init_namespace(&popd_namespace, 0)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
return 0;
}
/*
* run for each accepted connection
*/
int service_main(int argc, char **argv, char **envp)
{
int pop3s = 0;
int opt;
socklen_t salen;
struct hostent *hp;
char localip[60], remoteip[60];
int timeout;
sasl_security_properties_t *secprops=NULL;
signals_poll();
popd_in = prot_new(0, 0);
popd_out = prot_new(1, 1);
while ((opt = getopt(argc, argv, "C:sk")) != EOF) {
switch(opt) {
case 'C': /* alt config file - handled by service::main() */
break;
case 's': /* pop3s (do starttls right away) */
pop3s = 1;
if (!tls_enabled("pop3")) {
syslog(LOG_ERR, "pop3s: required OpenSSL options not present");
fatal("pop3s: required OpenSSL options not present",
EC_CONFIG);
}
case 'k':
kflag++;
break;
default:
usage();
}
}
/* Find out name of client host */
salen = sizeof(popd_remoteaddr);
if (getpeername(0, (struct sockaddr *)&popd_remoteaddr, &salen) == 0 &&
popd_remoteaddr.sin_family == AF_INET) {
hp = gethostbyaddr((char *)&popd_remoteaddr.sin_addr,
sizeof(popd_remoteaddr.sin_addr), AF_INET);
if (hp != NULL) {
strncpy(popd_clienthost, hp->h_name, sizeof(popd_clienthost)-30);
popd_clienthost[sizeof(popd_clienthost)-30] = '\0';
} else {
popd_clienthost[0] = '\0';
}
strcat(popd_clienthost, "[");
strcat(popd_clienthost, inet_ntoa(popd_remoteaddr.sin_addr));
strcat(popd_clienthost, "]");
salen = sizeof(popd_localaddr);
if (getsockname(0, (struct sockaddr *)&popd_localaddr, &salen) == 0) {
popd_haveaddr = 1;
}
}
/* other params should be filled in */
if (sasl_server_new("pop", config_servername, NULL, NULL, NULL,
NULL, 0, &popd_saslconn) != SASL_OK)
fatal("SASL failed initializing: sasl_server_new()",EC_TEMPFAIL);
/* will always return something valid */
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
sasl_setprop(popd_saslconn, SASL_SEC_PROPS, secprops);
if(iptostring((struct sockaddr *)&popd_localaddr,
sizeof(struct sockaddr_in), localip, 60) == 0) {
sasl_setprop(popd_saslconn, SASL_IPLOCALPORT, localip);
saslprops.iplocalport = xstrdup(localip);
}
if(iptostring((struct sockaddr *)&popd_remoteaddr,
sizeof(struct sockaddr_in), remoteip, 60) == 0) {
sasl_setprop(popd_saslconn, SASL_IPREMOTEPORT, remoteip);
saslprops.ipremoteport = xstrdup(remoteip);
}
proc_register("pop3d", popd_clienthost, NULL, NULL);
/* Set inactivity timer */
timeout = config_getint("poptimeout", 10);
if (timeout < 10) timeout = 10;
prot_settimeout(popd_in, timeout*60);
prot_setflushonread(popd_in, popd_out);
if (kflag) kpop();
/* we were connected on pop3s port so we should do
TLS negotiation immediatly */
if (pop3s == 1) cmd_starttls(1);
/* Create APOP challenge for banner */
if (!sasl_mkchal(popd_saslconn, popd_apop_chal, sizeof(popd_apop_chal), 1)) {
syslog(LOG_ERR, "APOP disabled: can't create challenge");
*popd_apop_chal = 0;
}
prot_printf(popd_out, "+OK %s Cyrus POP3 Murder %s server ready %s\r\n",
config_servername, CYRUS_VERSION,
apop_enabled() ? popd_apop_chal : "");
cmdloop();
return 0;
}
/* called if 'service_init()' was called but not 'service_main()' */
void service_abort(int error)
{
mboxlist_close();
mboxlist_done();
}
void usage(void)
{
prot_printf(popd_out,
"-ERR usage: pop3proxyd [-C <alt_config>]"
" [-k] [-s]\r\n");
prot_flush(popd_out);
exit(EC_USAGE);
}
/*
* Cleanly shut down and exit
*/
void shut_down(int code)
{
proc_cleanup();
#ifdef HAVE_SSL
tls_shutdown_serverengine();
#endif
prot_flush(popd_out);
exit(code);
}
void fatal(const char* s, int code)
{
static int recurse_code = 0;
if (recurse_code) {
/* We were called recursively. Just give up */
proc_cleanup();
exit(recurse_code);
}
recurse_code = code;
- prot_printf(popd_out, "-ERR Fatal error: %s\r\n", s);
+ prot_printf(popd_out, "-ERR [SYS/PERM] Fatal error: %s\r\n", s);
prot_flush(popd_out);
shut_down(code);
}
/*
* Found a shutdown file: Spit out an untagged BYE and shut down
*/
void shutdown_file(void)
{
int fd;
struct protstream *shutdown_in;
char buf[1024];
char *p;
static char shutdownfilename[1024];
if (!shutdownfilename[0])
sprintf(shutdownfilename, "%s/msg/shutdown", config_dir);
if ((fd = open(shutdownfilename, O_RDONLY, 0)) == -1) return;
shutdown_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), shutdown_in);
if ((p = strchr(buf, '\r')) != NULL) *p = 0;
if ((p = strchr(buf, '\n')) != NULL) *p = 0;
for (p = buf; *p == '['; p++); /* can't have [ be first char, sigh */
- prot_printf(popd_out, "-ERR %s\r\n", p);
+ prot_printf(popd_out, "-ERR [SYS/TEMP] %s\r\n", p);
shut_down(0);
}
#ifdef HAVE_KRB
/*
* MIT's kludge of a kpop protocol
* Client does a krb_sendauth() first thing
*/
static void kpop(void)
{
Key_schedule schedule;
KTEXT_ST ticket;
char instance[INST_SZ];
char version[9];
const char *srvtab;
int r;
if (!popd_haveaddr) {
fatal("Cannot get client's IP address", EC_OSERR);
}
srvtab = config_getstring("srvtab", "");
strcpy(instance, "*");
r = krb_recvauth(0L, 0, &ticket, "pop", instance,
&popd_remoteaddr, (struct sockaddr_in *) NULL,
&kdata, (char*) srvtab, schedule, version);
if (r) {
- prot_printf(popd_out, "-ERR Kerberos authentication failure: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] Kerberos authentication failure: %s\r\n",
krb_err_txt[r]);
syslog(LOG_NOTICE,
"badlogin: %s kpop ? %s%s%s@%s %s",
popd_clienthost, kdata.pname,
kdata.pinst[0] ? "." : "", kdata.pinst,
kdata.prealm, krb_err_txt[r]);
shut_down(0);
}
r = krb_get_lrealm(klrealm,1);
if (r) {
- prot_printf(popd_out, "-ERR Kerberos failure: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] Kerberos failure: %s\r\n",
krb_err_txt[r]);
syslog(LOG_NOTICE,
"badlogin: %s kpop ? %s%s%s@%s krb_get_lrealm: %s",
popd_clienthost, kdata.pname,
kdata.pinst[0] ? "." : "", kdata.pinst,
kdata.prealm, krb_err_txt[r]);
shut_down(0);
}
}
#else
static void kpop(void)
{
usage();
}
#endif
/*
* Top-level command loop parsing
*/
static void cmdloop(void)
{
char inputbuf[8192];
char *p, *arg;
for (;;) {
signals_poll();
if (popd_auth_done) {
bitpipe();
return;
}
/* check for shutdown file */
shutdown_file();
if (!prot_fgets(inputbuf, sizeof(inputbuf), popd_in)) {
return;
}
p = inputbuf + strlen(inputbuf);
if (p > inputbuf && p[-1] == '\n') *--p = '\0';
if (p > inputbuf && p[-1] == '\r') *--p = '\0';
/* Parse into keword and argument */
for (p = inputbuf; *p && !isspace((int) *p); p++);
if (*p) {
*p++ = '\0';
arg = p;
if (strcasecmp(inputbuf, "pass") != 0) {
while (*arg && isspace((int) *arg)) {
arg++;
}
}
if (!*arg) {
prot_printf(popd_out, "-ERR Syntax error\r\n");
continue;
}
}
else {
arg = 0;
}
lcase(inputbuf);
if (!strcmp(inputbuf, "quit")) {
if (!arg) {
prot_printf(popd_out, "+OK\r\n");
shut_down(0);
}
else prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
}
else if (!strcmp(inputbuf, "capa")) {
if (arg) {
prot_printf(popd_out, "-ERR Unexpected extra argument\r\n");
} else {
cmd_capa();
}
}
else if (!strcmp(inputbuf, "user")) {
if (!arg) {
prot_printf(popd_out, "-ERR Missing argument\r\n");
}
else {
cmd_user(arg);
}
}
else if (!strcmp(inputbuf, "pass")) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else cmd_pass(arg);
}
else if (!strcmp(inputbuf, "apop") && apop_enabled()) {
if (!arg) prot_printf(popd_out, "-ERR Missing argument\r\n");
else cmd_apop(arg);
}
else if (!strcmp(inputbuf, "auth")) {
cmd_auth(arg);
}
else if (!strcmp(inputbuf, "stls") && tls_enabled("pop3")) {
if (arg) {
prot_printf(popd_out,
"-ERR STLS doesn't take any arguements\r\n");
} else {
cmd_starttls(0);
}
}
else {
prot_printf(popd_out, "-ERR Unrecognized command\r\n");
}
}
}
#ifdef HAVE_SSL
static void cmd_starttls(int pop3s)
{
char *tls_cert, *tls_key;
int result;
int *layerp;
char *auth_id;
sasl_ssf_t ssf;
/* SASL and openssl have different ideas about whether ssf is signed */
layerp = (int *) &ssf;
if (popd_starttls_done == 1)
{
prot_printf(popd_out, "-ERR %s\r\n",
"Already successfully executed STLS");
return;
}
tls_cert = (char *)config_getstring("tls_pop3_cert_file",
config_getstring("tls_cert_file", ""));
tls_key = (char *)config_getstring("tls_pop3_key_file",
config_getstring("tls_key_file", ""));
result=tls_init_serverengine("pop3",
5, /* depth to verify */
!pop3s, /* can client auth? */
0, /* require client to auth? */
!pop3s); /* TLSv1 only? */
if (result == -1) {
syslog(LOG_ERR, "[pop3d] error initializing TLS");
if (pop3s == 0)
- prot_printf(popd_out, "-ERR %s\r\n", "Error initializing TLS");
+ prot_printf(popd_out, "-ERR [SYS/PERM] %s\r\n", "Error initializing TLS");
else
fatal("tls_init() failed",EC_TEMPFAIL);
return;
}
if (pop3s == 0)
{
prot_printf(popd_out, "+OK %s\r\n", "Begin TLS negotiation now");
/* must flush our buffers before starting tls */
prot_flush(popd_out);
}
result=tls_start_servertls(0, /* read */
1, /* write */
layerp,
&auth_id,
&tls_conn);
/* if error */
if (result==-1) {
if (pop3s == 0) {
- prot_printf(popd_out, "-ERR Starttls failed\r\n");
+ prot_printf(popd_out, "-ERR [SYS/PERM] Starttls failed\r\n");
syslog(LOG_NOTICE, "[pop3d] STARTTLS failed: %s", popd_clienthost);
} else {
syslog(LOG_NOTICE, "pop3s failed: %s", popd_clienthost);
fatal("tls_start_servertls() failed", EC_TEMPFAIL);
}
return;
}
/* tell SASL about the negotiated layer */
result = sasl_setprop(popd_saslconn, SASL_SSF_EXTERNAL, &ssf);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
saslprops.ssf = ssf;
result = sasl_setprop(popd_saslconn, SASL_AUTH_EXTERNAL, auth_id);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
if(saslprops.authid) {
free(saslprops.authid);
saslprops.authid = NULL;
}
if(auth_id)
saslprops.authid = xstrdup(auth_id);
/* tell the prot layer about our new layers */
prot_settls(popd_in, tls_conn);
prot_settls(popd_out, tls_conn);
popd_starttls_done = 1;
}
#else
static void cmd_starttls(int pop3s)
{
fatal("cmd_starttls() called, but no OpenSSL", EC_SOFTWARE);
}
#endif /* HAVE_SSL */
static int apop_enabled(void)
{
/* Check if pseudo APOP mechanism is enabled (challenge == NULL) */
if (sasl_checkapop(popd_saslconn, NULL, 0, NULL, 0) != SASL_OK) return 0;
/* Check if we have a challenge string */
if (!*popd_apop_chal) return 0;
return 1;
}
static void cmd_apop(char *response)
{
int fd;
struct protstream *shutdown_in;
char buf[1024];
char *p;
char shutdownfilename[1024];
int sasl_result;
char *canon_user;
assert(response != NULL);
if (popd_userid) {
- prot_printf(popd_out, "-ERR Must give PASS command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give PASS command\r\n");
return;
}
/* Check if it is enabled (challenge == NULL) */
if(sasl_checkapop(popd_saslconn, NULL, 0, NULL, 0) != SASL_OK)
fatal("cmd_apop called without working sasl_checkapop", EC_SOFTWARE);
sprintf(shutdownfilename, "%s/msg/shutdown", config_dir);
if ((fd = open(shutdownfilename, O_RDONLY, 0)) != -1) {
shutdown_in = prot_new(fd, 0);
prot_fgets(buf, sizeof(buf), shutdown_in);
if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
if ((p = strchr(buf, '\n'))!=NULL) *p = 0;
for(p = buf; *p == '['; p++); /* can't have [ be first char */
- prot_printf(popd_out, "-ERR %s\r\n", p);
+ prot_printf(popd_out, "-ERR [SYS/TEMP] %s\r\n", p);
prot_flush(popd_out);
shut_down(0);
}
sasl_result = sasl_checkapop(popd_saslconn,
popd_apop_chal,
strlen(popd_apop_chal),
response,
strlen(response));
/* failed authentication */
if (sasl_result != SASL_OK)
{
sleep(3);
- prot_printf(popd_out, "-ERR authenticating: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] authenticating: %s\r\n",
sasl_errstring(sasl_result, NULL, NULL));
syslog(LOG_NOTICE, "badlogin: %s APOP (%s) %s",
popd_clienthost, popd_apop_chal,
sasl_errdetail(popd_saslconn));
return;
}
/* successful authentication */
/*
* get the userid from SASL --- already canonicalized from
* mysasl_authproc()
*/
sasl_result = sasl_getprop(popd_saslconn, SASL_USERNAME,
(const void **) &canon_user);
popd_userid = xstrdup(canon_user);
if (sasl_result != SASL_OK) {
prot_printf(popd_out,
- "-ERR weird SASL error %d getting SASL_USERNAME\r\n",
+ "-ERR [AUTH] weird SASL error %d getting SASL_USERNAME\r\n",
sasl_result);
return;
}
syslog(LOG_NOTICE, "login: %s %s APOP %s",
popd_clienthost, popd_userid, "User logged in");
openproxy();
popd_auth_done = 1;
}
void
cmd_user(user)
char *user;
{
char *p;
/* possibly disallow USER */
if (!(kflag || popd_starttls_done ||
config_getswitch("allowplaintext", 1))) {
prot_printf(popd_out,
- "-ERR USER command only available under a layer\r\n");
+ "-ERR [AUTH] USER command only available under a layer\r\n");
return;
}
if (popd_userid) {
- prot_printf(popd_out, "-ERR Must give PASS command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give PASS command\r\n");
return;
}
shutdown_file(); /* check for shutdown file */
if (!(p = auth_canonifyid(user,0)) ||
/* '.' isn't allowed if '.' is the hierarchy separator */
(popd_namespace.hier_sep == '.' && strchr(p, '.')) ||
strlen(p) + 6 > MAX_MAILBOX_PATH) {
- prot_printf(popd_out, "-ERR Invalid user\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid user\r\n");
syslog(LOG_NOTICE,
"badlogin: %s plaintext %s invalid user",
popd_clienthost, beautify_string(user));
}
else {
popd_userid = xstrdup(p);
prot_printf(popd_out, "+OK Name is a valid mailbox\r\n");
}
}
void cmd_pass(char *pass)
{
char *reply = 0;
int plaintextloginpause;
if (!popd_userid) {
- prot_printf(popd_out, "-ERR Must give USER command\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Must give USER command\r\n");
return;
}
#ifdef HAVE_KRB
if (kflag) {
if (strcmp(popd_userid, kdata.pname) != 0 ||
kdata.pinst[0] ||
strcmp(klrealm, kdata.prealm) != 0) {
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
syslog(LOG_NOTICE,
"badlogin: %s kpop %s %s%s%s@%s access denied",
popd_clienthost, popd_userid,
kdata.pname, kdata.pinst[0] ? "." : "",
kdata.pinst, kdata.prealm);
return;
}
openproxy();
syslog(LOG_NOTICE, "login: %s %s kpop", popd_clienthost, popd_userid);
popd_auth_done = 1;
return;
}
#endif
if (!strcmp(popd_userid, "anonymous")) {
if (config_getswitch("allowanonymouslogin", 0)) {
pass = beautify_string(pass);
if (strlen(pass) > 500) pass[500] = '\0';
syslog(LOG_NOTICE, "login: %s anonymous %s",
popd_clienthost, pass);
}
else {
syslog(LOG_NOTICE, "badlogin: %s anonymous login refused",
popd_clienthost);
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
return;
}
}
else if (sasl_checkpass(popd_saslconn,
popd_userid,
strlen(popd_userid),
pass,
strlen(pass))!=SASL_OK) {
if (reply) {
syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
popd_clienthost, popd_userid, reply);
}
sleep(3);
- prot_printf(popd_out, "-ERR Invalid login\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid login\r\n");
free(popd_userid);
popd_userid = 0;
return;
}
else {
syslog(LOG_NOTICE, "login: %s %s plaintext %s",
popd_clienthost, popd_userid, reply ? reply : "");
plaintextloginpause = config_getint("plaintextloginpause", 0);
if (plaintextloginpause) sleep(plaintextloginpause);
}
openproxy();
popd_auth_done = 1;
}
/* Handle the POP3 Extension extension.
*/
void
cmd_capa()
{
int minpoll = config_getint("popminpoll", 0) * 60;
int expire = config_getint("popexpiretime", -1);
unsigned mechcount;
const char *mechlist;
prot_printf(popd_out, "+OK List of capabilities follows\r\n");
/* SASL special case: print SASL, then a list of supported capabilities */
if (sasl_listmech(popd_saslconn,
NULL, /* should be id string */
"SASL ", " ", "\r\n",
&mechlist,
NULL, &mechcount) == SASL_OK && mechcount > 0) {
prot_write(popd_out, mechlist, strlen(mechlist));
}
if (tls_enabled("pop3")) {
prot_printf(popd_out, "STLS\r\n");
}
if (expire < 0) {
prot_printf(popd_out, "EXPIRE NEVER\r\n");
} else {
prot_printf(popd_out, "EXPIRE %d\r\n", expire);
}
prot_printf(popd_out, "LOGIN-DELAY %d\r\n", minpoll);
prot_printf(popd_out, "TOP\r\n");
prot_printf(popd_out, "UIDL\r\n");
prot_printf(popd_out, "PIPELINING\r\n");
+ prot_printf(popd_out, "RESP-CODES\r\n");
+ prot_printf(popd_out, "AUTH-RESP-CODE\r\n");
if (kflag || popd_starttls_done || config_getswitch("allowplaintext", 1)) {
prot_printf(popd_out, "USER\r\n");
}
prot_printf(popd_out,
"IMPLEMENTATION Cyrus POP3 proxy server %s\r\n",
CYRUS_VERSION);
prot_printf(popd_out, ".\r\n");
prot_flush(popd_out);
}
/* according to RFC 2449, since we advertise the "SASL" capability, we
* must accept an optional second argument of the initial client
* response (base64 encoded!).
*/
void cmd_auth(char *arg)
{
int sasl_result;
static struct buf clientin;
unsigned clientinlen=0;
char *authtype;
const char *serverout;
unsigned int serveroutlen;
char *cin;
/* if client didn't specify an argument we give them the list */
if (!arg) {
const char *sasllist;
unsigned int mechnum;
prot_printf(popd_out, "+OK List of supported mechanisms follows\r\n");
/* CRLF seperated, dot terminated */
if (sasl_listmech(popd_saslconn, NULL,
"", "\r\n", "\r\n",
&sasllist,
NULL, &mechnum) == SASL_OK) {
if (mechnum>0) {
prot_printf(popd_out,"%s",sasllist);
}
}
prot_printf(popd_out, ".\r\n");
return;
}
authtype = arg;
while (*arg && !isspace((int) *arg)) {
arg++;
}
if (isspace((int) *arg)) {
/* null terminate authtype, get argument */
*arg++ = '\0';
} else {
/* no optional client response */
arg = NULL;
}
/* if arg != NULL, it's an initial client response */
if (arg) {
int arglen = strlen(arg);
clientin.alloc = arglen + 1;
cin = clientin.s = xmalloc(clientin.alloc);
sasl_result = sasl_decode64(arg, arglen, clientin.s,
clientin.alloc, &clientinlen);
} else {
sasl_result = SASL_OK;
cin = NULL;
clientinlen = 0;
}
/* server did specify a command, so let's try to authenticate */
if (sasl_result == SASL_OK || sasl_result == SASL_CONTINUE)
sasl_result = sasl_server_start(popd_saslconn, authtype,
cin, clientinlen,
&serverout, &serveroutlen);
/* sasl_server_start will return SASL_OK or SASL_CONTINUE on success */
while (sasl_result == SASL_CONTINUE)
{
char c;
/* print the message to the user */
printauthready(popd_out, serveroutlen, (unsigned char *)serverout);
c = prot_getc(popd_in);
if(c == '*') {
eatline(popd_in,c);
prot_printf(popd_out,
- "-ERR Client canceled authentication\r\n");
+ "-ERR [AUTH] Client canceled authentication\r\n");
reset_saslconn(&popd_saslconn);
return;
} else {
prot_ungetc(c, popd_in);
}
/* get string from user */
clientinlen = getbase64string(popd_in, &clientin);
if (clientinlen == -1) {
- prot_printf(popd_out, "-ERR Invalid base64 string\r\n");
+ prot_printf(popd_out, "-ERR [AUTH] Invalid base64 string\r\n");
return;
}
sasl_result = sasl_server_step(popd_saslconn,
clientin.s,
clientinlen,
&serverout, &serveroutlen);
syslog(LOG_NOTICE, "pop3d step - through it again");
}
syslog(LOG_NOTICE, "pop3d step- done");
/* failed authentication */
if (sasl_result != SASL_OK)
{
sleep(3);
reset_saslconn(&popd_saslconn);
/* convert the sasl error code to a string */
- prot_printf(popd_out, "-ERR authenticating: %s\r\n",
+ prot_printf(popd_out, "-ERR [AUTH] authenticating: %s\r\n",
sasl_errstring(sasl_result, NULL, NULL));
if (authtype) {
syslog(LOG_NOTICE, "badlogin: %s %s %s",
popd_clienthost, authtype, sasl_errdetail(popd_saslconn));
} else {
syslog(LOG_NOTICE, "badlogin: %s %s",
popd_clienthost, sasl_errdetail(popd_saslconn));
}
return;
}
/* successful authentication */
/* get the userid from SASL --- already canonicalized from
* mysasl_authproc()
*/
/* FIXME XXX: popd_userid is NOT CONST */
sasl_result = sasl_getprop(popd_saslconn, SASL_USERNAME,
(const void **) &popd_userid);
if (sasl_result != SASL_OK) {
prot_printf(popd_out,
- "-ERR weird SASL error %d getting SASL_USERNAME\r\n",
+ "-ERR [AUTH] weird SASL error %d getting SASL_USERNAME\r\n",
sasl_result);
return;
}
proc_register("pop3d", popd_clienthost, popd_userid, NULL);
syslog(LOG_NOTICE, "login: %s %s %s %s", popd_clienthost, popd_userid,
authtype, "User logged in");
prot_setsasl(popd_in, popd_saslconn);
prot_setsasl(popd_out, popd_saslconn);
openproxy();
popd_auth_done = 1;
}
static int mysasl_getauthline(struct protstream *p, char **line,
unsigned int *linelen)
{
char buf[2096];
char *str = (char *) buf;
if (!prot_fgets(str, sizeof(buf), p)) {
return SASL_FAIL;
}
if (!strncasecmp(str, "+OK", 3)) { return SASL_OK; }
if (!strncasecmp(str, "-ERR", 4)) { return SASL_BADAUTH; }
if (str[0] == '+' && str[1] == ' ') {
size_t len;
str += 2; /* jump past the "+ " */
len = strlen(str) + 1;
*line = xmalloc(strlen(str) + 1);
if (*str != '\r') { /* decode it */
int r;
r = sasl_decode64(str, strlen(str), *line, len, linelen);
if (r != SASL_OK) {
return r;
}
return SASL_CONTINUE;
} else { /* blank challenge */
*line = NULL;
*linelen = 0;
return SASL_CONTINUE;
}
} else {
/* huh??? */
return SASL_FAIL;
}
}
extern sasl_callback_t *mysasl_callbacks(const char *username,
const char *authname,
const char *realm,
const char *password);
extern void free_callbacks(sasl_callback_t *in);
static int proxy_authenticate(const char *hostname)
{
int r;
sasl_security_properties_t *secprops = NULL;
struct sockaddr_in saddr_l;
struct sockaddr_in saddr_r;
socklen_t addrsize;
sasl_callback_t *cb;
char buf[2048];
char optstr[128];
char *in, *p;
const char *out;
unsigned int inlen, outlen;
const char *mechusing;
unsigned b64len;
char localip[60], remoteip[60];
const char *pass;
strcpy(optstr, hostname);
p = strchr(optstr, '.');
if (p) *p = '\0';
strcat(optstr, "_password");
pass = config_getstring(optstr, NULL);
cb = mysasl_callbacks(popd_userid,
config_getstring("proxy_authname", "proxy"),
config_getstring("proxy_realm", NULL),
pass);
r = sasl_client_new("pop", hostname, NULL, NULL,
cb, 0, &backend_saslconn);
if (r != SASL_OK) {
return r;
}
secprops = mysasl_secprops(0);
r = sasl_setprop(backend_saslconn, SASL_SEC_PROPS, secprops);
if (r != SASL_OK) {
return r;
}
/* set the IP addresses */
addrsize=sizeof(struct sockaddr_in);
if (getpeername(backend_sock, (struct sockaddr *)&saddr_r, &addrsize) != 0)
return SASL_FAIL;
addrsize=sizeof(struct sockaddr_in);
if (getsockname(backend_sock, (struct sockaddr *)&saddr_l,&addrsize)!=0)
return SASL_FAIL;
if (iptostring((struct sockaddr *)&saddr_r,
sizeof(struct sockaddr_in), remoteip, 60) != 0)
return SASL_FAIL;
if (iptostring((struct sockaddr *)&saddr_l,
sizeof(struct sockaddr_in), localip, 60) != 0)
return SASL_FAIL;
r = sasl_setprop(popd_saslconn, SASL_IPLOCALPORT, localip);
if (r != SASL_OK) return r;
r = sasl_setprop(popd_saslconn, SASL_IPREMOTEPORT, remoteip);
if (r != SASL_OK) return r;
/* read the initial greeting */
if (!prot_fgets(buf, sizeof(buf), backend_in)) {
return SASL_FAIL;
}
strcpy(buf, hostname);
p = strchr(buf, '.');
*p = '\0';
strcat(buf, "_mechs");
/* we now do the actual SASL exchange */
r = sasl_client_start(backend_saslconn,
config_getstring(buf, "KERBEROS_V4"),
NULL, &out, &outlen, &mechusing);
if ((r != SASL_OK) && (r != SASL_CONTINUE)) {
return r;
}
if (out == NULL || outlen == 0) {
prot_printf(backend_out, "AUTH %s\r\n", mechusing);
} else {
/* send initial challenge */
r = sasl_encode64(out, outlen, buf, sizeof(buf), &b64len);
if (r != SASL_OK)
return r;
prot_printf(backend_out, "AUTH %s %s\r\n", mechusing, buf);
}
in = NULL;
inlen = 0;
r = mysasl_getauthline(backend_in, &in, &inlen);
while (r == SASL_CONTINUE) {
r = sasl_client_step(backend_saslconn, in, inlen, NULL, &out, &outlen);
if (in) {
free(in);
}
if (r != SASL_OK && r != SASL_CONTINUE) {
return r;
}
r = sasl_encode64(out, outlen, buf, sizeof(buf), &b64len);
if (r != SASL_OK) {
return r;
}
prot_write(backend_out, buf, b64len);
prot_printf(backend_out, "\r\n");
r = mysasl_getauthline(backend_in, &in, &inlen);
}
/* Done with callbacks */
free_callbacks(cb);
if (r == SASL_OK) {
prot_setsasl(backend_in, backend_saslconn);
prot_setsasl(backend_out, backend_saslconn);
}
/* r == SASL_OK on success */
return r;
}
static void openproxy(void)
{
struct hostent *hp;
struct sockaddr_in sin;
char inboxname[MAX_MAILBOX_PATH];
int r;
char *server;
/* have to figure out what server to connect to */
strcpy(inboxname, "user.");
strcat(inboxname, popd_userid);
/* Translate any separators in userid part of inboxname
(we need the original userid for AUTH to backend) */
mboxname_hiersep_tointernal(&popd_namespace, inboxname+5);
r = mboxlist_lookup(inboxname, &server, NULL, NULL);
if (!r) fatal("couldn't find backend server", EC_CONFIG);
hp = gethostbyname(server);
if (!hp) fatal("gethostbyname failed", EC_CONFIG);
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
sin.sin_port = htons(110);
if ((backend_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR, "socket() failed: %m");
fatal("socket failed", EC_CONFIG);
}
if (connect(backend_sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
syslog(LOG_ERR, "connect() failed: %m");
fatal("connect failed", 1);
}
backend_in = prot_new(backend_sock, 0);
backend_out = prot_new(backend_sock, 1);
prot_setflushonread(backend_in, backend_out);
if (proxy_authenticate(server) != SASL_OK) {
syslog(LOG_ERR, "couldn't authenticate to backend server", EC_CONFIG);
fatal("couldn't authenticate to backend server", 1);
}
prot_printf(popd_out, "+OK Maildrop locked and ready\r\n");
return;
}
/* we've authenticated the client, we've connected to the backend.
now it's all up to them */
static void bitpipe(void)
{
fd_set read_set, rset;
int nfds, r;
char buf[4096];
FD_ZERO(&read_set);
FD_SET(0, &read_set);
FD_SET(backend_sock, &read_set);
nfds = backend_sock + 1;
for (;;) {
rset = read_set;
r = select(nfds, &rset, NULL, NULL, NULL);
/* if select() failed it's not worth trying to figure anything out */
if (r < 0) goto done;
if (FD_ISSET(0, &rset)) {
do {
int c = prot_read(popd_in, buf, sizeof(buf));
if (c == 0 || c < 0) goto done;
prot_write(backend_out, buf, c);
} while (popd_in->cnt > 0);
prot_flush(backend_out);
}
if (FD_ISSET(backend_sock, &rset)) {
do {
int c = prot_read(backend_in, buf, sizeof(buf));
if (c == 0 || c < 0) goto done;
prot_write(popd_out, buf, c);
} while (backend_in->cnt > 0);
prot_flush(popd_out);
}
}
done:
/* ok, we're done. close backend connection */
prot_free(backend_in);
prot_free(backend_out);
close(backend_sock);
/* close the connection to the client */
close(0);
close(1);
prot_free(popd_in);
prot_free(popd_out);
return;
}
/* Reset the given sasl_conn_t to a sane state */
static int reset_saslconn(sasl_conn_t **conn)
{
int ret;
sasl_security_properties_t *secprops = NULL;
sasl_dispose(conn);
/* do initialization typical of service_main */
ret = sasl_server_new("pop", config_servername,
NULL, NULL, NULL,
NULL, 0, conn);
if(ret != SASL_OK) return ret;
if(saslprops.ipremoteport)
ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
saslprops.ipremoteport);
if(ret != SASL_OK) return ret;
if(saslprops.iplocalport)
ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
saslprops.iplocalport);
if(ret != SASL_OK) return ret;
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
if(ret != SASL_OK) return ret;
/* end of service_main initialization excepting SSF */
/* If we have TLS/SSL info, set it */
ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
saslprops.iplocalport);
if(ret != SASL_OK) return ret;
secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
if(ret != SASL_OK) return ret;
/* end of service_main initialization excepting SSF */
/* If we have TLS/SSL info, set it */
if(saslprops.ssf) {
ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
}
if(ret != SASL_OK) return ret;
if(saslprops.authid) {
ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
if(ret != SASL_OK) return ret;
}
/* End TLS/SSL Info */
return SASL_OK;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 4, 6:50 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822921
Default Alt Text
(82 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline