Commit 11637b0f 11637b0f262db62b4dc466cefb9315098a1a995a by Sergey Poznyakoff

New maidag mode: --url

* NEWS: Update.
* doc/texinfo/libmuauth.texi: Update.
* doc/texinfo/mailutils.texi: Update.
* doc/texinfo/programs.texi: Update.

* include/mailutils/mailbox.h (mu_mailbox_create_from_url): New
function.
* include/mailutils/mutil.h (mu_aget_user_email_domain): New
function.
* libproto/include/address0.h: Remove obsolete comment.
* libproto/remote/mbox.c (remote_mbox_append_message): If
recipient address is not given, try to construct it from the URL.
* mailbox/mailbox.c (mu_mailbox_create_from_url): New function.
* mailbox/mutil.c (mu_aget_user_email_domain): New function.

* maidag/deliver.c (deliver_to_user): Mailbox and auth must be
freed by the caller.
(deliver_url): New function.
(deliver): Rewrite to allow for delivering to mailboxes explicitly
specified by URLs (--url command line option).
* maidag/maidag.c (options): New option --url.
(main): Handle --url.
* maidag/maidag.h (url_option): New global.
* maidag/mailquota.c (check_quota): Return if auth == NULL.
1 parent 9106fc88
2008-10-19 Sergey Poznyakoff <gray@gnu.org.ua>
* NEWS: Update.
* doc/texinfo/libmuauth.texi: Update.
* doc/texinfo/mailutils.texi: Update.
* doc/texinfo/programs.texi: Update.
* include/mailutils/mailbox.h (mu_mailbox_create_from_url): New
function.
* include/mailutils/mutil.h (mu_aget_user_email_domain): New
function.
* libproto/include/address0.h: Remove obsolete comment.
* libproto/remote/mbox.c (remote_mbox_append_message): If
recipient address is not given, try to construct it from the URL.
* mailbox/mailbox.c (mu_mailbox_create_from_url): New function.
* mailbox/mutil.c (mu_aget_user_email_domain): New function.
* maidag/deliver.c (deliver_to_user): Mailbox and auth must be
freed by the caller.
(deliver_url): New function.
(deliver): Rewrite to allow for delivering to mailboxes explicitly
specified by URLs (--url command line option).
* maidag/maidag.c (options): New option --url.
(main): Handle --url.
* maidag/maidag.h (url_option): New global.
* maidag/mailquota.c (check_quota): Return if auth == NULL.
2008-10-17 Sergey Poznyakoff <gray@gnu.org.ua>
* include/mailutils/libsieve.h (mu_sieve_match_part_checker): New
......
GNU mailutils NEWS -- history of user-visible changes. 2008-08-20
GNU mailutils NEWS -- history of user-visible changes. 2008-10-16
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
2008 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -124,6 +124,8 @@ PREAUTH mode:
* Libraries
** Support for ESMTP SIZE extension (RFC 1870).
** Diagnostic and debugging functions essentially rewritten.
A set of debugging macros, MU_DEBUG0 through MU_DEBUG11, is provided.
......
......@@ -8,9 +8,9 @@ The functions from @file{libmailbox} library get user information from
the system user database. The library @file{libmuauth} extends this
functionality, allowing @file{libmailbox} functions to obtain
information about a user from several places, like @sc{sql} database,
etc. The method used is described in detail in @ref{authentication}.
This chapter contains a very succinct description of the underlying
library mechanism.
etc. The method used is described in detail in @ref{Auth Statement,
authentication}. This chapter contains a very succinct description of
the underlying library mechanism.
@menu
* Data Types::
......
......@@ -112,10 +112,15 @@ Indices
@detailmenu
--- The Detailed Node Listing ---
Introduction
* Book Contents:: What this Book Contains
* History:: A bit of History
Mailutils Programs
* command line:: Command Line Syntax.
* configuration:: Common Configuration File.
* authentication:: Authorization and Authentication Principles.
* frm and from:: List Headers from a Mailbox.
* mail:: Send and Receive Mail.
......@@ -139,6 +144,41 @@ Mailutils Programs
* mailutils-config:: Get the Information about the Mailutils Build.
Command Line
* Option Basics:: Basic Notions About Command Line Options.
* Common Options:: Options That are Common for All Utilities.
Mailutils Configuration File
* conf-syntax:: Configuration File Syntax
* Include:: Include Statement
* Logging Statement::
* Debug Statement::
* Mailbox Statement::
* Locking Statement::
* ACL Statement::
* Tcp-wrappers Statement::
* Server Settings::
* Auth Statement::
* PAM Statement::
* Virtdomain Statement::
* Radius Statement::
* SQL Statement::
* LDAP Statement::
* TLS Statement::
* GSASL Statement::
Configuration File Syntax
* Comments::
* Statements::
* Block Statements::
Server Settings
* General Server Configuration::
* Server Statement::
@command{mail} --- Send and Receive Mail
......
......@@ -22,7 +22,6 @@ syntax.
@menu
* command line:: Command Line Syntax.
* configuration:: Common Configuration File.
* authentication:: Authorization and Authentication Principles.
* frm and from:: List Headers from a Mailbox.
* mail:: Send and Receive Mail.
......@@ -1474,7 +1473,6 @@ and is denied if any one of them denies it.
@node Auth Statement
@subsection Auth Statement
@UNREVISED
@cindex authorization
@cindex authentication
@kwindex auth
......@@ -1534,6 +1532,12 @@ e.g. if the package was compiled without @acronym{sql} support then
the module @samp{sql} in the above example will always fail, thus
passing the execution on to the next module.
The @code{auth} statement configures authentication and authorization.
@deffn {Configuration} authorization @var{module-list}
Define a sequence of modules to use for authorization. Modules will
be tried in the same order as listed in @var{module-list}.
The modules available for use in authorization list are:
@table @asis
......@@ -1549,6 +1553,20 @@ User credentials are retrieved from a ``virtual domain'' user
database.
@end table
Unless overridden by @code{authorization} statement,
the default list of authorization modules is:
@smallexample
(system, sql, virtdomains)
@end smallexample
@end deffn
@deffn {Configuration} authentication @var{module-list}
Define a sequence of modules to use for authentication. Modules will
be tried in the same order as listed in @var{module-list}.
The following table lists modules available for use in @var{module-list}:
@table @asis
@item generic
The generic authentication type. User password is hashed and compared
......@@ -1572,19 +1590,10 @@ the list of authentication modules is:
@smallexample
(generic, system, pam, sql)
@end smallexample
@noindent
Unless overridden by @code{authorization} statement,
the list of authorization modules is:
@smallexample
(system, sql, virtdomains)
@end smallexample
@end deffn
@node PAM Statement
@subsection PAM Statement
@UNREVISED
@kwindex pam
@subheading Syntax
@smallexample
......@@ -1594,6 +1603,18 @@ pam @{
@}
@end smallexample
@subheading Description
The @code{pam} statement configures @acronym{PAM} authentication. It
contains a single sub-statement:
@deffn {Configuration} service @var{text}
Define service name to look for in @acronym{PAM} configuration. By
default, the base name of the Mailutils binary is used.
@end deffn
This statement takes effect only if @samp{pam} is listed in
@code{authentication} statement (@pxref{Auth Statement}).
@node Virtdomain Statement
@subsection Virtdomain Statement
@UNREVISED
......@@ -1606,6 +1627,10 @@ virtdomain @{
@}
@end smallexample
@subheading Description
The @code{virtdomain} statement configures virtual mail domain
database. @dfn{Virtual mail domain}...@FIXME
@node Radius Statement
@subsection Radius Statement
@UNREVISED
......@@ -1728,108 +1753,7 @@ gsasl @{
@c -------------------------------------------------------------------
@node authentication
@section Authorization and Authentication Principles
@UNREVISED
@cindex authorization
@cindex authentication
Some mail utilities provide access to their services only after
verifying that the user is actually the person he is claiming
to be. Such programs are, for example, @command{pop3d} and
@command{imap4d}. The process of the verification is broken
down into two stages: @dfn{authorization} and @dfn{authentication}.
In @dfn{authorization} stage the program retrieves the information
about a particular user. In @dfn{authentication} stage, this information
is compared against the user-supplied credentials. Only if both stages
succeed is the user allowed to use the service.
A set of @dfn{modules} is involved in performing each stage. For
example, the authorization stage can retrieve the user description
from various sources: system database, sql database, virtual domain
table, etc. Each module is responsible for retrieving the description
from a particular source of information. The modules are arranged in
a @dfn{module list}. The modules from the list are invoked in turn, until
either a one of them succeeds or the list is exhausted. In latter case
the authorization fails. Otherwise the data returned by the succeeded
module are used in authentication.
Similarly, authentication may be performed in several ways. The
authentication modules are also grouped in a list. Each module
is tried in turn until either a module succeeds, in which case the
authentication succeeds, or the end of the list is reached.
We represent the module lists as column-separated lists of module
names. For example, the authorization list
@smallexample
system:sql:virtdomains
@end smallexample
@noindent
means that first the system user database (@file{/etc/password}) is
searched for a description of a user in question. If the search fails,
the @acronym{sql} database is searched. Finally, if it also fails, the
search is performed in the virtual domain database.
@emph{Note}, that some authentication and/or authorization modules may
be disabled when configuring the package before compilation. The names
of the disabled modules are nevertheless available for use in runtime
configuration options, but they represent a ``fail-only'' functionality,
e.g. if the package was compiled without @acronym{sql} support then the
module @samp{sql} in the above example will always fail, thus passing
the execution on to the next module.
The modules available for use in authorization list are:
@table @asis
@item system
User credentials are retrieved from the system user database
(@file{/etc/password}).
@item sql
User credentials are retrieved from the @acronym{sql} database. The set
of @option{--sql-} options (@FIXME-pxref{auth}) is used to configure
access to the database.
@item virtdomain
User credentials are retrieved from a ``virtual domain'' user
database.
@end table
The modules available for use in authentication list are:
@table @asis
@item generic
The generic authentication type. User password is hashed and compared
against the hash value returned in authorization stage.
@item system
The hashed value of the user password is retrieved from
@file{/etc/shadow} file on systems that support it.
@item sql
The hashed value of the user password is retrieved from the @acronym{sql}
database using query supplied by @option{--sql-getpass} option
(@FIXME-pxref{auth}).
@item pam
The user is authenticated via pluggable authentication module
(@acronym{pam}). The @acronym{pam} service name to be used is
configured via @option{--pam-service} option (@FIXME-pxref{auth})
@end table
Unless overridden by @option{--authentication} command line option,
the list of authentication modules is:
@smallexample
generic:system:pam:sql
@end smallexample
@noindent
Unless overridden by @option{--authorization} command line option,
the list of authorization modules is:
@smallexample
system:sql:virtdomains
@end smallexample
@page
@node frm and from
@section @command{frm} and @command{from} --- List Headers from a Mailbox
......
......@@ -41,6 +41,8 @@ const char *mu_mailbox_get_default_proto (void);
/* Constructor/destructor and possible types. */
extern int mu_mailbox_create (mu_mailbox_t *, const char *);
extern int mu_mailbox_create_from_url (mu_mailbox_t *, mu_url_t);
extern void mu_mailbox_destroy (mu_mailbox_t *);
extern int mu_mailbox_create_default (mu_mailbox_t *, const char *);
......
......@@ -88,6 +88,9 @@ extern int mu_set_user_email_domain (const char *domain);
/* Return the currently set user email domain, or NULL if not set. */
extern int mu_get_user_email_domain (const char** domain);
/* Same, but allocates memory */
extern int mu_aget_user_email_domain (char **pdomain);
/*
* Get the default email address for user name. A NULL name is taken
* to mean the current user.
......
......@@ -55,9 +55,6 @@ struct _mu_address
char *route;
/* the optional ROUTE in the ROUTE-ADDR form of MAILBOX */
/* size_t num; this didn't appear to be used anywhere... so I commented
it out, is that ok? -sam */
struct _mu_address *next;
};
......
......@@ -29,6 +29,7 @@
#include <mailutils/property.h>
#include <mailutils/mailer.h>
#include <mailutils/url.h>
#include <mailutils/mutil.h>
#include <mailbox0.h>
struct remote_mbox_data
......@@ -134,6 +135,39 @@ remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
mkaddr (mbox, property, "FROM", &from);
mkaddr (mbox, property, "TO", &to);
if (!to)
{
const char *rcpt, *host;
char *domain = NULL;
status = mu_url_sget_user (mbox->url, &rcpt);
if (status != MU_ERR_NOENT)
{
if (status)
{
MU_DEBUG1 (mbox->debug, MU_DEBUG_ERROR,
"failed to get recipient from the url: %s\n",
mu_strerror (status));
return status;
}
/* FIXME: Set before if? */
mu_aget_user_email_domain (&domain);
mu_url_sget_host (mbox->url, &host);
mu_set_user_email_domain (host);
status = mu_address_create (&to, rcpt);
mu_set_user_email_domain (domain);
free (domain);
if (status)
{
MU_DEBUG3 (mbox->debug, MU_DEBUG_ERROR,
"%s: %s mu_address_create failed: %s\n",
rcpt, "TO", mu_strerror (status));
return status;
}
}
}
status = mu_mailer_send_message (dat->mailer, msg, from, to);
......
......@@ -82,7 +82,7 @@ maidag_stdio_delivery (int argc, char **argv)
static int biff_fd = -1;
static struct sockaddr_in biff_in;
static char *biff_user_name;
static const char *biff_user_name;
static int
notify_action (mu_observer_t obs, size_t type, void *data, void *action_data)
......@@ -161,7 +161,6 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
{
maidag_error (_("Cannot open mailbox %s: %s"),
path, mu_strerror (status));
mu_mailbox_destroy (&mbox);
return EX_TEMPFAIL;
}
......@@ -179,7 +178,6 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
{
maidag_error (_("Cannot lock mailbox `%s': %s"), path,
mu_strerror (status));
mu_mailbox_destroy (&mbox);
exit_code = EX_TEMPFAIL;
return EX_TEMPFAIL;
}
......@@ -198,10 +196,7 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
if (status == ENOSYS)
mbsize = 0; /* Try to continue anyway */
else
{
mu_mailbox_destroy (&mbox);
return EX_TEMPFAIL;
}
return EX_TEMPFAIL;
}
switch (check_quota (auth, mbsize, &n))
......@@ -266,40 +261,51 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
switch_user_id (auth, 0);
}
mu_auth_data_free (auth);
mu_mailbox_close (mbox);
mu_locker_unlock (lock);
mu_mailbox_destroy (&mbox);
return failed ? exit_code : 0;
}
static int
is_remote_url (mu_url_t url)
{
const char *scheme;
int rc = mu_url_sget_scheme (url, &scheme);
return rc == 0 && strncasecmp (scheme, "remote+", 7) == 0;
}
int
deliver (mu_mailbox_t imbx, char *name, char **errp)
deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
{
struct mu_auth_data *auth;
struct mu_auth_data *auth = NULL;
mu_mailbox_t mbox;
mu_message_t msg;
int status;
auth = mu_get_auth_by_name (name);
if (!auth)
if (is_remote_url (url))
auth = NULL;
else
{
maidag_error (_("%s: no such user"), name);
if (errp)
asprintf (errp, "%s: no such user", name);
exit_code = EX_UNAVAILABLE;
return EX_UNAVAILABLE;
}
if (current_uid)
auth->change_uid = 0;
auth = mu_get_auth_by_name (name);
if (!auth)
{
maidag_error (_("%s: no such user"), name);
if (errp)
asprintf (errp, "%s: no such user", name);
exit_code = EX_UNAVAILABLE;
return EX_UNAVAILABLE;
}
if (current_uid)
auth->change_uid = 0;
if (!sieve_test (auth, imbx))
{
exit_code = EX_OK;
mu_auth_data_free (auth);
return 0;
if (!sieve_test (auth, imbx))
{
exit_code = EX_OK;
mu_auth_data_free (auth);
return 0;
}
}
if ((status = mu_mailbox_get_message (imbx, 1, &msg)) != 0)
{
maidag_error (_("Cannot get message from the temporary mailbox: %s"),
......@@ -308,10 +314,16 @@ deliver (mu_mailbox_t imbx, char *name, char **errp)
return EX_TEMPFAIL;
}
if ((status = mu_mailbox_create (&mbox, auth->mailbox)) != 0)
if (url)
status = mu_mailbox_create_from_url (&mbox, url);
else
status = mu_mailbox_create (&mbox, auth->mailbox);
if (status)
{
maidag_error (_("Cannot open mailbox %s: %s"),
auth->mailbox, mu_strerror (status));
url ? mu_url_to_string (url): auth->mailbox,
mu_strerror (status));
mu_auth_data_free (auth);
return EX_TEMPFAIL;
}
......@@ -326,5 +338,53 @@ deliver (mu_mailbox_t imbx, char *name, char **errp)
status = deliver_to_user (imbx, mbox, msg, auth, name, errp);
if (switch_user_id (auth, 0))
return EX_TEMPFAIL;
mu_auth_data_free (auth);
mu_mailbox_destroy (&mbox);
return status;
}
int
deliver (mu_mailbox_t imbx, char *dest_id, char **errp)
{
int status;
const char *name;
mu_url_t url = NULL;
if (url_option)
{
status = mu_url_create (&url, dest_id);
if (status)
{
maidag_error (_("%s: cannot create url: %s"), dest_id,
mu_strerror (status));
return EX_NOUSER;
}
status = mu_url_parse (url);
if (status)
{
maidag_error (_("%s: cannot parse url: %s"), dest_id,
mu_strerror (status));
mu_url_destroy (&url);
return EX_NOUSER;
}
status = mu_url_sget_user (url, &name);
if (status == MU_ERR_NOENT)
name = NULL;
else if (status)
{
maidag_error (_("%s: cannot get user name from url: %s"),
dest_id, mu_strerror (status));
mu_url_destroy (&url);
return EX_NOUSER;
}
}
else
{
name = dest_id;
dest_id = NULL;
}
return deliver_url (url, imbx, name, errp);
}
......
......@@ -45,6 +45,7 @@ char *message_id_header; /* Use the value of this header as message
/* For LMTP mode */
mu_m_server_t server;
int lmtp_mode;
int url_option;
char *lmtp_url_string;
int reuse_lmtp_address = 1;
char *lmtp_group = "mail";
......@@ -66,6 +67,7 @@ static char args_doc[] = N_("[recipient...]");
#define MESSAGE_ID_HEADER_OPTION 257
#define LMTP_OPTION 258
#define FOREGROUND_OPTION 260
#define URL_OPTION 261
static struct argp_option options[] =
{
......@@ -73,7 +75,7 @@ static struct argp_option options[] =
{ "inetd", 'i', 0, 0, N_("Run in inetd mode"), 0 },
{ "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL,
N_("Runs in daemon mode with a maximum of NUMBER children"), 0 },
{ "url", URL_OPTION, 0, 0, N_("Deliver to given URLs"), 0 },
{ "from", 'f', N_("EMAIL"), 0,
N_("Specify the sender's name") },
{ NULL, 'r', NULL, OPTION_ALIAS, NULL },
......@@ -221,6 +223,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
case STDERR_OPTION:
mu_argp_node_list_new (&lst, "stderr", "yes");
break;
case URL_OPTION:
url_option = 1;
break;
case ARGP_KEY_INIT:
mu_argp_node_list_init (&lst);
......@@ -492,7 +498,7 @@ main (int argc, char *argv[])
current_uid = getuid ();
if (log_to_stderr == -1)
log_to_stderr = !lmtp_mode && (current_uid != 0);
log_to_stderr = url_option || (!lmtp_mode && (current_uid != 0));
if (!log_to_stderr)
{
......@@ -508,7 +514,7 @@ main (int argc, char *argv[])
argc -= arg_index;
argv += arg_index;
if (lmtp_mode)
if (lmtp_mode && !url_option)
{
if (argc)
{
......@@ -521,24 +527,30 @@ main (int argc, char *argv[])
{
if (current_uid)
{
static char *s_argv[2];
struct mu_auth_data *auth = mu_get_auth_by_uid (current_uid);
if (!current_uid)
if (url_option)
{
mu_error (_("Cannot get username"));
return EX_UNAVAILABLE;
/* FIXME: Verify if the urls are deliverable? */
}
if (argc > 1 || (argc > 0 && strcmp (auth->name, argv[0])))
else
{
mu_error (_("Recipients given when running as non-root"));
return EX_USAGE;
static char *s_argv[2];
struct mu_auth_data *auth = mu_get_auth_by_uid (current_uid);
if (!current_uid)
{
mu_error (_("Cannot get username"));
return EX_UNAVAILABLE;
}
if (argc > 0 && strcmp (auth->name, argv[0]))
{
mu_error (_("Recipients given when running as non-root"));
return EX_USAGE;
}
s_argv[0] = auth->name;
argv = s_argv;
argc = 1;
}
s_argv[0] = auth->name;
argv = s_argv;
argc = 1;
}
return maidag_stdio_delivery (argc, argv);
}
......
......@@ -123,6 +123,7 @@ extern char *sieve_pattern;
extern mu_m_server_t server;
extern int lmtp_mode;
extern int url_option;
extern char *lmtp_url_string;
extern int reuse_lmtp_address;
extern char *lmtp_group;
......
......@@ -274,6 +274,9 @@ int
check_quota (struct mu_auth_data *auth, mu_off_t size, mu_off_t *rest)
{
mu_off_t quota;
if (!auth)
return MQUOTA_OK;
switch (retrieve_quota (auth, &quota))
{
......@@ -292,7 +295,6 @@ check_quota (struct mu_auth_data *auth, mu_off_t size, mu_off_t *rest)
}
return MQUOTA_OK;
}
#endif /* USE_MAIL_QUOTA */
......
......@@ -247,6 +247,14 @@ mu_mailbox_create (mu_mailbox_t *pmbox, const char *name)
return rc;
}
int
mu_mailbox_create_from_url (mu_mailbox_t *pmbox, mu_url_t url)
{
if (pmbox == NULL)
return MU_ERR_OUT_PTR_NULL;
return _create_mailbox0 (pmbox, url, mu_url_to_string (url));
}
void
mu_mailbox_destroy (mu_mailbox_t *pmbox)
{
......
......@@ -411,6 +411,7 @@ mu_set_user_email_domain (const char *domain)
return 0;
}
/* FIXME: must be called _sget_ */
int
mu_get_user_email_domain (const char **domain)
{
......@@ -427,6 +428,24 @@ mu_get_user_email_domain (const char **domain)
return 0;
}
int
mu_aget_user_email_domain (char **pdomain)
{
const char *domain;
int status = mu_get_user_email_domain (&domain);
if (status)
return status;
if (domain == NULL)
*pdomain = domain;
else
{
*pdomain = strdup (domain);
if (*pdomain == NULL)
return ENOMEM;
}
return 0;
}
/* Note: allocates memory */
char *
mu_get_user_email (const char *name)
......