Commit 0b1e16be 0b1e16bea8f32524dd123cc267fe1839f9bc3b0f by Sergey Poznyakoff

pop3d: make tls more configurable

The "tls" statement can be used both within a server declaration
and in the global scope, the former overriding the latter.  Its
argument can be one of the following:

  no          TLS is not used.  The STLS command won't be available even
              if the tls configuration is otherwise complete.
  ondemand    TLS is initiated when the user issues the STLS command.
              This is the default when TLS is configured.
  required    Same as above, but the use of STLS is mandatory.  The
              authentication state is entered only after TLS negotiation
              has succeeded.
  connection  TLS is always forced when the connection is established (pops).

For compatibility with prior versions the tls-required statement is retained,
but is considered deprecated.  It is synonymous with "tls required".  The
"tls" statement allows for the following alias values:

  false, off, 0           same as  no
  stls                    same as  ondemand
  yes, true, on, 1        same as  connection

Internally, all handler functions receive a pointer to the POP session
structure, which configures the current session.  In particular, it
contains the TLS mode and capability list.

* pop3d/capa.c (pop3d_capa): Traverse the capability list,
outputting each of its elements.
(pop3d_session_init)
(pop3d_session_free): New functions.
* pop3d/logindelay.c (login_delay_capa): Change signature.
* pop3d/pop3d.c (tls_required): Remove.
(tls_mode): New variable.
(pop3d_srv_config) <tls>: Remove.
(pop3d_srv_config) <tls_mode>: New member.
(pop3d_srv_param): Change definition of the "tls" statement.
(pop3d_cfg_param): New statement "tls". Mark "tls-required"
as deprecated.
(pop3d_mainloop): Change type of the tls argument.
Initialize session and pass it to each handler.
(pop3d_connection): Decide whether and how to use TLS
using global and per-session settings, the latter
overriding the former.
* pop3d/pop3d.h (login_delay_capa): Change signature.
(tls_mode): New enum.
(pop3d_capa_type): New enum.
(pop3d_capa, pop3d_session): New structs.
(pop3d_session_init)
(pop3d_session_free): New protos.
(pop3d_command_handler_t): Change signature. All handlers and their
uses are changed accordingly.
* pop3d/stls.c (pop3d_stls): Return error if session does not
allow tls.
1 parent 98f7cc4c
......@@ -164,7 +164,7 @@ pop3d_apopuser (const char *user)
}
int
pop3d_apop (char *arg)
pop3d_apop (char *arg, struct pop3d_session *sess)
{
char *p, *password, *user_digest, *user;
struct mu_md5_ctx md5context;
......
......@@ -19,7 +19,7 @@
/* AUTH is not yet implemented */
int
pop3d_auth (char *arg MU_ARG_UNUSED)
pop3d_auth (char *arg MU_ARG_UNUSED, struct pop3d_session *sess MU_ARG_UNUSED)
{
if (state != AUTHORIZATION)
return ERR_WRONG_STATE;
......
......@@ -26,38 +26,134 @@
Capabilities available in the AUTHORIZATION state MUST be announced
in both states. */
static int
print_capa (void *item, void *data)
{
struct pop3d_capa *cp = item;
struct pop3d_session *session = data;
if (cp->type == capa_func)
{
cp->value.func (cp->name, session);
}
else
{
pop3d_outf ("%s", cp->name);
if (cp->value.string)
pop3d_outf ("%s", cp->value.string);
pop3d_outf ("\n");
}
return 0;
}
int
pop3d_capa (char *arg)
pop3d_capa (char *arg, struct pop3d_session *sess)
{
if (strlen (arg) != 0)
return ERR_BAD_ARGS;
pop3d_outf ("+OK Capability list follows\n");
pop3d_outf ("TOP\n");
pop3d_outf ("USER\n");
pop3d_outf ("UIDL\n");
pop3d_outf ("RESP-CODES\n");
pop3d_outf ("PIPELINING\n");
if (pop3d_xlines)
pop3d_outf ("XLINES\n");
mu_list_foreach (sess->capa, print_capa, sess);
pop3d_outf (".\n");
return OK;
}
static void
pop3d_append_capa_string (struct pop3d_session *sess, const char *name,
const char *value)
{
struct pop3d_capa *cp;
cp = mu_alloc (sizeof (*cp));
cp->type = capa_string;
cp->name = name;
cp->value.string = value ? mu_strdup (value) : NULL;
if (mu_list_append (sess->capa, cp))
mu_alloc_die ();
}
static void
pop3d_append_capa_func (struct pop3d_session *sess, const char *name,
void (*func) (const char *, struct pop3d_session *))
{
struct pop3d_capa *cp;
if (!func)
return;
cp = mu_alloc (sizeof (*cp));
cp->type = capa_func;
cp->name = name;
cp->value.func = func;
if (mu_list_append (sess->capa, cp))
mu_alloc_die ();
}
static void
capa_free (void *p)
{
struct pop3d_capa *cp = p;
if (cp->type == capa_string && cp->value.string)
free (cp->value.string);
free (cp);
}
static void
capa_implementation (const char *name, struct pop3d_session *session)
{
if (state == TRANSACTION) /* let's not advertise to just anyone */
pop3d_outf ("%s %s\n", name, PACKAGE_STRING);
}
#ifdef WITH_TLS
if (tls_available && tls_done == 0)
pop3d_outf ("STLS\n");
static void
capa_stls (const char *name, struct pop3d_session *session)
{
if ((session->tls == tls_ondemand || session->tls == tls_required)
&& tls_available && tls_done == 0)
pop3d_outf ("%s\n", name);
}
#else
# define capa_stls NULL
#endif /* WITH_TLS */
login_delay_capa ();
/* This can be implemented by setting an header field on the message. */
if (expire == EXPIRE_NEVER)
pop3d_outf ("EXPIRE NEVER\n");
else
pop3d_outf ("EXPIRE %s\n", mu_umaxtostr (0, expire));
static void
capa_user (const char *name, struct pop3d_session *session)
{
if (state == INITIAL)
pop3d_outf ("XTLSREQUIRED\n");
else
pop3d_outf ("USER\n");
}
if (state == TRANSACTION) /* let's not advertise to just anyone */
pop3d_outf ("IMPLEMENTATION %s\n", PACKAGE_STRING);
pop3d_outf (".\n");
return OK;
void
pop3d_session_init (struct pop3d_session *session)
{
if (mu_list_create (&session->capa))
mu_alloc_die ();
mu_list_set_destroy_item (session->capa, capa_free);
/* Initialize the capability list */
pop3d_append_capa_string (session, "TOP", NULL);
pop3d_append_capa_string (session, "UIDL", NULL);
pop3d_append_capa_string (session, "RESP-CODES", NULL);
pop3d_append_capa_string (session, "PIPELINING", NULL);
if (pop3d_xlines)
pop3d_append_capa_string (session, "XLINES", NULL);
pop3d_append_capa_func (session, "LOGIN-DELAY", login_delay_capa);
/* This can be implemented by setting an header field on the message. */
pop3d_append_capa_string (session, "EXPIRE",
(expire == EXPIRE_NEVER) ?
"NEVER" : mu_umaxtostr (0, expire));
pop3d_append_capa_func (session, NULL, capa_user);
pop3d_append_capa_func (session, "STLS", capa_stls);
pop3d_append_capa_func (session, "IMPLEMENTATION", capa_implementation);
}
void
pop3d_session_free (struct pop3d_session *session)
{
mu_list_destroy (&session->capa);
}
......
......@@ -20,7 +20,7 @@
/* DELE adds a message number to the list of messages to be deleted on QUIT */
int
pop3d_dele (char *arg)
pop3d_dele (char *arg, struct pop3d_session *sess)
{
size_t num;
mu_message_t msg;
......
......@@ -39,7 +39,7 @@
XLINES capability.
*/
int
pop3d_list (char *arg)
pop3d_list (char *arg, struct pop3d_session *sess)
{
size_t mesgno;
mu_message_t msg = NULL;
......
......@@ -141,13 +141,13 @@ update_login_delay (char *username)
}
void
login_delay_capa ()
login_delay_capa (const char *name, struct pop3d_session *session)
{
mu_dbm_file_t db;
if (login_delay && (db = open_stat_db (MU_STREAM_RDWR)) != NULL)
{
pop3d_outf ("LOGIN-DELAY %s\n", mu_umaxtostr (0, login_delay));
pop3d_outf ("%lu\n", (unsigned long) login_delay);
mu_dbm_destroy (&db);
}
}
......
......@@ -20,7 +20,7 @@
/* Does nothing */
int
pop3d_noop (char *arg)
pop3d_noop (char *arg, struct pop3d_session *sess)
{
if (strlen (arg) != 0)
return ERR_BAD_ARGS;
......
......@@ -18,6 +18,7 @@
#include "mailutils/pam.h"
#include "mailutils/libargp.h"
#include "mailutils/pop3.h"
#include "mailutils/kwd.h"
#include "tcpwrap.h"
mu_mailbox_t mbox;
......@@ -29,13 +30,14 @@ mu_m_server_t server;
unsigned int idle_timeout;
int pop3d_transcript;
int debug_mode;
int tls_required;
int pop3d_xlines;
char *apop_database_name = APOP_PASSFILE;
int apop_database_safety = MU_FILE_SAFETY_ALL;
uid_t apop_database_owner;
int apop_database_owner_set;
enum tls_mode tls_mode;
#ifdef WITH_TLS
int tls_available;
int tls_done;
......@@ -137,13 +139,61 @@ cb_bulletin_db (void *data, mu_config_value_t *val)
struct pop3d_srv_config
{
struct mu_srv_config m_cfg;
int tls;
enum tls_mode tls_mode;
};
#ifdef WITH_TLS
static int
cb_tls (void *data, mu_config_value_t *val)
{
int *res = data;
static struct mu_kwd tls_kwd[] = {
{ "no", tls_no },
{ "false", tls_no },
{ "off", tls_no },
{ "0", tls_no },
{ "ondemand", tls_ondemand },
{ "stls", tls_ondemand },
{ "required", tls_required },
{ "connection", tls_connection },
/* For compatibility with prior versions: */
{ "yes", tls_connection },
{ "true", tls_connection },
{ "on", tls_connection },
{ "1", tls_connection },
{ NULL }
};
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_kwd_xlat_name (tls_kwd, val->v.string, res))
mu_error (_("not a valid tls keyword: %s"), val->v.string);
return 0;
}
#endif
static int
cb_tls_required (void *data, mu_config_value_t *val)
{
int *res = data;
int bv;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_cfg_parse_boolean (val->v.string, &bv))
mu_error (_("Not a boolean value"));
else
*res = tls_required;
return 0;
}
static struct mu_cfg_param pop3d_srv_param[] = {
{ "tls", mu_cfg_bool, NULL, mu_offsetof (struct pop3d_srv_config, tls), NULL,
N_("Use TLS encryption for this server")
},
#ifdef WITH_TLS
{ "tls", mu_cfg_callback,
NULL, mu_offsetof (struct pop3d_srv_config, tls_mode), cb_tls,
N_("Kind of TLS encryption to use for this server") },
#endif
{ NULL }
};
......@@ -178,8 +228,11 @@ static struct mu_cfg_param pop3d_cfg_param[] = {
N_("arg: list") },
#ifdef WITH_TLS
{ "tls-required", mu_cfg_bool, &tls_required, 0, NULL,
N_("Always require STLS before entering authentication phase.") },
{ "tls", mu_cfg_callback, &tls_mode, 0, cb_tls,
N_("Kind of TLS encryption to use") },
{ "tls-required", mu_cfg_callback, &tls_mode, 0, cb_tls_required,
N_("Always require STLS before entering authentication phase.\n"
"Deprecated, use \"tls required\" instead.") },
#endif
#ifdef ENABLE_LOGIN_DELAY
{ "login-delay", mu_cfg_time, &login_delay, 0, NULL,
......@@ -292,18 +345,35 @@ pop3d_get_client_address (int fd, struct sockaddr_in *pcs)
ofd -- output descriptor
tls -- initiate encrypted connection */
int
pop3d_mainloop (int ifd, int ofd, int tls)
pop3d_mainloop (int ifd, int ofd, enum tls_mode tls)
{
int status = OK;
char buffer[512];
static int sigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP, SIGPIPE,
SIGABRT, SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGALRM };
struct pop3d_session session;
mu_set_signals (pop3d_child_signal, sigtab, MU_ARRAY_SIZE (sigtab));
pop3d_setio (ifd, ofd, tls);
if (tls == tls_unspecified)
tls = tls_available ? tls_ondemand : tls_no;
else if (tls != tls_no && !tls_available)
{
mu_error (_("TLS is not configured, but requested in the "
"configuration"));
tls = tls_no;
}
pop3d_setio (ifd, ofd, tls == tls_connection);
if (tls == tls_required)
initial_state = INITIAL;
state = tls == tls_connection ? AUTHORIZATION : initial_state;
state = tls ? AUTHORIZATION : initial_state;
pop3d_session_init (&session);
session.tls = tls;
/* FIXME: state should also be in the session? */
/* Prepare the shared secret for APOP. */
{
......@@ -353,7 +423,7 @@ pop3d_mainloop (int ifd, int ofd, int tls)
manlock_touchlock (mbox);
if ((handler = pop3d_find_command (cmd)) != NULL)
status = handler (arg);
status = handler (arg, &session);
else
status = ERR_BAD_CMD;
......@@ -361,6 +431,8 @@ pop3d_mainloop (int ifd, int ofd, int tls)
pop3d_outf ("-ERR %s\n", pop3d_error_string (status));
}
pop3d_session_free (&session);
pop3d_bye ();
return status;
......@@ -446,7 +518,8 @@ pop3d_connection (int fd, struct sockaddr *sa, int salen,
else
rc = 1;
pop3d_mainloop (fd, fd, cfg->tls);
pop3d_mainloop (fd, fd,
cfg->tls_mode == tls_unspecified ? tls_mode : cfg->tls_mode);
if (rc == 0)
clr_strerr_flt ();
......@@ -517,9 +590,6 @@ main (int argc, char **argv)
argc, argv, 0, NULL, server))
exit (EX_CONFIG); /* FIXME: No way to discern from EX_USAGE? */
if (tls_required)
initial_state = INITIAL;
if (expire == 0)
expire_on_exit = 1;
......@@ -589,7 +659,7 @@ main (int argc, char **argv)
{
/* Make sure we are in the root directory. */
chdir ("/");
status = pop3d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, 0);
status = pop3d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, tls_no);
}
if (status)
......
......@@ -42,17 +42,19 @@
# undef ENABLE_LOGIN_DELAY
#endif
struct pop3d_session;
#ifdef ENABLE_LOGIN_DELAY
# define LOGIN_STAT_FILE "/var/run/pop3-login"
extern time_t login_delay;
extern char *login_stat_file;
extern int check_login_delay (char *username);
extern void update_login_delay (char *username);
extern void login_delay_capa (void);
extern void login_delay_capa (const char *, struct pop3d_session *);
#else
# define check_login_delay(u) 0
# define update_login_delay(u)
# define login_delay_capa()
# define login_delay_capa NULL
#endif
/* Minimum advertise retention time for messages. */
......@@ -166,8 +168,44 @@ extern int expire_on_exit;
#define ERR_LOGIN_DELAY 22
#define ERR_TERMINATE 23
enum tls_mode
{
tls_unspecified,
tls_no,
tls_ondemand,
tls_required,
tls_connection
};
enum pop3d_capa_type
{
capa_string,
capa_func
};
struct pop3d_capa
{
enum pop3d_capa_type type;
const char *name;
union
{
char *string;
void (*func) (const char *, struct pop3d_session *);
} value;
};
struct pop3d_session
{
mu_list_t capa;
enum tls_mode tls;
};
void pop3d_session_init (struct pop3d_session *session);
void pop3d_session_free (struct pop3d_session *session);
typedef struct mu_pop_server *mu_pop_server_t;
typedef int (*pop3d_command_handler_t) (char *);
typedef int (*pop3d_command_handler_t) (char *, struct pop3d_session *sess);
struct pop3d_command
{
......@@ -214,19 +252,19 @@ extern int apop_database_owner_set;
extern pop3d_command_handler_t pop3d_find_command (const char *name);
extern int pop3d_stat (char *);
extern int pop3d_top (char *);
extern int pop3d_uidl (char *);
extern int pop3d_user (char *);
extern int pop3d_apop (char *);
extern int pop3d_auth (char *);
extern int pop3d_capa (char *);
extern int pop3d_dele (char *);
extern int pop3d_list (char *);
extern int pop3d_noop (char *);
extern int pop3d_quit (char *);
extern int pop3d_retr (char *);
extern int pop3d_rset (char *);
extern int pop3d_stat (char *, struct pop3d_session *);
extern int pop3d_top (char *, struct pop3d_session *);
extern int pop3d_uidl (char *, struct pop3d_session *);
extern int pop3d_user (char *, struct pop3d_session *);
extern int pop3d_apop (char *, struct pop3d_session *);
extern int pop3d_auth (char *, struct pop3d_session *);
extern int pop3d_capa (char *, struct pop3d_session *);
extern int pop3d_dele (char *, struct pop3d_session *);
extern int pop3d_list (char *, struct pop3d_session *);
extern int pop3d_noop (char *, struct pop3d_session *);
extern int pop3d_quit (char *, struct pop3d_session *);
extern int pop3d_retr (char *, struct pop3d_session *);
extern int pop3d_rset (char *, struct pop3d_session *);
void pop3d_send_payload (mu_stream_t stream, mu_stream_t linestr,
size_t maxlines);
......@@ -242,7 +280,7 @@ extern RETSIGTYPE pop3d_master_signal (int);
extern RETSIGTYPE pop3d_child_signal (int);
#ifdef WITH_TLS
extern int pop3d_stls (char *);
extern int pop3d_stls (char *, struct pop3d_session *);
extern void enable_stls (void);
#endif /* WITH_TLS */
extern void pop3d_outf (const char *fmt, ...) MU_PRINTFLIKE(1,2);
......
......@@ -26,7 +26,7 @@
static void pop3d_fix_mark ();
int
pop3d_quit (char *arg)
pop3d_quit (char *arg, struct pop3d_session *sess)
{
int err = OK;
if (strlen (arg) != 0)
......
......@@ -64,7 +64,7 @@ pop3d_send_payload (mu_stream_t stream, mu_stream_t linestr, size_t maxlines)
/* Prints out the specified message */
int
pop3d_retr (char *arg)
pop3d_retr (char *arg, struct pop3d_session *sess)
{
size_t mesgno;
mu_message_t msg = NULL;
......
......@@ -20,7 +20,7 @@
/* Resets the connection so that no messages are marked as deleted */
int
pop3d_rset (char *arg)
pop3d_rset (char *arg, struct pop3d_session *sess)
{
size_t i;
size_t total = 0;
......
......@@ -20,7 +20,7 @@
/* Prints the number of messages and the total size of all messages */
int
pop3d_stat (char *arg)
pop3d_stat (char *arg, struct pop3d_session *sess)
{
size_t mesgno;
size_t size = 0;
......
......@@ -21,7 +21,7 @@
#ifdef WITH_TLS
int
pop3d_stls (char *arg)
pop3d_stls (char *arg, struct pop3d_session *session)
{
if (strlen (arg) != 0)
return ERR_BAD_ARGS;
......@@ -29,6 +29,15 @@ pop3d_stls (char *arg)
if (state != initial_state)
return ERR_WRONG_STATE;
switch (session->tls)
{
case tls_ondemand:
case tls_required:
break;
default:
return ERR_WRONG_STATE;
}
if (tls_done)
return ERR_TLS_ACTIVE;
......@@ -45,7 +54,7 @@ pop3d_stls (char *arg)
}
state = AUTHORIZATION; /* Confirm we're in this state. Necessary for
--tls-required to work */
"tls required" to work */
return OK;
}
......
......@@ -20,7 +20,7 @@
/* Prints the header of a message plus a specified number of lines. */
int
pop3d_top (char *arg)
pop3d_top (char *arg, struct pop3d_session *sess)
{
size_t mesgno;
unsigned long lines;
......
......@@ -18,7 +18,7 @@
#include "pop3d.h"
int
pop3d_uidl (char *arg)
pop3d_uidl (char *arg, struct pop3d_session *sess)
{
size_t mesgno;
char uidl[128];
......
......@@ -72,7 +72,7 @@ pop3d_begin_session ()
}
int
pop3d_user (char *arg)
pop3d_user (char *arg, struct pop3d_session *sess)
{
char *buf, *pass, *cmd;
char buffer[512];
......@@ -129,7 +129,7 @@ pop3d_user (char *arg)
else if (mu_c_strcasecmp (cmd, "QUIT") == 0)
{
mu_diag_output (MU_DIAG_INFO, _("possible probe of account `%s'"), arg);
return pop3d_quit (pass);
return pop3d_quit (pass, sess);
}
else
{
......