Commit afda9ba4 afda9ba46c7eb2c061f3907dc7e80ae1ba38157b by Sergey Poznyakoff

Rewrite TLS support

The new implementation allows for per-server certificates.

* libmu_auth/Makefile.am: Build tls support depending on the value of
MU_COND_GNUTLS.
* libmu_auth/notls.c: New file.
* libmu_auth/tls.c: Rewrite.
* libmu_auth/tlsiostr.c: New file.
* libmu_auth/tlsvar.c: New file.
* libmu_auth/tlsconf.c: New file.

* include/mailutils/sys/tls-stream.h (_mu_tls_stream): New members:
session_type, conf, cred.
(mu_tls_io_stream_create): New proto.
* include/mailutils/tls.h (mu_tls_module_config): Remove definition.
(mu_tls_config): New structure.
(mu_tls_server_stream_create): Remove proto.
(mu_tls_cert_file_checks)
(mu_tls_key_file_checksr)
(mu_tls_ca_file_checks): New globals
(MU_TLS_CERT_FILE_CHECKS)
(MU_TLS_KEY_FILE_CHECKS)
(MU_TLS_CA_FILE_CHECKS): New defines.
(mu_tls_stream_create): New proto.
(mu_tls_config_status): New constants.
(mu_tls_check_config): Remove.
(mu_tls_config_check): New function.

* include/mailutils/server.h (mu_m_server_preflight_fp): New typedef.
(mu_m_server_set_preflight): New proto.

* libmailutils/diag/debcat (tls): New category.
* libmailutils/server/ipsrv.c (mu_ip_server_get_data): New function.
* libmailutils/server/msrv.c (_mu_m_server) <preflight>: New method.
(mu_m_server_set_preflight): New function.
(mu_m_server_destroy): Destroy the srvlist.
(open_connection): New function.
(mu_m_server_run): Remove from srvlist only those servers that failed
to open.  Run preflight check, if registered.

* include/mailutils/cfg.h (mu_cfg_section): New member: data.
* libmailutils/cfg/driver.c (dup_container): Copy the "data" pointer.
(mu_cfg_section_add_params): Preserve original pointer and offset when
necessary.
* libmailutils/cfg/parser.y (_scan_tree_helper): Use the section data
pointer (if set) as data target.

* configure.ac (MU_COND_GNUTLS): New conditional.
* imap4d/commands.c: Remove #ifdef WITH_TLS preprocessor conditional.
* imap4d/imap4d.c (tls_mode): Remove variable.
(imap4d_srv_config): Move definition to the header file.
Remove #ifdef WITH_TLS preprocessor conditionals.
Rename the tls configuration statement to tls-mode.
Add new subsection .server.tls;
Remove the legacy tls-required configuration statement.
(imap4d_mainloop): Change signature: take a pointer to the
struct imap4d_srv_config as the 3rd argument,
Use the cfg->tls_mode member to decide on TLS state.
(main): Call mu_tls_cfg_init.
Install server preflight checker.
* imap4d/imap4d.h: Remove #ifdef WITH_TLS preprocessor conditionals.
(imap4d_srv_config): Moved from imap4d.c
New member: tls_conf
(imap4d_session): New member: tls_conf
(global_tls_conf): New global.
(io_setio, imap4d_init_tls_server): Change prototypes.
* imap4d/io.c (io_setio): Change signature: take a pointer to the
struct mu_tls_config as the 3rd argument.
Rewrite TLS support.
(imap4d_init_tls_server): Take a pointer to the
struct mu_tls_config.
* imap4d/starttls.c (tls_available, tls_done): Remove globals.
(global_tls_conf): New global.
(imap4d_starttls): Keep TLS state in the session.
(tls_encryption_on): Likewise.
(starttls_init): Rewrite as a mserver preflight check function.

* pop3d/capa.c: Remove #ifdef WITH_TLS preprocessor conditional.
(capa_stls): Rewrite.
* pop3d/cmd.c (global_tls_conf): New global.
(stls_preflight): New function.
(pop3d_error_string): Rewrite using char ** array.
* pop3d/extra.c (pop3d_setio): Take a pointer to struct mu_tls_config
as the 3rd argument. Decide on TLS using it.
(pop3d_init_tls_server): Take a pointer to struct mu_tls_config.
* pop3d/pop3d.c: Rename the tls configuration statement to tls-mode.
Add new subsection .server.tls;
Remove the legacy tls-required configuration statement.
(pop3d_mainloop) Take a pointer to struct mu_tls_config
as the 3rd argument. Decide on TLS using it.
(main): Call mu_tls_cfg_init. Install server preflight check.
* pop3d/pop3d.h (pop3d_session): New member tls_conf.
(pop3d_srv_config): New definition.
(global_tls_conf): New extern.
* pop3d/stls.c: Rewrite TLS support.

* NEWS: Document new features.
* configure.ac: Version 3.2.90.
* doc/texinfo/programs.texi: Update.
1 parent 0c8e5985
GNU mailutils NEWS -- history of user-visible changes. 2017-03-11
GNU mailutils NEWS -- history of user-visible changes. 2017-03-18
Copyright (C) 2002-2017 Free Software Foundation, Inc.
See the end of file for copying conditions.
Please send mailutils bug reports to <bug-mailutils@gnu.org>.
Version 3.2.90 (Git)
* TLS configuration
Note the following changes to the TLS configuration:
** global tls section
The following statements have been renamed:
ssl-cafile renamed to ssl-ca-file
ssl-cert renamed to ssl-certificate-file
ssl-key renamed to ssl-key-file
The following statements have been removed:
enable
key-file-safety-checks
cert-file-safety-checks
ca-file-safety-checks
The functionality of the latter three is now moved to the
tls-file-checks section.
Example of a valid global tls section:
tls {
ssl-certificate-file /etc/ssl/cert/imap.pem;
ssl-key-file /etc/ssl/private/imap.key;
}
** global tls-file-checks statement
The tls-file-checks statement configures safety checks for SSL
certificate and key files. It is a global section. Its syntax is
as follows:
tls-file-checks {
key-file <arg: list>;
cert-file <arg: list>;
ca-file <arg: list>;
}
The <arg> list is a list or sequence of check names optionally
prefixed with '+' to enable or '-' to disable the corresponding check.
Valid check names are the same as in previous versions.
Use this statement instead of the deprecated cert-file-safety-checks,
key-file-safety-checks, and ca-file-safety-checks.
* Per-server TLS support
It is now possible to configure per-server SSL certificates in POP3
and IMAP4 servers. To do so, place a "tls" subsection within the
corresponding "server" section. The syntax of the "tls" subsection
is the same as for the global section described above, e.g.:
server imap.example.com:143 {
tls-mode required;
tls {
ssl-certificate-file /etc/ssl/cert/imap.pem;
ssl-key-file /etc/ssl/private/imap.key;
}
}
If the "tls" section is absent, but "tls-mode" is specified and it's
value is anything but "no", the settings from the global "tls" section
will be used. Im this case, it is an error if the global "tls"
section is not defined.
Version 3.2 - 2017-03-11
* configuration syntax
......
......@@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public License along
dnl with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.63)
AC_INIT([GNU Mailutils], [3.2], [bug-mailutils@gnu.org], [mailutils],
AC_INIT([GNU Mailutils], [3.2.90], [bug-mailutils@gnu.org], [mailutils],
[http://mailutils.org])
AC_CONFIG_SRCDIR([libmailutils/mailbox/mailbox.c])
AC_CONFIG_AUX_DIR([build-aux])
......@@ -306,6 +306,7 @@ MU_CHECK_GNUTLS([1.2.1],[
],[
status_gnutls=no
])
AM_CONDITIONAL([MU_COND_GNUTLS], [test "$status_gnutls" = yes])
AC_SUBST(SITE_MAIL_RC)
AC_ARG_WITH([mail-rc],
......
......@@ -417,6 +417,7 @@ configuration files.
* sql statement::
* ldap statement::
* tls statement::
* tls-file-checks statement::
* gsasl statement::
@end menu
......@@ -1700,8 +1701,19 @@ server @var{ipaddr}[:@var{port}] @{
backlog <number: callback>;
# @r{Kind of TLS encryption to use for this server.}
tls @samp{no}|@samp{ondemand}|@samp{required}|@samp{connection};
tls-mode @samp{no}|@samp{ondemand}|@samp{required}|@samp{connection};
tls @{
# @r{Specify SSL certificate file.}
ssl-certificate-file @var{string};
# @r{Specify SSL certificate key file.}
ssl-key-file @var{file};
# @r{Specify trusted CAs file.}
ssl-ca-file @var{file};
# @r{Set the priorities to use on the ciphers, methods, etc.}
ssl-priorities @var{string};
@}
# @r{Set server specific ACLs.}
acl @{ /* @xref{ACL Statement}. */ @};
@}
......@@ -1756,7 +1768,7 @@ settings (@pxref{General Server Configuration, timeout}).
Configures the size of the queue of pending connections
@end deffn
@deffn {Configuration} tls @var{mode};
@deffn {Configuration} tls-mode @var{mode};
Configure the use of TLS encryption. The @var{mode} argument is one
of the following:
......@@ -1781,6 +1793,20 @@ TLS is always forced when the connection is established. For
@end table
@end deffn
@deffn {Configuration} tls @{ ... @}
The @code{tls} statement configures SSL certificate and key files, as
well as other SSL settings for use in this server. It is used when
@code{tls-mode} is set to any of the following values:
@code{ondemand}, @code{required}, @code{connection}.
If @code{tls-mode} is set to any of the values above and @code{tls}
section is absent, settings from the global @code{tls} section will
be used. In this case, it is an error if the global @code{tls}
section is not defined.
@xref{tls statement}, for a discussion of its syntax.
@end deffn
@deffn {Configuration} acl
This statement defines a per-server Access Control List. Its syntax
is as described in @ref{ACL Statement}. Per-server ACLs complement,
......@@ -2483,39 +2509,32 @@ default filter is:
@subheading Syntax
@example
tls @{
# @r{Enable TLS support.}
enable @var{bool};
# @r{Specify SSL certificate file.}
ssl-cert @var{string};
ssl-certificate-file @var{string};
# @r{Specify SSL certificate key file.}
ssl-key @var{file};
ssl-key-file @var{file};
# @r{Specify trusted CAs file.}
ssl-cafile @var{file};
ssl-ca-file @var{file};
# @r{Set the priorities to use on the ciphers, methods, etc.}
ssl-priorities @var{string};
# @r{Configure safety checks for SSL key file.}
key-file-safety-checks @var{list};
# @r{Configure safety checks for SSL certificate.}
cert-file-safety-checks @var{list};
# @r{Configure safety checks for SSL CA file.}
ca-file-safety-checks @var{list};
@}
@end example
@subheading Description
@deffn {Configuration} enable @var{bool}
Enable TLS support. If absent, @samp{enable On} is assumed.
@end deffn
The @samp{tls} statement configures TLS parameters to be used by
servers. It can appear both in the global scope and in server
scope. Global tls settings are applied for servers that are declared
as supporting TLS encryption, but lack the @samp{tls} substatement.
@deffn {Configuration} ssl-cert @var{string}
@deffn {Configuration} ssl-certificate-file @var{string}
Specify SSL certificate file.
@end deffn
@deffn {Configuration} ssl-key @var{file}
@deffn {Configuration} ssl-key-file @var{file}
Specify SSL certificate key file.
@end deffn
@deffn {Configuration} ssl-cafile @var{file}
@deffn {Configuration} ssl-ca-file @var{file}
Specify the trusted certificate authorities file.
@end deffn
......@@ -2524,7 +2543,27 @@ Set the priorities to use on the ciphers, key exchange methods, MACs
and compression methods.
@end deffn
@deffn {Configuration} key-file-safety-checks @var{list}
@node tls-file-checks statement
@subsection The @code{tls-file-checks} Statement
@kwindex tls-file-checks
@subheading Syntax
@example
tls-file-checks @{
# @r{Configure safety checks for SSL key file.}
key-file @var{list};
# @r{Configure safety checks for SSL certificate.}
cert-file @var{list};
# @r{Configure safety checks for SSL CA file.}
ca-file @var{list};
@}
@end example
@subheading Description
This section configures security checks applied to the particular SSL
configuration files in order to decide whether it is safe to use them.
@deffn {Configuration} key-file @var{list}
Configure safety checks for SSL key file. Elements of the @var{list} are
names of individual checks, optionally prefixed with @samp{+} to enable or
@samp{-} to disable the corresponding check. Valid check names are:
......@@ -2551,16 +2590,17 @@ Forbid files in world writable directories,
@end table
@end deffn
@deffn {Configuration} cert-file-safety-checks @var{list}
@deffn {Configuration} cert-file @var{list}
Configure safety checks for SSL certificate. See
@code{key-file-safety-checks} for a description of @var{list}.
@code{key-file} for a description of @var{list}.
@end deffn
@deffn {Configuration} ca-file-safety-checks @var{list}
@deffn {Configuration} ca-file @var{list}
Configure safety checks for SSL CA file. See
@code{key-file-safety-checks} for a description of @var{list}.
@code{key-file} for a description of @var{list}.
@end deffn
@node gsasl statement
@subsection The @code{gsasl} Statement
@anchor{GSASL Statement}
......@@ -7494,6 +7534,7 @@ The following configuration file statements affect the behavior of
@headitem Statement @tab Reference
@item debug @tab @xref{debug statement}.
@item tls @tab @xref{tls statement}.
@item tls-file-checks @tab @xref{tls-file-checks statement}.
@item mailbox @tab @xref{mailbox statement}.
@item locking @tab @xref{locking statement}.
@item logging @tab @xref{logging statement}.
......@@ -7804,6 +7845,7 @@ configuration statements:
@headitem Statement @tab Reference
@item debug @tab @xref{debug statement}.
@item tls @tab @xref{tls statement}.
@item tls-file-checks @tab @xref{tls-file-checks statement}.
@item mailbox @tab @xref{mailbox statement}.
@item locking @tab @xref{locking statement}.
@item logging @tab @xref{logging statement}.
......
......@@ -47,8 +47,6 @@ struct imap4d_command imap4d_command_table [] =
{ "NAMESPACE", imap4d_namespace, STATE_AUTH | STATE_SEL, STATE_NONE, STATE_NONE, NULL },
{ "ID", imap4d_id, STATE_AUTH | STATE_SEL, STATE_NONE, STATE_NONE, NULL },
{ "IDLE", imap4d_idle, STATE_SEL, STATE_NONE, STATE_NONE, NULL },
#ifdef WITH_TLS
{ "STARTTLS", imap4d_starttls, STATE_NONAUTH, STATE_NONE, STATE_NONE, NULL },
#endif /* WITH_TLS */
{ NULL, 0, 0, 0, 0, NULL }
};
......
......@@ -30,7 +30,6 @@ char *real_homedir; /* Homedir as returned by user database */
int state = STATE_NONAUTH; /* Current IMAP4 state */
struct mu_auth_data *auth_data;
enum tls_mode tls_mode;
int login_disabled; /* Disable LOGIN command */
int create_home_dir; /* Create home directory if it does not
exist */
......@@ -258,13 +257,6 @@ cb_group (void *data, mu_config_value_t *arg)
}
struct imap4d_srv_config
{
struct mu_srv_config m_cfg;
enum tls_mode tls_mode;
};
#ifdef WITH_TLS
static int
cb_tls (void *data, mu_config_value_t *val)
{
......@@ -293,33 +285,6 @@ cb_tls (void *data, mu_config_value_t *val)
mu_error (_("not a valid tls keyword: %s"), val->v.string);
return 0;
}
#endif
#ifdef WITH_TLS
static int
cb_tls_required (void *data, mu_config_value_t *val)
{
int bv;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_str_to_c (val->v.string, mu_c_bool, &bv, NULL))
mu_error (_("Not a boolean value"));
else if (bv)
{
tls_mode = tls_required;
mu_diag_output (MU_DIAG_WARNING,
"the \"tls-required\" statement is deprecated, "
"use \"tls required\" instead");
}
else
mu_diag_output (MU_DIAG_WARNING,
"the \"tls-required\" statement is deprecated, "
"use \"tls\" instead");
return 0;
}
#endif
static mu_list_t auth_deny_user_list, auth_allow_user_list;
static mu_list_t auth_deny_group_list, auth_allow_group_list;
......@@ -532,13 +497,13 @@ namespace_cfg_init (void)
}
static struct mu_cfg_param imap4d_srv_param[] = {
#ifdef WITH_TLS
{ "tls", mu_cfg_callback,
{ "tls-mode", mu_cfg_callback,
NULL, mu_offsetof (struct imap4d_srv_config, tls_mode), cb_tls,
N_("Kind of TLS encryption to use for this server"),
/* TRANSLATORS: translate only arg:, the rest are keywords */
N_("arg: false|true|ondemand|stls|requred|connection") },
#endif
{ "tls", mu_cfg_section,
NULL, mu_offsetof (struct imap4d_srv_config, tls_conf) },
{ NULL }
};
......@@ -568,16 +533,7 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
{ "retain-groups", mu_cfg_callback, &user_retain_groups, 0, cb_group,
N_("Retain these supplementary groups when switching to user privileges"),
N_("groups: list of string") },
#ifdef WITH_TLS
{ "tls", mu_cfg_callback, &tls_mode, 0, cb_tls,
N_("Kind of TLS encryption to use"),
/* TRANSLATORS: translate only arg:, the rest are keywords */
N_("arg: false|true|ondemand|stls|requred|connection") },
{ "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."),
N_("arg: bool") },
#endif
{ "tls", mu_cfg_section, &global_tls_conf },
{ "preauth", mu_cfg_callback, NULL, 0, cb_preauth,
N_("Configure PREAUTH mode. <value> is one of:\n"
" prog:///<full-program-name: string>\n"
......@@ -836,7 +792,7 @@ imap4d_child_signal_setup (RETSIGTYPE (*handler) (int signo))
}
static int
imap4d_mainloop (int ifd, int ofd, enum tls_mode tls)
imap4d_mainloop (int ifd, int ofd, struct imap4d_srv_config *cfg)
{
imap4d_tokbuf_t tokp;
char *text;
......@@ -877,33 +833,27 @@ imap4d_mainloop (int ifd, int ofd, enum tls_mode tls)
/* Set child-specific signal handlers */
imap4d_child_signal_setup (imap4d_child_signal);
}
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;
}
switch (tls)
switch (cfg->tls_mode)
{
case tls_required:
imap4d_capability_add (IMAP_CAPA_XTLSREQUIRED);
imap4d_capability_add (IMAP_CAPA_STARTTLS);
break;
case tls_no:
imap4d_capability_remove (IMAP_CAPA_STARTTLS);
tls_available = 0;
case tls_ondemand:
imap4d_capability_add (IMAP_CAPA_STARTTLS);
break;
default:
break;
}
session.tls_mode = tls;
session.tls_mode = cfg->tls_mode;
session.tls_conf = &cfg->tls_conf;
io_setio (ifd, ofd, tls == tls_connection);
if (tls == tls_connection)
io_setio (ifd, ofd, cfg->tls_mode == tls_connection ? &cfg->tls_conf : NULL);
if (cfg->tls_mode == tls_connection)
tls_encryption_on (&session);
if (imap4d_preauth_setup (ifd) == 0)
......@@ -961,8 +911,7 @@ imap4d_connection (int fd, struct sockaddr *sa, int salen,
else
rc = 1;
imap4d_mainloop (fd, fd,
cfg->tls_mode == tls_unspecified ? tls_mode : cfg->tls_mode);
imap4d_mainloop (fd, fd, cfg);
if (rc == 0)
clr_strerr_flt ();
......@@ -1040,8 +989,9 @@ main (int argc, char **argv)
mu_tcpwrapper_cfg_init ();
manlock_cfg_init ();
mu_acl_cfg_init ();
mu_tls_cfg_init ();
namespace_cfg_init ();
mu_m_server_create (&server, program_version);
mu_m_server_set_config_size (server, sizeof (struct imap4d_srv_config));
mu_m_server_set_conn (server, imap4d_connection);
......@@ -1141,9 +1091,7 @@ main (int argc, char **argv)
umask (S_IROTH | S_IWOTH | S_IXOTH); /* 007 */
/* Check TLS environment, i.e. cert and key files */
#ifdef WITH_TLS
starttls_init ();
#endif /* WITH_TLS */
mu_m_server_set_preflight (server, starttls_init);
/* Actually run the daemon. */
if (mu_m_server_mode (server) == MODE_DAEMON)
......@@ -1155,9 +1103,12 @@ main (int argc, char **argv)
}
else
{
struct imap4d_srv_config cfg;
memset (&cfg, 0, sizeof cfg);
cfg.tls_mode = tls_no;
/* Make sure we are in the root directory. */
chdir ("/");
status = imap4d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, tls_mode);
status = imap4d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, &cfg);
}
if (status)
......
......@@ -193,6 +193,14 @@ enum tls_mode
struct imap4d_session
{
enum tls_mode tls_mode;
struct mu_tls_config *tls_conf;
};
struct imap4d_srv_config
{
struct mu_srv_config m_cfg;
enum tls_mode tls_mode;
struct mu_tls_config tls_conf;
};
extern struct imap4d_command imap4d_command_table[];
......@@ -217,7 +225,8 @@ extern mu_list_t imap4d_id_list;
extern int imap4d_argc;
extern char **imap4d_argv;
extern jmp_buf child_jmp;
extern struct mu_tls_config global_tls_conf;
extern int test_mode;
extern int silent_expunge;
......@@ -240,7 +249,7 @@ extern int io_stream_completion_response (mu_stream_t str,
const char *format, ...)
MU_PRINTFLIKE(4,5);
void io_getline (char **pbuf, size_t *psize, size_t *pnbytes);
void io_setio (int, int, int);
void io_setio (int, int, struct mu_tls_config *);
void io_flush (void);
int io_wait_input (int);
......@@ -333,14 +342,10 @@ extern int imap4d_select (struct imap4d_session *,
struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_select0 (struct imap4d_command *, const char *, int);
extern int imap4d_select_status (void);
#ifdef WITH_TLS
extern int imap4d_starttls (struct imap4d_session *,
struct imap4d_command *, imap4d_tokbuf_t);
extern void starttls_init (void);
int starttls_init (mu_m_server_t msrv);
void tls_encryption_on (struct imap4d_session *);
#else
# define tls_encryption_on(s)
#endif /* WITH_TLS */
extern int imap4d_status (struct imap4d_session *,
struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_store (struct imap4d_session *,
......@@ -466,9 +471,7 @@ int util_trim_nl (char *s, size_t len);
int set_xscript_level (int xlev);
#ifdef WITH_TLS
int imap4d_init_tls_server (void);
#endif /* WITH_TLS */
int imap4d_init_tls_server (struct mu_tls_config *);
struct imap4d_auth
{
......
......@@ -50,7 +50,7 @@ log_cipher (mu_stream_t stream)
}
void
io_setio (int ifd, int ofd, int tls)
io_setio (int ifd, int ofd, struct mu_tls_config *tls_conf)
{
mu_stream_t str, istream, ostream;
......@@ -68,10 +68,12 @@ io_setio (int ifd, int ofd, int tls)
mu_stream_set_buffer (ostream, mu_buffer_line, 0);
/* Combine the two streams into an I/O one. */
#ifdef WITH_TLS
if (tls)
if (tls_conf)
{
int rc = mu_tls_server_stream_create (&str, istream, ostream, 0);
int rc = mu_tls_stream_create (&str, istream, ostream,
tls_conf,
MU_TLS_SERVER,
0);
if (rc)
{
mu_stream_unref (istream);
......@@ -81,9 +83,7 @@ io_setio (int ifd, int ofd, int tls)
}
log_cipher (str);
}
else
#endif
if (mu_iostream_create (&str, istream, ostream))
else if (mu_iostream_create (&str, istream, ostream))
imap4d_bye (ERR_STREAM_CREATE);
/* Convert all writes to CRLF form.
......@@ -120,9 +120,8 @@ io_setio (int ifd, int ofd, int tls)
}
}
#ifdef WITH_TLS
int
imap4d_init_tls_server ()
imap4d_init_tls_server (struct mu_tls_config *tls_conf)
{
mu_stream_t tlsstream, stream[2];
int rc;
......@@ -135,7 +134,12 @@ imap4d_init_tls_server ()
return 1;
}
rc = mu_tls_server_stream_create (&tlsstream, stream[0], stream[1], 0);
rc = mu_tls_stream_create (&tlsstream, stream[0], stream[1],
tls_conf,
MU_TLS_SERVER,
0);
mu_stream_unref (stream[0]);
mu_stream_unref (stream[1]);
if (rc)
{
mu_diag_output (MU_DIAG_ERROR, _("cannot open TLS stream: %s"),
......@@ -145,8 +149,6 @@ imap4d_init_tls_server ()
log_cipher (tlsstream);
mu_stream_unref (stream[0]);
mu_stream_unref (stream[1]);
stream[0] = stream[1] = tlsstream;
rc = mu_stream_ioctl (iostream, MU_IOCTL_SUBSTREAM, MU_IOCTL_OP_SET, stream);
......@@ -158,11 +160,9 @@ imap4d_init_tls_server ()
}
mu_stream_unref (stream[0]);
mu_stream_unref (stream[1]);
return 0;
}
#endif
/* Status Code to String. */
static const char *
......
......@@ -17,11 +17,7 @@
#include "imap4d.h"
int tls_available;
#ifdef WITH_TLS
static int tls_done;
struct mu_tls_config global_tls_conf;
/*
6.2.1. STARTTLS Command
......@@ -39,7 +35,7 @@ imap4d_starttls (struct imap4d_session *session,
{
int status;
if (!tls_available || tls_done)
if (session->tls_mode == tls_no)
return io_completion_response (command, RESP_BAD, "Invalid command");
if (imap4d_tokbuf_argc (tok) != 2)
......@@ -48,7 +44,7 @@ imap4d_starttls (struct imap4d_session *session,
status = io_completion_response (command, RESP_OK, "Begin TLS negotiation");
io_flush ();
if (imap4d_init_tls_server () == 0)
if (imap4d_init_tls_server (session->tls_conf) == 0)
tls_encryption_on (session);
else
{
......@@ -63,24 +59,101 @@ imap4d_starttls (struct imap4d_session *session,
void
tls_encryption_on (struct imap4d_session *session)
{
tls_done = 1;
session->tls_mode = tls_no;
imap4d_capability_remove (IMAP_CAPA_STARTTLS);
login_disabled = 0;
imap4d_capability_remove (IMAP_CAPA_LOGINDISABLED);
session->tls_mode = tls_no;
imap4d_capability_remove (IMAP_CAPA_XTLSREQUIRED);
}
void
starttls_init ()
int
starttls_init (mu_m_server_t msrv)
{
tls_available = mu_check_tls_environment ();
if (tls_available)
imap4d_capability_add (IMAP_CAPA_STARTTLS);
}
mu_list_t srvlist;
mu_iterator_t itr;
int errors = 0;
int tls_ok = mu_init_tls_libs ();
int tls_requested = 0;
int global_conf_status = 0;
if (global_tls_conf.cert_file)
global_conf_status = mu_tls_config_check (&global_tls_conf, 1);
else
global_conf_status = MU_TLS_CONFIG_NULL;
mu_m_server_get_srvlist (msrv, &srvlist);
mu_list_get_iterator (srvlist, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
{
mu_ip_server_t ipsrv;
struct imap4d_srv_config *cfg;
mu_iterator_current (itr, (void**) &ipsrv);
cfg = mu_ip_server_get_data (ipsrv);
switch (cfg->tls_mode)
{
case tls_unspecified:
if (cfg->tls_conf.cert_file)
{
cfg->tls_mode = tls_ondemand;
break;
}
else
cfg->tls_mode = tls_no;
/* fall through */
case tls_no:
continue;
default:
break;
}
switch (mu_tls_config_check (&cfg->tls_conf, 1))
{
case MU_TLS_CONFIG_OK:
if (!cfg->tls_conf.cert_file)
{
mu_error (_("server %s: no certificate set"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
break;
case MU_TLS_CONFIG_NULL:
if (global_conf_status != MU_TLS_CONFIG_NULL)
{
cfg->tls_conf = global_tls_conf;
}
else
{
mu_error (_("server %s: no certificate set"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
break;
default:
mu_error (_("server %s: TLS configuration failed"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
tls_requested = 1;
}
mu_iterator_destroy (&itr);
#endif /* WITH_TLS */
if (tls_requested && !tls_ok)
{
mu_error (_("TLS is not configured, but requested in the "
"configuration"));
errors = 1;
}
if (errors)
return 1;
return 0;
}
/* EOF */
......
......@@ -180,13 +180,16 @@ typedef int (*mu_cfg_section_fp) (enum mu_cfg_section_stage stage,
struct mu_cfg_section
{
const char *ident;
char *label;
mu_cfg_section_fp parser;
void *target;
size_t offset;
const char *ident; /* Section identifier */
char *label; /* Label description */
mu_cfg_section_fp parser; /* Parser function */
void *data; /* Data pointer */
size_t offset; /* Offset within target (see below) */
mu_list_t /* of mu_cfg_cont */ children;
char *docstring;
char *docstring; /* Documentation string */
void *target; /* Actual pointer to the data. It is
recomputed each time the section is
reduced. */
};
enum mu_cfg_cont_type
......
......@@ -28,7 +28,7 @@ typedef int (*mu_conn_loop_fp) (int fd, void *conn_data, void *server_data);
typedef void (*mu_conn_free_fp) (void *conn_data, void *server_data);
typedef int (*mu_server_idle_fp) (void *server_data);
typedef void (*mu_server_free_fp) (void *server_data);
#define MU_SERVER_SUCCESS 0
#define MU_SERVER_CLOSE_CONN 1
#define MU_SERVER_SHUTDOWN 2
......@@ -40,7 +40,8 @@ int mu_server_set_idle (mu_server_t srv, mu_server_idle_fp fp);
int mu_server_set_data (mu_server_t srv, void *data, mu_server_free_fp fp);
int mu_server_add_connection (mu_server_t srv,
int fd, void *data,
mu_conn_loop_fp loop, mu_conn_free_fp free);
mu_conn_loop_fp loop,
mu_conn_free_fp free);
struct timeval;
int mu_server_set_timeout (mu_server_t srv, struct timeval *to);
int mu_server_count (mu_server_t srv, size_t *pcount);
......@@ -100,6 +101,7 @@ typedef struct mu_m_server_connect_data mu_m_server_connect_data_t;
typedef int (*mu_m_server_handler_fp) (int fd, struct sockaddr *sa, int salen,
struct mu_srv_config *pconf,
void *data);
typedef int (*mu_m_server_preflight_fp) (mu_m_server_t);
void mu_m_server_create (mu_m_server_t *psrv, const char *ident);
void mu_m_server_destroy (mu_m_server_t *pmsrv);
......@@ -109,6 +111,7 @@ void mu_m_server_get_type (mu_m_server_t srv, int *ptype);
void mu_m_server_set_conn (mu_m_server_t srv, mu_m_server_handler_fp f);
void mu_m_server_set_prefork (mu_m_server_t srv, mu_m_server_handler_fp fun);
void mu_m_server_set_data (mu_m_server_t srv, void *data);
void *mu_ip_server_get_data (mu_ip_server_t tcpsrv);
void mu_m_server_set_max_children (mu_m_server_t srv, size_t num);
int mu_m_server_set_pidfile (mu_m_server_t srv, const char *pidfile);
int mu_m_server_set_foreground (mu_m_server_t srv, int enable);
......@@ -119,6 +122,8 @@ void mu_m_server_set_sigset (mu_m_server_t srv, sigset_t *sigset);
void mu_m_server_set_strexit (mu_m_server_t srv, const char *(*fun) (int));
void mu_m_server_set_app_data_size (mu_m_server_t srv, size_t size);
int mu_m_server_set_config_size (mu_m_server_t srv, size_t size);
void mu_m_server_set_preflight (mu_m_server_t srv,
mu_m_server_preflight_fp fun);
struct mu_srv_config *mu_m_server_listen (mu_m_server_t msrv,
struct mu_sockaddr *s, int type);
......@@ -144,5 +149,4 @@ int mu_m_server_check_acl (mu_m_server_t msrv, struct sockaddr *s, int salen);
struct mu_cfg_param;
void mu_m_server_cfg_init (mu_m_server_t msrv, struct mu_cfg_param *app_param);
#endif
......
......@@ -20,6 +20,7 @@
# include <mailutils/types.h>
# include <mailutils/stream.h>
# include <mailutils/sys/stream.h>
# include <mailutils/tls.h>
enum _mu_tls_stream_state
{
......@@ -40,9 +41,16 @@ struct _mu_tls_stream
{
struct _mu_stream stream;
enum _mu_tls_stream_state state;
int session_type; /* Either GNUTLS_CLIENT or GNUTLS_SERVER */
gnutls_session_t session;
int tls_err;
mu_stream_t transport[2];
struct mu_tls_config conf;
gnutls_certificate_credentials_t cred;
};
extern int mu_tls_io_stream_create (mu_stream_t *pstream,
mu_stream_t transport, int flags,
struct _mu_tls_stream *master);
#endif
......
......@@ -21,39 +21,67 @@
#include <mailutils/types.h>
#include <mailutils/cli.h>
#include <mailutils/util.h>
#ifdef __cplusplus
extern "C" {
#endif
struct mu_tls_module_config
struct mu_tls_config
{
int enable;
char *ssl_cert;
int ssl_cert_safety_checks;
char *ssl_key;
int ssl_key_safety_checks;
char *ssl_cafile;
int ssl_cafile_safety_checks;
char *cert_file;
char *key_file;
char *ca_file;
char *priorities;
};
extern int mu_tls_server_stream_create (mu_stream_t *stream,
mu_stream_t strin, mu_stream_t strout,
int flags);
extern int mu_tls_client_stream_create (mu_stream_t *stream,
mu_stream_t strin, mu_stream_t strout,
int flags);
extern int mu_check_tls_environment (void);
extern int mu_init_tls_libs (int x509);
extern void mu_deinit_tls_libs (void);
enum mu_tls_type
{
MU_TLS_CLIENT,
MU_TLS_SERVER
};
extern int mu_tls_enable;
extern int mu_tls_cert_file_checks;
extern int mu_tls_key_file_checks;
extern int mu_tls_ca_file_checks;
#define MU_TLS_CERT_FILE_CHECKS \
(MU_FILE_SAFETY_GROUP_WRITABLE \
| MU_FILE_SAFETY_GROUP_WRITABLE \
| MU_FILE_SAFETY_LINKED_WRDIR)
#define MU_TLS_KEY_FILE_CHECKS \
(MU_FILE_SAFETY_ALL & ~MU_FILE_SAFETY_OWNER_MISMATCH)
#define MU_TLS_CA_FILE_CHECKS \
(MU_FILE_SAFETY_GROUP_WRITABLE \
| MU_FILE_SAFETY_GROUP_WRITABLE \
| MU_FILE_SAFETY_LINKED_WRDIR)
void mu_tls_cfg_init (void);
int mu_tls_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout,
struct mu_tls_config const *conf,
enum mu_tls_type type,
int flags);
int mu_tls_client_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout,
int flags);
void mu_deinit_tls_libs (void);
int mu_init_tls_libs (void);
enum mu_tls_config_status
{
MU_TLS_CONFIG_OK, /* Configuration OK */
MU_TLS_CONFIG_NULL, /* Configuration is empty */
MU_TLS_CONFIG_UNSAFE, /* At least one file is considered unsafe */
MU_TLS_CONFIG_FAIL /* Some files absent (or other system error) */
};
int mu_tls_config_check (struct mu_tls_config const *conf, int verbose);
extern struct mu_cli_capa mu_cli_capa_tls;
......
......@@ -161,7 +161,7 @@ dup_container (struct mu_cfg_cont **pcont)
newcont->v.section.ident = oldcont->v.section.ident;
newcont->v.section.label = oldcont->v.section.label;
newcont->v.section.parser = oldcont->v.section.parser;
newcont->v.section.target = oldcont->v.section.target;
newcont->v.section.data = oldcont->v.section.data;
newcont->v.section.offset = oldcont->v.section.offset;
newcont->v.section.docstring = oldcont->v.section.docstring;
newcont->v.section.children = NULL;
......@@ -281,11 +281,27 @@ mu_cfg_section_add_params (struct mu_cfg_section *sect,
switch (c->type)
{
case mu_cfg_cont_section:
c->v.section.offset += param->offset;
if (param->data)
{
c->v.section.data = param->data;
c->v.section.offset = param->offset;
}
else if (c->v.section.data)
/* Keep data & offset */;
else
c->v.section.offset += param->offset;
break;
case mu_cfg_cont_param:
container->v.param.offset += param->offset;
if (param->data)
{
container->v.param.data = param->data;
container->v.param.offset = param->offset;
}
else if (container->v.param.data)
/* Keep data & offset */;
else
container->v.param.offset += param->offset;
break;
}
mu_cfg_section_add_container (sect, c);
......@@ -298,7 +314,7 @@ mu_cfg_section_add_params (struct mu_cfg_section *sect,
mu_config_clone_container (container);
if (mu_refcount_value (container->refcount) > 1)
dup_container (&container);
container->v.section.target = param->data;
container->v.section.data = param->data;
container->v.section.offset = param->offset;
}
}
......
......@@ -972,10 +972,14 @@ _scan_tree_helper (const mu_cfg_node_t *node, void *data)
}
if (!sec->children)
return MU_CFG_ITER_SKIP;
if (sdata->list->sec->target)
if (sec->data)
sec->target = sec->data;
else if (sdata->list->sec->target)
sec->target = (char*)sdata->list->sec->target + sec->offset;
else if (sdata->target)
sec->target = (char*)sdata->target + sec->offset;
else
sec->target = NULL;
if (sec->parser)
{
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
......
......@@ -41,3 +41,4 @@ wicket
assoc
acl
server
tls
......
......@@ -216,6 +216,12 @@ mu_ip_server_set_data (mu_ip_server_t srv,
return 0;
}
void *
mu_ip_server_get_data (mu_ip_server_t tcpsrv)
{
return tcpsrv->data;
}
int
mu_address_family_to_domain (int family)
{
......
......@@ -77,7 +77,7 @@ struct _mu_m_server
mu_list_t srvlist; /* A list of configured mu_ip_server_t
objects. It is cleared after the objects
are opened and attached to the server. */
mu_m_server_preflight_fp preflight; /* Pre-flight check function */
mu_m_server_handler_fp conn; /* Connection handler function. */
mu_m_server_handler_fp prefork;/* Pre-fork function. */
void *data; /* User-supplied data for conn and prefork. */
......@@ -366,6 +366,12 @@ mu_m_server_set_strexit (mu_m_server_t srv, const char *(*fun) (int))
srv->strexit = fun;
}
void
mu_m_server_set_preflight (mu_m_server_t srv, mu_m_server_preflight_fp fun)
{
srv->preflight = fun;
}
int
mu_m_server_get_srvlist (mu_m_server_t srv, mu_list_t *plist)
{
......@@ -565,6 +571,7 @@ mu_m_server_destroy (mu_m_server_t *pmsrv)
{
mu_m_server_t msrv = *pmsrv;
mu_list_remove (m_server_list, msrv);
mu_list_destroy (&msrv->srvlist);
mu_server_destroy (&msrv->server);
free (msrv->child_pid);
/* FIXME: Send processes the TERM signal here?*/
......@@ -594,17 +601,16 @@ tcp_conn_free (void *conn_data, void *server_data)
}
static int
_open_conn (void *item, void *data)
open_connection (mu_ip_server_t tcpsrv, mu_m_server_t msrv)
{
mu_ip_server_t tcpsrv = item;
mu_m_server_t msrv = data;
int rc = mu_ip_server_open (tcpsrv);
if (rc)
{
mu_error (_("cannot open connection on %s: %s"),
mu_ip_server_addrstr (tcpsrv), mu_strerror (rc));
return 0;
return rc;
}
rc = mu_server_add_connection (msrv->server,
mu_ip_server_get_fd (tcpsrv),
tcpsrv,
......@@ -614,9 +620,8 @@ _open_conn (void *item, void *data)
mu_error (_("cannot add connection %s: %s"),
mu_ip_server_addrstr (tcpsrv), mu_strerror (rc));
mu_ip_server_shutdown (tcpsrv);
mu_ip_server_destroy (&tcpsrv);
}
return 0;
return rc;
}
int
......@@ -624,16 +629,36 @@ mu_m_server_run (mu_m_server_t msrv)
{
int rc;
size_t count;
mode_t saved_umask = umask (0117);
mu_list_foreach (msrv->srvlist, _open_conn, msrv);
mode_t saved_umask;
mu_iterator_t itr;
saved_umask = umask (0117);
mu_list_get_iterator (msrv->srvlist, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
{
mu_ip_server_t tcpsrv;
mu_iterator_current (itr, (void**) &tcpsrv);
if (open_connection (tcpsrv, msrv))
{
mu_iterator_ctl (itr, mu_itrctl_delete_nd, NULL);
mu_ip_server_destroy (&tcpsrv);
}
}
umask (saved_umask);
mu_list_destroy (&msrv->srvlist);
mu_iterator_destroy (&itr);
MU_ASSERT (mu_server_count (msrv->server, &count));
if (count == 0)
{
mu_error (_("no servers configured: exiting"));
exit (1);
}
if (msrv->preflight && msrv->preflight (msrv))
{
mu_error (_("%s: preflight check failed"), msrv->ident);
return MU_ERR_FAILURE;
}
if (msrv->ident)
mu_diag_output (MU_DIAG_INFO, _("%s started"), msrv->ident);
rc = mu_server_run (msrv->server);
......@@ -642,8 +667,6 @@ mu_m_server_run (mu_m_server_t msrv)
mu_diag_output (MU_DIAG_INFO, _("%s terminated"), msrv->ident);
return rc;
}
int
mu_m_server_check_acl (mu_m_server_t msrv, struct sockaddr *s, int salen)
......
......@@ -29,9 +29,18 @@ libmu_auth_la_SOURCES = \
radius.c\
sql.c\
sql.h\
tls.c\
virtual.c
libmu_auth_la_SOURCES += tlsconf.c tlsvar.c
if MU_COND_GNUTLS
libmu_auth_la_SOURCES += \
tls.c\
tlsiostr.c
else
libmu_auth_la_SOURCES += notls.c
endif
libmu_auth_la_LIBADD = ${MU_LIB_MAILUTILS} @MU_AUTHLIBS@ @SQLLIB@ @LTLIBINTL@
libmu_auth_la_LDFLAGS = -version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@
libmu_auth_la_DEPENDENCIES = @SQLLIB_DEPENDENCY@
......
/* Declare fail-only TLS interfaces in the absense of GNU TLS */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/tls.h>
#include <mailutils/errno.h>
int
mu_tls_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout,
struct mu_tls_config const *conf,
enum mu_tls_type type,
int flags)
{
return ENOSYS;
}
void
mu_deinit_tls_libs (void)
{
}
int
mu_init_tls_libs (void)
{
mu_tls_enable = 0;
return 0;
}
......@@ -20,123 +20,33 @@
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <mailutils/error.h>
#include <mailutils/mu_auth.h>
#include <mailutils/tls.h>
#include <mailutils/nls.h>
#include <mailutils/stream.h>
#include <mailutils/error.h>
#include <mailutils/errno.h>
#include <mailutils/util.h>
#include <mailutils/property.h>
#include <mailutils/cli.h>
#define SSL_CERT_FILE_CHECKS (MU_FILE_SAFETY_GROUP_WRITABLE | \
MU_FILE_SAFETY_GROUP_WRITABLE | \
MU_FILE_SAFETY_LINKED_WRDIR)
#define SSL_KEY_FILE_CHECKS (MU_FILE_SAFETY_ALL & \
~MU_FILE_SAFETY_OWNER_MISMATCH)
#define SSL_CA_FILE_CHECKS (MU_FILE_SAFETY_GROUP_WRITABLE | \
MU_FILE_SAFETY_GROUP_WRITABLE | \
MU_FILE_SAFETY_LINKED_WRDIR)
struct mu_tls_module_config mu_tls_module_config = {
#ifdef WITH_TLS
1, /* enable by default */
NULL, /* Certificate file */
SSL_CERT_FILE_CHECKS,
NULL, /* Key file */
SSL_KEY_FILE_CHECKS, /* Stringent safety checks for keys */
NULL, /* CA file */
SSL_CA_FILE_CHECKS
#else
0
#endif
};
#ifdef WITH_TLS
#include <mailutils/mu_auth.h>
#include <gnutls/gnutls.h>
#include <mailutils/sys/tls-stream.h>
static gnutls_certificate_server_credentials x509_cred;
/* Return: zero means NOT READY, one means READY */
int
mu_check_tls_environment (void)
{
if (!mu_tls_module_config.enable)
return 0;
if (mu_tls_module_config.ssl_cert && mu_tls_module_config.ssl_key)
{
int rc = mu_file_safety_check (mu_tls_module_config.ssl_cert,
mu_tls_module_config.ssl_cert_safety_checks,
-1, NULL);
if (rc)
{
mu_error ("%s: %s", mu_tls_module_config.ssl_cert,
mu_strerror (rc));
return 0;
}
rc = mu_file_safety_check (mu_tls_module_config.ssl_key,
mu_tls_module_config.ssl_key_safety_checks,
-1, NULL);
if (rc)
{
mu_error ("%s: %s", mu_tls_module_config.ssl_key,
mu_strerror (rc));
return 0;
}
if (mu_tls_module_config.ssl_cafile)
{
rc = mu_file_safety_check (mu_tls_module_config.ssl_cafile,
mu_tls_module_config.ssl_cafile_safety_checks,
-1, NULL);
if (rc)
{
mu_error ("%s: %s", mu_tls_module_config.ssl_cafile,
mu_strerror (rc));
return 0;
}
}
}
else
return 0;
return 1;
}
int mu_tls_enable = 0;
#ifdef DEBUG_TLS
void
static void
_mu_gtls_logger(int level, const char *text)
{
mu_diag_output (MU_DIAG_DEBUG, "GnuTLS(%d): %s", level, text);
int len = strlen (text);
if (text[len-1] == '\n')
len--;
mu_diag_output (MU_DIAG_DEBUG, "GnuTLS(%d): %.*s", level, len, text);
}
#endif
void
mu_deinit_tls_libs (void)
{
if (mu_tls_enable)
{
if (x509_cred)
gnutls_certificate_free_credentials (x509_cred);
gnutls_global_deinit ();
}
gnutls_global_deinit ();
mu_tls_enable = 0;
}
......@@ -147,270 +57,31 @@ _onexit_deinit (void *ptr MU_ARG_UNUSED)
}
int
mu_init_tls_libs (int x509_setup)
mu_init_tls_libs (void)
{
if (!mu_tls_enable)
{
int rc;
if ((rc = gnutls_global_init ()) == GNUTLS_E_SUCCESS)
mu_tls_enable = 1;
else
{
mu_error ("gnutls_global_init: %s", gnutls_strerror (rc));
return 0;
}
mu_onexit (_onexit_deinit, NULL);
}
if (x509_setup && !x509_cred)
{
gnutls_certificate_allocate_credentials (&x509_cred);
if (mu_tls_module_config.ssl_cafile)
gnutls_certificate_set_x509_trust_file (x509_cred,
mu_tls_module_config.ssl_cafile,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_key_file (x509_cred,
mu_tls_module_config.ssl_cert,
mu_tls_module_config.ssl_key,
GNUTLS_X509_FMT_PEM);
}
#ifdef DEBUG_TLS
gnutls_global_set_log_function (_mu_gtls_logger);
gnutls_global_set_log_level (110);
#endif
return mu_tls_enable;
}
static char default_priority_string[] = "NORMAL";
static gnutls_session_t
initialize_tls_session (void)
{
gnutls_session_t session = 0;
gnutls_init (&session, GNUTLS_SERVER);
gnutls_priority_set_direct (session,
mu_tls_module_config.priorities
? mu_tls_module_config.priorities
: default_priority_string,
NULL);
gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
return session;
}
/* ************************* TLS Stream Support **************************** */
static int
_tls_io_close (mu_stream_t stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
return mu_stream_close (sp->transport);
}
static void
_tls_io_done (struct _mu_stream *stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
mu_stream_unref (sp->transport);
}
static int
_tls_io_flush (struct _mu_stream *stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
return mu_stream_flush (sp->transport);
}
static int
_tls_io_read (struct _mu_stream *stream, char *buf, size_t bufsize,
size_t *pnread)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc;
if (sp->up->state != state_open)
return EINVAL;
rc = gnutls_record_recv (sp->up->session, buf, bufsize);
if (rc >= 0)
{
*pnread = rc;
return 0;
}
sp->up->tls_err = rc;
return EIO;
}
static int
_tls_io_write (struct _mu_stream *stream, const char *buf, size_t bufsize,
size_t *pnwrite)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc;
if (sp->up->state != state_open)
return EINVAL;
/* gnutls_record_send() docs say:
If the EINTR is returned by the internal push function (write())
then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or
GNUTLS_E_AGAIN is returned you must call this function again, with the
same parameters. Otherwise the write operation will be
corrupted and the connection will be terminated. */
do
rc = gnutls_record_send (sp->up->session, buf, bufsize);
while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
if (rc < 0)
{
sp->up->tls_err = rc;
return EIO;
}
*pnwrite = rc;
return 0;
}
static int
_tls_rd_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc = EINVAL;
if (*pflags == MU_STREAM_READY_RD)
rc = mu_stream_wait (sp->transport, pflags, tvp);
return rc;
}
static int
_tls_wr_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc = EINVAL;
if (*pflags == MU_STREAM_READY_WR)
rc = mu_stream_wait (sp->transport, pflags, tvp);
return rc;
}
static int
get_cipher_info (gnutls_session_t session, mu_property_t *pprop)
{
mu_property_t prop;
const char *s;
int rc;
if (!pprop)
return EINVAL;
rc = mu_property_create_init (&prop, mu_assoc_property_init, NULL);
if (rc)
return rc;
s = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
mu_property_set_value (prop, "protocol", s, 1);
s = gnutls_cipher_get_name (gnutls_cipher_get (session));
mu_property_set_value (prop, "cipher", s, 1);
s = gnutls_mac_get_name (gnutls_mac_get (session));
mu_property_set_value (prop, "mac", s, 1);
*pprop = prop;
return 0;
}
static int
_tls_io_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
switch (code)
{
case MU_IOCTL_TRANSPORT:
if (!arg)
return EINVAL;
else
int rc = gnutls_global_init ();
if (rc == GNUTLS_E_SUCCESS)
{
mu_transport_t *ptrans = arg;
switch (opcode)
mu_tls_enable = 1;
mu_onexit (_onexit_deinit, NULL);
if (mu_debug_level_p (MU_DEBCAT_TLS, MU_DEBUG_PROT))
{
case MU_IOCTL_OP_GET:
ptrans[0] = (mu_transport_t) sp->transport;
ptrans[1] = NULL;
break;
case MU_IOCTL_OP_SET:
return ENOSYS;
default:
return EINVAL;
gnutls_global_set_log_function (_mu_gtls_logger);
gnutls_global_set_log_level (110);
}
}
break;
case MU_IOCTL_TLSSTREAM:
switch (opcode)
else
{
case MU_IOCTL_TLS_GET_CIPHER_INFO:
return get_cipher_info (sp->up->session, arg);
default:
return EINVAL;
mu_error ("gnutls_global_init: %s", gnutls_strerror (rc));
}
break;
default:
return ENOSYS;
}
return 0;
}
static int
_mu_tls_io_stream_create (mu_stream_t *pstream,
mu_stream_t transport, int flags,
struct _mu_tls_stream *master)
{
struct _mu_tls_io_stream *sp;
sp = (struct _mu_tls_io_stream *)
_mu_stream_create (sizeof (*sp), (flags & MU_STREAM_RDWR) | _MU_STR_OPEN);
if (!sp)
return ENOMEM;
if (flags & MU_STREAM_READ)
{
sp->stream.read = _tls_io_read;
sp->stream.wait = _tls_rd_wait;
mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_full, 0);
}
else
{
sp->stream.write = _tls_io_write;
sp->stream.wait = _tls_wr_wait;
mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 0);
}
sp->stream.flush = _tls_io_flush;
sp->stream.close = _tls_io_close;
sp->stream.done = _tls_io_done;
sp->stream.ctl = _tls_io_ioctl;
mu_stream_ref (transport);
sp->transport = transport;
sp->up = master;
*pstream = (mu_stream_t) sp;
return 0;
return mu_tls_enable;
}
/* TLS push & pull functions */
static ssize_t
_tls_stream_pull (gnutls_transport_ptr_t fd, void *buf, size_t size)
{
......@@ -443,82 +114,125 @@ _tls_stream_push (gnutls_transport_ptr_t fd, const void *buf, size_t size)
mu_stream_flush (stream);
return size;
}
static int
_tls_server_open (mu_stream_t stream)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
int rc = 0;
mu_transport_t transport[2];
if (!mu_tls_module_config.enable)
return MU_ERR_DISABLED;
if (!stream || sp->state != state_init)
return EINVAL;
if (!mu_init_tls_libs (1))
return MU_ERR_FAILURE;
sp->session = initialize_tls_session ();
mu_stream_ioctl (stream, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, transport);
gnutls_transport_set_ptr2 (sp->session,
(gnutls_transport_ptr_t) transport[0],
(gnutls_transport_ptr_t) transport[1]);
gnutls_transport_set_pull_function (sp->session, _tls_stream_pull);
gnutls_transport_set_push_function (sp->session, _tls_stream_push);
rc = gnutls_handshake (sp->session);
if (rc < 0)
{
gnutls_deinit (sp->session);
sp->tls_err = rc;
return EIO;
}
sp->state = state_open;
return 0;
}
static char default_priority_string[] = "NORMAL";
static int
prepare_client_session (mu_stream_t stream)
prep_session (mu_stream_t stream)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
int rc;
gnutls_certificate_credentials_t cred = NULL;
mu_transport_t transport[2];
int rc;
const char *errp;
gnutls_init (&sp->session, GNUTLS_CLIENT);
gnutls_priority_set_direct (sp->session,
mu_tls_module_config.priorities
? mu_tls_module_config.priorities
: default_priority_string,
NULL);
gnutls_certificate_allocate_credentials (&x509_cred);
if (mu_tls_module_config.ssl_cafile)
if (!sp->cred)
{
rc = gnutls_certificate_set_x509_trust_file (x509_cred,
mu_tls_module_config.ssl_cafile,
GNUTLS_X509_FMT_PEM);
if (rc < 0)
rc = gnutls_certificate_allocate_credentials (&cred);
if (rc)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("gnutls_certificate_allocate_credentials: %s",
gnutls_strerror (rc)));
sp->tls_err = rc;
return -1;
return MU_ERR_FAILURE;
}
if (sp->conf.ca_file)
{
rc = gnutls_certificate_set_x509_trust_file (cred, sp->conf.ca_file,
GNUTLS_X509_FMT_PEM);
if (rc < 0)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("can't use X509 CA file %s: %s",
sp->conf.ca_file,
gnutls_strerror (rc)));
goto cred_err;
}
}
if (sp->conf.cert_file && sp->conf.key_file)
{
rc = gnutls_certificate_set_x509_key_file (cred,
sp->conf.cert_file,
sp->conf.key_file,
GNUTLS_X509_FMT_PEM);
if (rc != GNUTLS_E_SUCCESS)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("can't use X509 cert/key pair (%s,%s): %s",
sp->conf.cert_file,
sp->conf.key_file,
gnutls_strerror (rc)));
goto cred_err;
}
}
sp->cred = cred;
}
rc = gnutls_init (&sp->session, sp->session_type);
if (rc != GNUTLS_E_SUCCESS)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("failed to initialize session: %s", gnutls_strerror (rc)));
goto cred_err;
}
gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
mu_stream_ioctl (stream, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, transport);
rc = gnutls_priority_set_direct (sp->session,
sp->conf.priorities
? sp->conf.priorities
: default_priority_string,
&errp);
if (rc != GNUTLS_E_SUCCESS)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("error setting priorities near %s: %s",
errp, gnutls_strerror (rc)));
goto cred_err;
}
rc = gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, sp->cred);
if (rc)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("gnutls_credentials_set: %s", gnutls_strerror (rc)));
goto sess_err;
}
if (sp->session_type == GNUTLS_SERVER)
gnutls_certificate_server_set_request (sp->session, GNUTLS_CERT_REQUEST);
rc = mu_stream_ioctl (stream, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET,
transport);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_ioctl", NULL, rc);
abort (); /* should not happen */
}
gnutls_transport_set_ptr2 (sp->session,
(gnutls_transport_ptr_t) transport[0],
(gnutls_transport_ptr_t) transport[1]);
gnutls_transport_set_pull_function (sp->session, _tls_stream_pull);
gnutls_transport_set_push_function (sp->session, _tls_stream_push);
return 0;
sess_err:
gnutls_deinit (sp->session);
cred_err:
if (sp->cred)
{
gnutls_certificate_free_credentials (sp->cred);
sp->cred = NULL;
}
sp->tls_err = rc;
return MU_ERR_FAILURE;
}
static int
_tls_client_open (mu_stream_t stream)
_tls_open (mu_stream_t stream)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
int rc = 0;
......@@ -527,32 +241,38 @@ _tls_client_open (mu_stream_t stream)
{
case state_closed:
if (sp->session)
gnutls_deinit (sp->session);
{
gnutls_deinit (sp->session);
sp->session = NULL;
}
/* FALLTHROUGH */
case state_init:
if (!mu_init_tls_libs (0))
return MU_ERR_FAILURE;
prepare_client_session (stream);
rc = prep_session (stream);
if (rc)
break;
rc = gnutls_handshake (sp->session);
if (rc < 0)
if (rc != GNUTLS_E_SUCCESS)
{
mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
("gnutls_handshake: %s", gnutls_strerror (rc)));
sp->tls_err = rc;
gnutls_deinit (sp->session);
sp->session = NULL;
sp->state = state_init;
return MU_ERR_FAILURE;
}
else
/* FIXME: if (ssl_cafile) verify_certificate (s->session); */
sp->state = state_open;
break;
default:
return MU_ERR_FAILURE;
rc = MU_ERR_BADOP;
}
/* FIXME: if (ssl_cafile) verify_certificate (s->session); */
sp->state = state_open;
return 0;
return rc;
}
/* TLS stream */
static int
_tls_read (struct _mu_stream *str, char *buf, size_t bufsize,
size_t *pnread)
......@@ -570,6 +290,34 @@ _tls_write (struct _mu_stream *str, const char *buf, size_t bufsize,
}
static int
get_cipher_info (gnutls_session_t session, mu_property_t *pprop)
{
mu_property_t prop;
const char *s;
int rc;
if (!pprop)
return EINVAL;
rc = mu_property_create_init (&prop, mu_assoc_property_init, NULL);
if (rc)
return rc;
s = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
mu_property_set_value (prop, "protocol", s, 1);
s = gnutls_cipher_get_name (gnutls_cipher_get (session));
mu_property_set_value (prop, "cipher", s, 1);
s = gnutls_mac_get_name (gnutls_mac_get (session));
mu_property_set_value (prop, "mac", s, 1);
*pprop = prop;
return 0;
}
static int
_tls_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
......@@ -684,17 +432,18 @@ _tls_close (mu_stream_t stream)
return 0;
}
static void free_conf (struct mu_tls_config *conf);
static void
_tls_done (struct _mu_stream *stream)
{
struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
if (sp->session && sp->state == state_closed)
{
gnutls_deinit (sp->session);
sp->state = state_destroyed;
}
if (sp->session)
gnutls_deinit (sp->session);
if (sp->cred)
gnutls_certificate_free_credentials (sp->cred);
free_conf (&sp->conf);
mu_stream_destroy (&sp->transport[0]);
mu_stream_destroy (&sp->transport[1]);
}
......@@ -710,24 +459,121 @@ _tls_error_string (struct _mu_stream *stream, int rc)
return mu_strerror (rc);
}
static void
free_conf (struct mu_tls_config *conf)
{
free (conf->cert_file);
free (conf->key_file);
free (conf->ca_file);
}
static int
_mu_tls_stream_create (mu_stream_t *pstream,
int (*openfn) (mu_stream_t stream),
mu_stream_t strin, mu_stream_t strout, int flags)
copy_conf (struct mu_tls_config *dst, struct mu_tls_config const *src)
{
if (src->cert_file)
{
dst->cert_file = strdup (src->cert_file);
if (!dst->cert_file)
return errno;
}
else
dst->cert_file = NULL;
if (src->key_file)
{
dst->key_file = strdup (src->key_file);
if (!dst->cert_file)
{
int rc = errno;
free (dst->cert_file);
return rc;
}
}
else
dst->key_file = NULL;
if (src->ca_file)
{
dst->ca_file = strdup (src->ca_file);
if (!dst->ca_file)
{
int rc = errno;
free (dst->cert_file);
free (dst->key_file);
return rc;
}
}
else
dst->ca_file = NULL;
return 0;
}
int
mu_tls_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout,
struct mu_tls_config const *conf,
enum mu_tls_type type,
int flags)
{
struct _mu_tls_stream *sp;
int rc;
mu_stream_t stream;
int session_type;
if (!pstream)
return MU_ERR_OUT_PTR_NULL;
if (!conf || !strin || !strout)
return EINVAL;
if (!mu_init_tls_libs ())
return ENOSYS;
switch (mu_tls_config_check (conf, 1))
{
case MU_TLS_CONFIG_OK:
case MU_TLS_CONFIG_NULL:
break;
case MU_TLS_CONFIG_UNSAFE:
return EACCES;
case MU_TLS_CONFIG_FAIL:
return ENOENT;
}
switch (type)
{
case MU_TLS_CLIENT:
session_type = GNUTLS_CLIENT;
break;
case MU_TLS_SERVER:
session_type = GNUTLS_SERVER;
break;
default:
return EINVAL;
}
sp = (struct _mu_tls_stream *)
_mu_stream_create (sizeof (*sp), MU_STREAM_RDWR);
if (!sp)
return ENOMEM;
sp->session_type = session_type;
sp->state = state_init;
sp->session = NULL;
sp->cred = NULL;
rc = copy_conf (&sp->conf, conf);
if (rc)
{
free (sp);
return rc;
}
sp->stream.read = _tls_read;
sp->stream.write = _tls_write;
sp->stream.flush = _tls_flush;
sp->stream.open = openfn;
sp->stream.open = _tls_open;
sp->stream.close = _tls_close;
sp->stream.done = _tls_done;
sp->stream.ctl = _tls_ioctl;
......@@ -736,134 +582,43 @@ _mu_tls_stream_create (mu_stream_t *pstream,
mu_stream_set_buffer (strin, mu_buffer_none, 0);
mu_stream_set_buffer (strout, mu_buffer_none, 0);
rc = _mu_tls_io_stream_create (&sp->transport[0], strin, MU_STREAM_READ, sp);
if (rc)
{
free (sp);
return rc;
}
rc = _mu_tls_io_stream_create (&sp->transport[1], strout, MU_STREAM_WRITE,
sp);
if (rc)
{
free (sp);
free (sp->transport[0]);
return rc;
}
stream = (mu_stream_t) sp;
rc = mu_tls_io_stream_create (&sp->transport[0], strin,
MU_STREAM_READ, sp);
if (rc)
goto err;
rc = mu_tls_io_stream_create (&sp->transport[1], strout,
MU_STREAM_WRITE, sp);
if (rc)
goto err;
mu_stream_set_buffer (stream, mu_buffer_line, 0);
rc = mu_stream_open (stream);
if (rc)
mu_stream_destroy (&stream);
else
*pstream = stream;
goto err;
*pstream = stream;
return 0;
err:
mu_stream_destroy (&stream);
return rc;
}
int
mu_tls_server_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout, int flags)
{
return _mu_tls_stream_create (pstream,
_tls_server_open,
strin, strout, flags);
}
int
mu_tls_client_stream_create (mu_stream_t *pstream,
mu_stream_t strin, mu_stream_t strout, int flags)
{
return _mu_tls_stream_create (pstream,
_tls_client_open,
strin, strout, flags);
}
static int
cb2_safety_checks (const char *name, void *data)
{
int defval;
if (data == &mu_tls_module_config.ssl_key_safety_checks)
defval = SSL_KEY_FILE_CHECKS;
else if (data == &mu_tls_module_config.ssl_cert_safety_checks)
defval = SSL_CERT_FILE_CHECKS;
else if (data == &mu_tls_module_config.ssl_cafile_safety_checks)
defval = SSL_CA_FILE_CHECKS;
else
{
mu_error (_("INTERNAL ERROR at %s:%d: unknown default value?"),
__FILE__, __LINE__);
defval = MU_FILE_SAFETY_ALL;
}
if (mu_file_safety_compose (data, name, defval))
mu_error (_("unknown keyword: %s"), name);
return 0;
}
struct mu_tls_config conf = {
.cert_file = NULL,
.key_file = NULL,
.ca_file = NULL,
.priorities = NULL
};
static int
cb_safety_checks (void *data, mu_config_value_t *arg)
{
return mu_cfg_string_value_cb (arg, cb2_safety_checks, data);
return mu_tls_stream_create (pstream, strin, strout, &conf, MU_TLS_CLIENT,
flags);
}
static struct mu_cfg_param mu_tls_param[] = {
{ "enable", mu_c_bool, &mu_tls_module_config.enable, 0, NULL,
N_("Enable TLS encryption.") },
{ "ssl-cert", mu_c_string, &mu_tls_module_config.ssl_cert, 0, NULL,
N_("Specify SSL certificate file."),
N_("file") },
{ "ssl-key", mu_c_string, &mu_tls_module_config.ssl_key, 0, NULL,
N_("Specify SSL certificate key file."),
N_("file") },
{ "ssl-cafile", mu_c_string, &mu_tls_module_config.ssl_cafile, 0, NULL,
N_("Specify trusted CAs file."),
N_("file") },
{ "ssl-priorities", mu_c_string, &mu_tls_module_config.priorities, 0, NULL,
N_("Set the priorities to use on the ciphers, key exchange methods, "
"macs and compression methods."),
NULL },
{ "key-file-safety-checks", mu_cfg_callback,
&mu_tls_module_config.ssl_key_safety_checks, 0,
cb_safety_checks,
N_("Configure safety checks for SSL key file. Argument is a list or "
"sequence of check names optionally prefixed with '+' to enable or "
"'-' to disable the corresponding check. Valid check names are:\n"
"\n"
" none disable all checks\n"
" all enable all checks\n"
" gwrfil forbid group writable files\n"
" awrfil forbid world writable files\n"
" grdfil forbid group readable files\n"
" ardfil forbid world writable files\n"
" linkwrdir forbid symbolic links in group or world writable directories\n"
" gwrdir forbid files in group writable directories\n"
" awrdir forbid files in world writable directories\n"),
N_("arg: list") },
{ "cert-file-safety-checks", mu_cfg_callback,
&mu_tls_module_config.ssl_cert_safety_checks, 0,
cb_safety_checks,
N_("Configure safety checks for SSL certificate. See above for a description of <arg>."),
N_("arg: list") },
{ "ca-file-safety-checks", mu_cfg_callback,
&mu_tls_module_config.ssl_cafile_safety_checks, 0,
cb_safety_checks,
N_("Configure safety checks for SSL certificate authority file. See above for a description of <arg>."),
N_("arg: list") },
{ NULL }
};
struct mu_auth_module mu_auth_tls_module = {
.name = "tls",
.cfg = mu_tls_param
};
#else
struct mu_auth_module mu_auth_tls_module = {
.name = "tls"
};
#endif /* WITH_TLS */
/* EOF */
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2003-2004, 2007-2012, 2014-2017 Free Software
Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <mailutils/tls.h>
#include <mailutils/nls.h>
#include <mailutils/error.h>
#include <mailutils/errno.h>
#include <mailutils/mu_auth.h>
struct safety_check_closure
{
int defval;
int *data;
};
static int
cb_safety_checks (const char *name, void *data)
{
struct safety_check_closure *cp = data;
if (mu_file_safety_compose (cp->data, name, cp->defval))
mu_error (_("unknown keyword: %s"), name);
return 0;
}
static int
cb_cert_safety_checks (void *data, mu_config_value_t *arg)
{
struct safety_check_closure clos;
clos.defval = MU_TLS_CERT_FILE_CHECKS;
clos.data = data;
return mu_cfg_string_value_cb (arg, cb_safety_checks, &clos);
}
static int
cb_key_safety_checks (void *data, mu_config_value_t *arg)
{
struct safety_check_closure clos;
clos.defval = MU_TLS_KEY_FILE_CHECKS;
clos.data = data;
return mu_cfg_string_value_cb (arg, cb_safety_checks, &clos);
}
static int
cb_ca_safety_checks (void *data, mu_config_value_t *arg)
{
struct safety_check_closure clos;
clos.defval = MU_TLS_CA_FILE_CHECKS;
clos.data = data;
return mu_cfg_string_value_cb (arg, cb_safety_checks, &clos);
}
static struct mu_cfg_param mu_tls_global_param[] = {
{ "key-file", mu_cfg_callback,
&mu_tls_key_file_checks, 0,
cb_key_safety_checks,
N_("Configure safety checks for SSL key file. Argument is a list or "
"sequence of check names optionally prefixed with '+' to enable or "
"'-' to disable the corresponding check. Valid check names are:\n"
"\n"
" none disable all checks\n"
" all enable all checks\n"
" gwrfil forbid group writable files\n"
" awrfil forbid world writable files\n"
" grdfil forbid group readable files\n"
" ardfil forbid world writable files\n"
" linkwrdir forbid symbolic links in group or world writable directories\n"
" gwrdir forbid files in group writable directories\n"
" awrdir forbid files in world writable directories\n"),
N_("arg: list") },
{ "cert-file", mu_cfg_callback,
&mu_tls_cert_file_checks, 0,
cb_cert_safety_checks,
N_("Configure safety checks for SSL certificate. See above for a description of <arg>."),
N_("arg: list") },
{ "ca-file", mu_cfg_callback,
&mu_tls_ca_file_checks, 0,
cb_ca_safety_checks,
N_("Configure safety checks for SSL certificate authority file. See above for a description of <arg>."),
N_("arg: list") },
{ NULL }
};
static struct mu_cfg_param tls_canned_param[] = {
{ "ssl-certificate-file", mu_c_string,
NULL, mu_offsetof(struct mu_tls_config, cert_file), NULL,
N_("Specify SSL certificate file."),
N_("file") },
{ "ssl-key-file", mu_c_string,
NULL, mu_offsetof(struct mu_tls_config, key_file), NULL,
N_("Specify SSL certificate key file."),
N_("file") },
{ "ssl-ca-file", mu_c_string,
NULL, mu_offsetof(struct mu_tls_config, ca_file), NULL,
N_("Specify trusted CAs file."),
N_("file") },
{ "ssl-priorities", mu_c_string,
NULL, mu_offsetof(struct mu_tls_config, priorities), NULL,
N_("Set the priorities to use on the ciphers, key exchange methods, "
"macs and compression methods."),
NULL },
{ NULL }
};
void
mu_tls_cfg_init (void)
{
struct mu_cfg_section *section;
if (mu_create_canned_section ("tls", &section))
abort ();
section->docstring = N_("Configure TLS");
section->label = NULL;
mu_cfg_section_add_params (section, tls_canned_param);
}
struct mu_auth_module mu_auth_tls_module = {
.name = "tls-file-checks",
.cfg = mu_tls_global_param,
};
/* TLS I/O streams */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <gnutls/gnutls.h>
#include <mailutils/stream.h>
#include <mailutils/errno.h>
#include <mailutils/property.h>
#include <mailutils/sys/tls-stream.h>
static int
_tls_io_close (mu_stream_t stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
return mu_stream_close (sp->transport);
}
static void
_tls_io_done (struct _mu_stream *stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
mu_stream_unref (sp->transport);
}
static int
_tls_io_flush (struct _mu_stream *stream)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
return mu_stream_flush (sp->transport);
}
static int
_tls_io_read (struct _mu_stream *stream, char *buf, size_t bufsize,
size_t *pnread)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc;
if (sp->up->state != state_open)
return EINVAL;
rc = gnutls_record_recv (sp->up->session, buf, bufsize);
if (rc >= 0)
{
*pnread = rc;
return 0;
}
sp->up->tls_err = rc;
return EIO;
}
static int
_tls_io_write (struct _mu_stream *stream, const char *buf, size_t bufsize,
size_t *pnwrite)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc;
if (sp->up->state != state_open)
return EINVAL;
/* gnutls_record_send() docs say:
If the EINTR is returned by the internal push function (write())
then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or
GNUTLS_E_AGAIN is returned you must call this function again, with the
same parameters. Otherwise the write operation will be
corrupted and the connection will be terminated. */
do
rc = gnutls_record_send (sp->up->session, buf, bufsize);
while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
if (rc < 0)
{
sp->up->tls_err = rc;
return EIO;
}
*pnwrite = rc;
return 0;
}
static int
_tls_rd_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc = EINVAL;
if (*pflags == MU_STREAM_READY_RD)
rc = mu_stream_wait (sp->transport, pflags, tvp);
return rc;
}
static int
_tls_wr_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
int rc = EINVAL;
if (*pflags == MU_STREAM_READY_WR)
rc = mu_stream_wait (sp->transport, pflags, tvp);
return rc;
}
static int
get_cipher_info (gnutls_session_t session, mu_property_t *pprop)
{
mu_property_t prop;
const char *s;
int rc;
if (!pprop)
return EINVAL;
rc = mu_property_create_init (&prop, mu_assoc_property_init, NULL);
if (rc)
return rc;
s = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
mu_property_set_value (prop, "protocol", s, 1);
s = gnutls_cipher_get_name (gnutls_cipher_get (session));
mu_property_set_value (prop, "cipher", s, 1);
s = gnutls_mac_get_name (gnutls_mac_get (session));
mu_property_set_value (prop, "mac", s, 1);
*pprop = prop;
return 0;
}
static int
_tls_io_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
{
struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
switch (code)
{
case MU_IOCTL_TRANSPORT:
if (!arg)
return EINVAL;
else
{
mu_transport_t *ptrans = arg;
switch (opcode)
{
case MU_IOCTL_OP_GET:
ptrans[0] = (mu_transport_t) sp->transport;
ptrans[1] = NULL;
break;
case MU_IOCTL_OP_SET:
return ENOSYS;
default:
return EINVAL;
}
}
break;
case MU_IOCTL_TLSSTREAM:
switch (opcode)
{
case MU_IOCTL_TLS_GET_CIPHER_INFO:
return get_cipher_info (sp->up->session, arg);
default:
return EINVAL;
}
break;
default:
return ENOSYS;
}
return 0;
}
int
mu_tls_io_stream_create (mu_stream_t *pstream,
mu_stream_t transport, int flags,
struct _mu_tls_stream *master)
{
struct _mu_tls_io_stream *sp;
sp = (struct _mu_tls_io_stream *)
_mu_stream_create (sizeof (*sp), (flags & MU_STREAM_RDWR) | _MU_STR_OPEN);
if (!sp)
return ENOMEM;
if (flags & MU_STREAM_READ)
{
sp->stream.read = _tls_io_read;
sp->stream.wait = _tls_rd_wait;
mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_full, 0);
}
else
{
sp->stream.write = _tls_io_write;
sp->stream.wait = _tls_wr_wait;
mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 0);
}
sp->stream.flush = _tls_io_flush;
sp->stream.close = _tls_io_close;
sp->stream.done = _tls_io_done;
sp->stream.ctl = _tls_io_ioctl;
mu_stream_ref (transport);
sp->transport = transport;
sp->up = master;
*pstream = (mu_stream_t) sp;
return 0;
}
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2003-2004, 2007-2012, 2014-2017 Free Software
Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/tls.h>
#include <mailutils/errno.h>
int mu_tls_enable = 0;
int mu_tls_cert_file_checks = MU_TLS_CERT_FILE_CHECKS;
int mu_tls_key_file_checks = MU_TLS_KEY_FILE_CHECKS;
int mu_tls_ca_file_checks = MU_TLS_CA_FILE_CHECKS;
static int
check_err(int rc)
{
switch (rc)
{
case MU_ERR_PERM_OWNER_MISMATCH:
case MU_ERR_PERM_GROUP_WRITABLE:
case MU_ERR_PERM_WORLD_WRITABLE:
case MU_ERR_PERM_GROUP_READABLE:
case MU_ERR_PERM_WORLD_READABLE:
case MU_ERR_PERM_LINKED_WRDIR:
case MU_ERR_PERM_DIR_IWGRP:
case MU_ERR_PERM_DIR_IWOTH:
return MU_TLS_CONFIG_UNSAFE;
default:
return MU_TLS_CONFIG_FAIL;
}
}
int
mu_tls_config_check (struct mu_tls_config const *conf, int verbose)
{
int rc;
int res = MU_TLS_CONFIG_NULL;
if (conf->cert_file)
{
rc = mu_file_safety_check (conf->cert_file, mu_tls_cert_file_checks,
-1, NULL);
if (rc)
{
if (verbose)
mu_error ("%s: %s", conf->cert_file, mu_strerror (rc));
return check_err (rc);
}
res = MU_TLS_CONFIG_OK;
}
if (conf->key_file)
{
rc = mu_file_safety_check (conf->key_file, mu_tls_key_file_checks,
-1, NULL);
if (rc)
{
if (verbose)
mu_error ("%s: %s", conf->key_file, mu_strerror (rc));
return check_err (rc);
}
res = MU_TLS_CONFIG_OK;
}
if (conf->ca_file)
{
rc = mu_file_safety_check (conf->ca_file, mu_tls_ca_file_checks,
-1, NULL);
if (rc)
{
if (verbose)
mu_error ("%s: %s", conf->ca_file, mu_strerror (rc));
return check_err (rc);
}
res = MU_TLS_CONFIG_OK;
}
if (conf->priorities)
res = MU_TLS_CONFIG_OK;
return res;
}
......@@ -104,17 +104,20 @@ capa_implementation (const char *name, struct pop3d_session *session)
pop3d_outf ("%s %s\n", name, PACKAGE_STRING);
}
#ifdef WITH_TLS
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);
switch (session->tls_mode)
{
case tls_ondemand:
case tls_required:
pop3d_outf ("%s\n", name);
break;
default:
break;
}
}
#else
# define capa_stls NULL
#endif /* WITH_TLS */
static void
capa_user (const char *name, struct pop3d_session *session)
......
......@@ -16,13 +16,12 @@
#include "pop3d.h"
struct mu_tls_config global_tls_conf;
static struct pop3d_command command_table[] = {
#ifdef WITH_TLS
{ "STLS", pop3d_stls },
# define COMMAND_TABLE_HEAD 1
#else
# define COMMAND_TABLE_HEAD 0
#endif
#define COMMAND_TABLE_STLS 0
#define COMMAND_TABLE_PLAIN 1
{ "RETR", pop3d_retr },
{ "DELE", pop3d_dele },
{ "USER", pop3d_user },
......@@ -39,8 +38,7 @@ static struct pop3d_command command_table[] = {
{ NULL }
};
static struct pop3d_command *command_table_head =
command_table + COMMAND_TABLE_HEAD;
static struct pop3d_command *command_table_head;
pop3d_command_handler_t
pop3d_find_command (const char *name)
......@@ -54,57 +52,130 @@ pop3d_find_command (const char *name)
return p->handler;
}
#ifdef WITH_TLS
void
enable_stls ()
int
stls_preflight (mu_m_server_t msrv)
{
command_table_head = command_table;
}
#endif
mu_list_t srvlist;
mu_iterator_t itr;
int errors = 0;
int tls_ok = mu_init_tls_libs ();
int tls_requested = 0;
int global_conf_status = 0;
struct error_table
{
int code;
const char *text;
};
if (global_tls_conf.cert_file)
global_conf_status = mu_tls_config_check (&global_tls_conf, 1);
else
global_conf_status = MU_TLS_CONFIG_NULL;
mu_m_server_get_srvlist (msrv, &srvlist);
mu_list_get_iterator (srvlist, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
{
mu_ip_server_t ipsrv;
struct pop3d_srv_config *cfg;
mu_iterator_current (itr, (void**) &ipsrv);
cfg = mu_ip_server_get_data (ipsrv);
switch (cfg->tls_mode)
{
case tls_unspecified:
if (cfg->tls_conf.cert_file)
{
cfg->tls_mode = tls_ondemand;
break;
}
else
cfg->tls_mode = tls_no;
/* fall through */
case tls_no:
continue;
default:
break;
}
switch (mu_tls_config_check (&cfg->tls_conf, 1))
{
case MU_TLS_CONFIG_OK:
if (!cfg->tls_conf.cert_file)
{
mu_error (_("server %s: no certificate set"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
break;
case MU_TLS_CONFIG_NULL:
if (global_conf_status != MU_TLS_CONFIG_NULL)
{
cfg->tls_conf = global_tls_conf;
}
else
{
mu_error (_("server %s: no certificate set"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
break;
default:
mu_error (_("server %s: TLS configuration failed"),
mu_ip_server_addrstr (ipsrv));
errors = 1;
}
tls_requested = 1;
}
mu_iterator_destroy (&itr);
if (tls_requested && !tls_ok)
{
mu_error (_("TLS is not configured, but requested in the "
"configuration"));
errors = 1;
}
if (errors)
return 1;
if (tls_requested)
command_table_head = command_table + COMMAND_TABLE_STLS;
else
command_table_head = command_table + COMMAND_TABLE_PLAIN;
return 0;
}
static struct error_table error_table[] = {
{ ERR_WRONG_STATE, "Incorrect state" },
{ ERR_BAD_ARGS, "Invalid arguments" },
{ ERR_BAD_LOGIN, "[AUTH] Bad login" },
{ ERR_NO_MESG, "No such message" },
{ ERR_MESG_DELE, "Message has been deleted" },
{ ERR_NOT_IMPL, "Not implemented" },
{ ERR_BAD_CMD, "Invalid command" },
{ ERR_MBOX_LOCK, "[IN-USE] Mailbox in use" },
{ ERR_TOO_LONG, "Argument too long" },
{ ERR_NO_MEM, "Out of memory, quitting" },
{ ERR_SIGNAL, "Quitting on signal" },
{ ERR_FILE, "Some deleted messages not removed" },
{ ERR_NO_IFILE, "No input stream" },
{ ERR_NO_OFILE, "No output stream" },
{ ERR_IO, "I/O error" },
{ ERR_PROTO, "Remote protocol error" },
{ ERR_TIMEOUT, "Session timed out" },
{ ERR_UNKNOWN, "Unknown error" },
{ ERR_MBOX_SYNC, "Mailbox was updated by other process" },
#ifdef WITH_TLS
{ ERR_TLS_ACTIVE, "Command not permitted when TLS active" },
#endif /* WITH_TLS */
{ ERR_TLS_IO, "TLS I/O error" },
{ ERR_LOGIN_DELAY,
"[LOGIN-DELAY] Attempt to log in within the minimum login delay interval" },
{ ERR_TERMINATE, "Terminating on request" },
{ ERR_SYS_LOGIN, "[SYS/PERM] Cannot authenticate" },
{ 0 }
static char *error_table[] = {
[ERR_WRONG_STATE] = "Incorrect state",
[ERR_BAD_ARGS] = "Invalid arguments",
[ERR_BAD_LOGIN] = "[AUTH] Bad login",
[ERR_NO_MESG] = "No such message",
[ERR_MESG_DELE] = "Message has been deleted",
[ERR_NOT_IMPL] = "Not implemented",
[ERR_BAD_CMD] = "Invalid command",
[ERR_MBOX_LOCK] = "[IN-USE] Mailbox in use",
[ERR_TOO_LONG] = "Argument too long",
[ERR_NO_MEM] = "Out of memory] =quitting",
[ERR_SIGNAL] = "Quitting on signal",
[ERR_FILE] = "Some deleted messages not removed",
[ERR_NO_IFILE] = "No input stream",
[ERR_NO_OFILE] = "No output stream",
[ERR_IO] = "I/O error",
[ERR_PROTO] = "Remote protocol error",
[ERR_TIMEOUT] = "Session timed out",
[ERR_UNKNOWN] = "Unknown error",
[ERR_MBOX_SYNC] = "Mailbox was updated by other process",
[ERR_TLS_IO] = "TLS I/O error",
[ERR_LOGIN_DELAY] =
"[LOGIN-DELAY] Attempt to log in within the minimum login delay interval",
[ERR_TERMINATE] = "Terminating on request",
[ERR_SYS_LOGIN] = "[SYS/PERM] Cannot authenticate",
};
const char *
pop3d_error_string (int code)
{
struct error_table *ep;
for (ep = error_table; ep->code != 0; ep++)
if (ep->code == code)
return ep->text;
if (code >= 0 && code < MU_ARRAY_SIZE (error_table) && error_table[code])
return error_table[code];
return "unknown error";
}
......
......@@ -158,7 +158,7 @@ log_cipher (mu_stream_t stream)
}
void
pop3d_setio (int ifd, int ofd, int tls)
pop3d_setio (int ifd, int ofd, struct mu_tls_config *tls_conf)
{
mu_stream_t str, istream, ostream;
......@@ -175,10 +175,12 @@ pop3d_setio (int ifd, int ofd, int tls)
pop3d_abquit (ERR_NO_OFILE);
/* Combine the two streams into an I/O one. */
#ifdef WITH_TLS
if (tls)
if (tls_conf)
{
int rc = mu_tls_server_stream_create (&str, istream, ostream, 0);
int rc = mu_tls_stream_create (&str, istream, ostream,
tls_conf,
MU_TLS_SERVER,
0);
if (rc)
{
mu_stream_unref (istream);
......@@ -186,12 +188,9 @@ pop3d_setio (int ifd, int ofd, int tls)
mu_error (_("failed to create TLS stream: %s"), mu_strerror (rc));
pop3d_abquit (ERR_FILE);
}
tls_done = 1;
log_cipher (str);
}
else
#endif
if (mu_iostream_create (&str, istream, ostream))
else if (mu_iostream_create (&str, istream, ostream))
pop3d_abquit (ERR_FILE);
/* Convert all writes to CRLF form.
......@@ -228,9 +227,8 @@ pop3d_setio (int ifd, int ofd, int tls)
}
}
#ifdef WITH_TLS
int
pop3d_init_tls_server ()
pop3d_init_tls_server (struct mu_tls_config *tls_conf)
{
mu_stream_t tlsstream, stream[2];
int rc;
......@@ -243,7 +241,10 @@ pop3d_init_tls_server ()
return 1;
}
rc = mu_tls_server_stream_create (&tlsstream, stream[0], stream[1], 0);
rc = mu_tls_stream_create (&tlsstream, stream[0], stream[1],
tls_conf,
MU_TLS_SERVER,
0);
mu_stream_unref (stream[0]);
mu_stream_unref (stream[1]);
if (rc)
......@@ -263,7 +264,6 @@ pop3d_init_tls_server ()
}
return 0;
}
#endif
void
pop3d_bye ()
......
......@@ -37,15 +37,6 @@ 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;
#else
# define tls_available 0
#endif /* WITH_TLS */
int initial_state = AUTHORIZATION;
/* Should all the messages be undeleted on startup */
......@@ -169,13 +160,6 @@ cb_bulletin_db (void *data, mu_config_value_t *val)
}
#endif
struct pop3d_srv_config
{
struct mu_srv_config m_cfg;
enum tls_mode tls_mode;
};
#ifdef WITH_TLS
static int
cb_tls (void *data, mu_config_value_t *val)
{
......@@ -205,39 +189,14 @@ cb_tls (void *data, mu_config_value_t *val)
return 0;
}
static int
cb_tls_required (void *data, mu_config_value_t *val)
{
int bv;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
if (mu_str_to_c (val->v.string, mu_c_bool, &bv, NULL))
mu_error (_("Not a boolean value"));
else if (bv)
{
tls_mode = tls_required;
mu_diag_output (MU_DIAG_WARNING,
"the \"tls-required\" statement is deprecated, "
"use \"tls required\" instead");
}
else
mu_diag_output (MU_DIAG_WARNING,
"the \"tls-required\" statement is deprecated, "
"use \"tls\" instead");
return 0;
}
#endif
static struct mu_cfg_param pop3d_srv_param[] = {
#ifdef WITH_TLS
{ "tls", mu_cfg_callback,
{ "tls-mode", mu_cfg_callback,
NULL, mu_offsetof (struct pop3d_srv_config, tls_mode), cb_tls,
N_("Kind of TLS encryption to use for this server"),
/* TRANSLATORS: words to the right of : are keywords - do not translate */
N_("arg: false|true|ondemand|stls|requred|connection") },
#endif
{ "tls", mu_cfg_section,
NULL, mu_offsetof (struct pop3d_srv_config, tls_conf) },
{ NULL }
};
......@@ -272,16 +231,8 @@ static struct mu_cfg_param pop3d_cfg_param[] = {
" awrdir forbid files in world writable directories\n"),
N_("arg: list") },
#ifdef WITH_TLS
{ "tls", mu_cfg_callback, &tls_mode, 0, cb_tls,
N_("Kind of TLS encryption to use"),
/* TRANSLATORS: words to the right of : are keywords - do not translate */
N_("arg: false|true|ondemand|stls|requred|connection") },
{ "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."),
N_("arg: bool") },
#endif
{ "tls", mu_cfg_section, &global_tls_conf },
#ifdef ENABLE_LOGIN_DELAY
{ "login-delay", mu_c_time, &login_delay, 0, NULL,
N_("Set the minimal allowed delay between two successive logins.") },
......@@ -355,7 +306,7 @@ pop3d_get_client_address (int fd, struct sockaddr_in *pcs)
ofd -- output descriptor
tls -- initiate encrypted connection */
int
pop3d_mainloop (int ifd, int ofd, enum tls_mode tls)
pop3d_mainloop (int ifd, int ofd, struct pop3d_srv_config *cfg)
{
int status = OK;
char buffer[512];
......@@ -365,24 +316,18 @@ pop3d_mainloop (int ifd, int ofd, enum tls_mode tls)
mu_set_signals (pop3d_child_signal, sigtab, MU_ARRAY_SIZE (sigtab));
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);
pop3d_setio (ifd, ofd,
cfg->tls_mode == tls_connection ? &cfg->tls_conf : NULL);
if (tls == tls_required)
if (cfg->tls_mode == tls_required)
initial_state = INITIAL;
state = tls == tls_connection ? AUTHORIZATION : initial_state;
state = cfg->tls_mode == tls_connection ? AUTHORIZATION : initial_state;
pop3d_session_init (&session);
session.tls = tls;
session.tls_mode = cfg->tls_mode;
session.tls_conf = &cfg->tls_conf;
/* FIXME: state should also be in the session? */
/* Prepare the shared secret for APOP. */
......@@ -528,8 +473,7 @@ pop3d_connection (int fd, struct sockaddr *sa, int salen,
else
rc = 1;
pop3d_mainloop (fd, fd,
cfg->tls_mode == tls_unspecified ? tls_mode : cfg->tls_mode);
pop3d_mainloop (fd, fd, cfg);
if (rc == 0)
clr_strerr_flt ();
......@@ -569,6 +513,7 @@ main (int argc, char **argv)
mu_tcpwrapper_cfg_init ();
manlock_cfg_init ();
mu_acl_cfg_init ();
mu_tls_cfg_init ();
mu_m_server_create (&server, program_version);
mu_m_server_set_config_size (server, sizeof (struct pop3d_srv_config));
......@@ -645,11 +590,7 @@ main (int argc, char **argv)
umask (S_IROTH | S_IWOTH | S_IXOTH); /* 007 */
/* Check TLS environment, i.e. cert and key files */
#ifdef WITH_TLS
tls_available = mu_check_tls_environment ();
if (tls_available)
enable_stls ();
#endif /* WITH_TLS */
mu_m_server_set_preflight (server, stls_preflight);
/* Actually run the daemon. */
if (mu_m_server_mode (server) == MODE_DAEMON)
......@@ -661,9 +602,12 @@ main (int argc, char **argv)
}
else
{
struct pop3d_srv_config cfg;
memset (&cfg, 0, sizeof cfg);
cfg.tls_mode = tls_no;
/* Make sure we are in the root directory. */
chdir ("/");
status = pop3d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, tls_mode);
status = pop3d_mainloop (MU_STDIN_FD, MU_STDOUT_FD, &cfg);
}
if (status)
......
......@@ -145,31 +145,32 @@ extern int expire_on_exit;
#define UPDATE 2
#define ABORT 3
#define OK 0
#define ERR_WRONG_STATE 1
#define ERR_BAD_ARGS 2
#define ERR_BAD_LOGIN 3
#define ERR_NO_MESG 4
#define ERR_MESG_DELE 5
#define ERR_NOT_IMPL 6
#define ERR_BAD_CMD 7
#define ERR_MBOX_LOCK 8
#define ERR_TOO_LONG 9
#define ERR_NO_MEM 10
#define ERR_SIGNAL 11
#define ERR_FILE 12
#define ERR_NO_IFILE 13
#define ERR_NO_OFILE 14
#define ERR_IO 15
#define ERR_PROTO 16
#define ERR_TIMEOUT 17
#define ERR_UNKNOWN 18
#define ERR_MBOX_SYNC 19
#define ERR_TLS_ACTIVE 20
#define ERR_TLS_IO 21
#define ERR_LOGIN_DELAY 22
#define ERR_TERMINATE 23
#define ERR_SYS_LOGIN 24
enum pop3d_error {
OK = 0,
ERR_WRONG_STATE,
ERR_BAD_ARGS,
ERR_BAD_LOGIN,
ERR_NO_MESG,
ERR_MESG_DELE,
ERR_NOT_IMPL,
ERR_BAD_CMD,
ERR_MBOX_LOCK,
ERR_TOO_LONG,
ERR_NO_MEM,
ERR_SIGNAL,
ERR_FILE,
ERR_NO_IFILE,
ERR_NO_OFILE,
ERR_IO,
ERR_PROTO,
ERR_TIMEOUT,
ERR_UNKNOWN,
ERR_MBOX_SYNC,
ERR_TLS_IO,
ERR_LOGIN_DELAY,
ERR_TERMINATE,
ERR_SYS_LOGIN
};
enum tls_mode
{
......@@ -200,7 +201,15 @@ struct pop3d_capa
struct pop3d_session
{
mu_list_t capa;
enum tls_mode tls;
enum tls_mode tls_mode;
struct mu_tls_config *tls_conf;
};
struct pop3d_srv_config
{
struct mu_srv_config m_cfg;
enum tls_mode tls_mode;
struct mu_tls_config tls_conf;
};
void pop3d_session_init (struct pop3d_session *session);
......@@ -227,10 +236,7 @@ extern char *md5shared;
extern size_t children;
extern struct daemon_param daemon_param;
extern int debug_mode;
#ifdef WITH_TLS
extern int tls_available;
extern int tls_done;
#endif /* WITH_TLS */
extern int undelete_on_startup;
extern struct mu_auth_data *auth_data;
extern unsigned int idle_timeout;
......@@ -242,6 +248,8 @@ extern int apop_database_safety;
extern uid_t apop_database_owner;
extern int apop_database_owner_set;
extern struct mu_tls_config global_tls_conf;
/* Safety checks for group-rw database files, such as stat and bulletin
databases */
......@@ -282,13 +290,12 @@ extern void pop3d_parse_command (char *cmd, char **pcmd, char **parg);
extern RETSIGTYPE pop3d_master_signal (int);
extern RETSIGTYPE pop3d_child_signal (int);
#ifdef WITH_TLS
extern int pop3d_stls (char *, struct pop3d_session *);
extern void enable_stls (void);
#endif /* WITH_TLS */
int stls_preflight (mu_m_server_t msrv);
extern void pop3d_outf (const char *fmt, ...) MU_PRINTFLIKE(1,2);
extern void pop3d_setio (int, int, int);
extern void pop3d_setio (int, int, struct mu_tls_config *);
extern char *pop3d_readline (char *, size_t);
extern void pop3d_flush_output (void);
......@@ -299,10 +306,7 @@ extern int pop3d_is_deleted (mu_attribute_t attr);
extern void pop3d_unset_deleted (mu_attribute_t attr);
void pop3d_undelete_all (void);
#ifdef WITH_TLS
extern int pop3d_init_tls_server (void);
extern void pop3d_deinit_tls_server (void);
#endif /* WITH_TLS */
extern int pop3d_init_tls_server (struct mu_tls_config *tls_conf);
extern void pop3d_mark_retr (mu_attribute_t attr);
extern int pop3d_is_retr (mu_attribute_t attr);
......
......@@ -19,8 +19,6 @@
/* STLS command -- TLS/SSL encryption */
#ifdef WITH_TLS
int
pop3d_stls (char *arg, struct pop3d_session *session)
{
......@@ -30,7 +28,7 @@ pop3d_stls (char *arg, struct pop3d_session *session)
if (state != initial_state)
return ERR_WRONG_STATE;
switch (session->tls)
switch (session->tls_mode)
{
case tls_ondemand:
case tls_required:
......@@ -39,26 +37,20 @@ pop3d_stls (char *arg, struct pop3d_session *session)
return ERR_WRONG_STATE;
}
if (tls_done)
return ERR_TLS_ACTIVE;
pop3d_outf ("+OK Begin TLS negotiation\n");
pop3d_flush_output ();
tls_done = pop3d_init_tls_server () == 0;
if (!tls_done)
if (pop3d_init_tls_server (session->tls_conf))
{
mu_diag_output (MU_DIAG_ERROR, _("Session terminated"));
state = ABORT;
return ERR_UNKNOWN;
}
session->tls_mode = tls_no;
state = AUTHORIZATION; /* Confirm we're in this state. Necessary for
"tls required" to work */
return OK;
}
#endif /* WITH_TLS */
......