Commit 76163154 76163154c740039046a2e0ae4b4aa423707c5473 by Sergey Poznyakoff

Improve authentication interface in SMTP client.

* include/mailutils/smtp.h (MU_SMTP_PARAM_URL): New parameter code.
* include/mailutils/sys/smtp.h (_MU_SMTP_CLNPASS): New flag.
(_mu_smtp) <secret>: New member.
* libproto/mailer/smtp_auth.c (get_ticket)
(_mu_smtp_fixup_params): New static functions.
(mu_smtp_auth): Call _mu_smtp_fixup_params to supply missing
pararameters.
* libproto/mailer/smtp_create.c (mu_smtp_destroy): Destroy secret.
Take care to erase eventual plaintext representation of the password.
* libproto/mailer/smtp_gsasl.c (_smtp_callback): Use mu_smtp_get_param
to obtain parameters.
* libproto/mailer/smtp_param.c (mu_smtp_set_param): Setting
MU_SMTP_PARAM_PASSWORD updates secret.  param[MU_SMTP_PARAM_PASSWORD]
points (temporarly and on request only) to plaintext password.
(mu_smtp_get_param): Likewise for retrieving password.
* testsuite/smtpsend.c (main): New parameter url=.
1 parent 652197d5
......@@ -30,10 +30,11 @@ enum
MU_SMTP_PARAM_PASSWORD,
MU_SMTP_PARAM_SERVICE,
MU_SMTP_PARAM_REALM,
MU_SMTP_PARAM_HOST
MU_SMTP_PARAM_HOST,
MU_SMTP_PARAM_URL
};
#define MU_SMTP_MAX_PARAM (MU_SMTP_PARAM_HOST+1)
#define MU_SMTP_MAX_PARAM (MU_SMTP_PARAM_URL+1)
int mu_smtp_create (mu_smtp_t *);
void mu_smtp_destroy (mu_smtp_t *);
......
......@@ -22,12 +22,13 @@
# include <mailutils/types.h>
# include <mailutils/smtp.h>
# define _MU_SMTP_ESMTP 0x01 /* Connection supports ESMTP */
# define _MU_SMTP_TRACE 0x02 /* Session trace required/enabled */
# define _MU_SMTP_ERR 0x04 /* Object in error state */
# define _MU_SMTP_MLREPL 0x08 /* Last reply was multi-line */
# define _MU_SMTP_TLS 0x10 /* TLS initiated */
# define _MU_SMTP_AUTH 0x20 /* Authorization passed */
# define _MU_SMTP_ESMTP 0x01 /* Connection supports ESMTP */
# define _MU_SMTP_TRACE 0x02 /* Session trace required/enabled */
# define _MU_SMTP_ERR 0x04 /* Object in error state */
# define _MU_SMTP_MLREPL 0x08 /* Last reply was multi-line */
# define _MU_SMTP_TLS 0x10 /* TLS initiated */
# define _MU_SMTP_AUTH 0x20 /* Authorization passed */
# define _MU_SMTP_CLNPASS 0x40 /* Password has been de-obfuscated */
#define MU_SMTP_XSCRIPT_MASK(n) (0x100<<(n))
......@@ -55,6 +56,7 @@ struct _mu_smtp
/* User-supplied data */
char *param[MU_SMTP_MAX_PARAM];
mu_list_t authmech; /* List of allowed authentication mechs */
mu_secret_t secret;
/* I/O buffers */
char replcode[4];
......
......@@ -19,9 +19,103 @@
#endif
#include <errno.h>
#include <stdlib.h>
#include <mailutils/diag.h>
#include <mailutils/errno.h>
#include <mailutils/smtp.h>
#include <mailutils/sys/smtp.h>
#include <mailutils/mailbox.h> /* Strange, mu_ticket_file is declared here */
#include <mailutils/mutil.h>
#include <mailutils/auth.h>
#include <mailutils/url.h>
static int
get_ticket (mu_ticket_t *pticket)
{
char *filename = mu_tilde_expansion (mu_ticket_file, "/", NULL);
mu_wicket_t wicket;
int rc;
rc = mu_file_wicket_create (&wicket, filename);
if (rc == 0)
rc = mu_wicket_get_ticket (wicket, NULL, pticket);
mu_wicket_destroy (&wicket);
free (filename);
return rc;
}
#define _HAS_USERNAME 0x01
#define _HAS_PASSWORD 0x02
static int
_mu_smtp_fixup_params (mu_smtp_t smtp)
{
const char *str;
mu_url_t url;
mu_ticket_t ticket = NULL;
int flags = 0;
int rc;
if (smtp->param[MU_SMTP_PARAM_USERNAME])
flags |= _HAS_USERNAME;
if (smtp->secret)
flags |= _HAS_PASSWORD;
if ((flags & (_HAS_USERNAME|_HAS_PASSWORD)) == (_HAS_USERNAME|_HAS_PASSWORD))
return 0; /* Nothing to do */
if (!smtp->param[MU_SMTP_PARAM_URL])
return 0;
rc = mu_url_create (&url, smtp->param[MU_SMTP_PARAM_URL]);
if (rc)
{
mu_diag_output (MU_DIAG_ERROR, "cannot create URL: %s",
mu_strerror (rc));
return rc;
}
rc = mu_url_parse (url);
if (rc)
{
mu_diag_output (MU_DIAG_ERROR, "cannot parse URL: %s",
mu_strerror (rc));
mu_url_destroy (&url);
return rc;
}
if (!(flags & _HAS_USERNAME))
{
rc = mu_url_sget_user (url, &str);
if (rc == 0 &&
mu_smtp_set_param (smtp, MU_SMTP_PARAM_USERNAME, str) == 0)
flags |= _HAS_USERNAME;
}
if (!(flags & _HAS_PASSWORD) && mu_url_get_secret (url, &smtp->secret) == 0)
flags |= _HAS_PASSWORD;
if ((!(flags & _HAS_USERNAME) ||
!(flags & _HAS_PASSWORD)) &&
get_ticket (&ticket) == 0)
{
if (!(flags & _HAS_USERNAME) &&
mu_ticket_get_cred (ticket, url, "SMTP User: ",
&smtp->param[MU_SMTP_PARAM_USERNAME],
NULL) == 0)
flags |= _HAS_USERNAME;
if (!(flags & _HAS_PASSWORD) && !smtp->secret)
mu_ticket_get_cred (ticket, url, "SMTP Passwd: ",
NULL, &smtp->secret);
mu_ticket_destroy (&ticket);
}
mu_url_destroy (&url);
return 0;
}
int
mu_smtp_auth (mu_smtp_t smtp)
......@@ -34,7 +128,19 @@ mu_smtp_auth (mu_smtp_t smtp)
return MU_ERR_SEQ;
if (smtp->state != MU_SMTP_MAIL)
return MU_ERR_SEQ;
/* Obtain missing authentication credentials either from the
URL (when supplied) or from the user ticket file, or by
asking the user, if anything else fails.
FIXME: This needs some more work. First of all, it should
be called only when really needed (e.g. by mu_smtp_get_param).
Secondly, it should ask the user even if no URL was supplied
(presently it does not). Thirdly, there should be an API to
let caller determine the way of inputting missing data (by
default it does that on tty, which obviously will not suite
GUI applications). */
_mu_smtp_fixup_params (smtp);
#if defined(WITH_GSASL)
return _mu_smtp_gsasl_auth (smtp);
#else
......
......@@ -20,6 +20,7 @@
#include <errno.h>
#include <stdlib.h>
#include <mailutils/list.h>
#include <mailutils/secret.h>
#include <mailutils/smtp.h>
#include <mailutils/stream.h>
#include <mailutils/sys/smtp.h>
......@@ -59,8 +60,19 @@ mu_smtp_destroy (mu_smtp_t *psmtp)
mu_list_destroy (&smtp->mlrepl);
mu_list_destroy (&smtp->authmech);
if (smtp->secret)
{
if (MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
mu_secret_password_unref (smtp->secret);
mu_secret_destroy (&smtp->secret);
}
for (i = 0; i < MU_SMTP_MAX_PARAM; i++)
free (smtp->param[i]);
{
if (i == MU_SMTP_PARAM_PASSWORD)
continue;
free (smtp->param[i]);
}
free (smtp);
*psmtp = NULL;
......
......@@ -75,44 +75,48 @@ _smtp_callback (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
{
int rc = GSASL_OK;
mu_smtp_t smtp = gsasl_callback_hook_get (ctx);
char *p;
const char *p = NULL;
switch (prop)
{
case GSASL_PASSWORD:
gsasl_property_set (sctx, prop, smtp->param[MU_SMTP_PARAM_PASSWORD]);
if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_PASSWORD, &p) == 0 && p)
gsasl_property_set (sctx, prop, p);
else
rc = GSASL_NO_PASSWORD;
break;
case GSASL_AUTHID:
case GSASL_ANONYMOUS_TOKEN:
gsasl_property_set (sctx, prop, smtp->param[MU_SMTP_PARAM_USERNAME]);
if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_USERNAME, &p) == 0 && p)
gsasl_property_set (sctx, prop, p);
else if (prop == GSASL_AUTHID)
rc = GSASL_NO_AUTHID;
else
rc = GSASL_NO_ANONYMOUS_TOKEN;
break;
case GSASL_AUTHZID:
gsasl_property_set (sctx, prop, NULL);
rc = GSASL_NO_AUTHZID;
break;
case GSASL_SERVICE:
gsasl_property_set (sctx, prop,
smtp->param[MU_SMTP_PARAM_SERVICE] ?
smtp->param[MU_SMTP_PARAM_SERVICE] : "smtp");
if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_SERVICE, &p) || !p)
p = "smtp";
gsasl_property_set (sctx, prop, p);
break;
case GSASL_REALM:
p = smtp->param[MU_SMTP_PARAM_REALM] ?
smtp->param[MU_SMTP_PARAM_REALM] :
smtp->param[MU_SMTP_PARAM_DOMAIN];
if (!p)
if ((mu_smtp_get_param (smtp, MU_SMTP_PARAM_REALM, &p) || !p) &&
(mu_smtp_get_param (smtp, MU_SMTP_PARAM_DOMAIN, &p) || !p))
rc = GSASL_NO_HOSTNAME;
else
gsasl_property_set (sctx, prop, p);
break;
case GSASL_HOSTNAME:
if (smtp->param[MU_SMTP_PARAM_HOST])
p = smtp->param[MU_SMTP_PARAM_HOST];
else if (mu_get_host_name (&p))
if ((mu_smtp_get_param (smtp, MU_SMTP_PARAM_HOST, &p) || !p) &&
mu_get_host_name ((char**)&p))
{
rc = GSASL_NO_HOSTNAME;
break;
......
......@@ -27,7 +27,7 @@
#include <mailutils/smtp.h>
#include <mailutils/sys/smtp.h>
int
int
mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam)
{
char *param;
......@@ -36,6 +36,20 @@ mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam)
return EINVAL;
if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM)
return EINVAL;
if (pcode == MU_SMTP_PARAM_PASSWORD)
{
/* Special handling for passwords */
smtp->param[MU_SMTP_PARAM_PASSWORD] = NULL;
if (smtp->secret)
{
if (MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
mu_secret_password_unref (smtp->secret);
mu_secret_destroy (&smtp->secret);
}
MU_SMTP_FCLR (smtp, _MU_SMTP_CLNPASS);
return mu_secret_create (&smtp->secret, newparam, strlen (newparam));
}
param = strdup (newparam);
if (!param)
......@@ -52,6 +66,13 @@ mu_smtp_get_param (mu_smtp_t smtp, int pcode, const char **pparam)
return EINVAL;
if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM)
return EINVAL;
if (pcode == MU_SMTP_PARAM_PASSWORD && smtp->secret &&
!MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
{
smtp->param[pcode] = mu_secret_password (smtp->secret);
MU_SMTP_FSET (smtp, _MU_SMTP_CLNPASS);
}
*pparam = smtp->param[pcode];
return 0;
}
......
......@@ -27,7 +27,7 @@ static char usage_text[] =
"usage: %s hostname [port=N] [trace=N] [tls=N] [from=STRING] [rcpt=STRING]\n"
" [domain=STRING] [user=STRING] [pass=STRING]\n"
" [service=STRING] [realm=STRING] [host=STRING]\n"
" [auth=method[,...]] [input=FILE] [raw=N]\n";
" [auth=method[,...]] [url=STRING] [input=FILE] [raw=N]\n";
static void
usage ()
......@@ -107,6 +107,9 @@ main (int argc, char **argv)
else if (strncmp (argv[i], "host=", 5) == 0)
MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_HOST,
argv[i] + 5));
else if (strncmp (argv[i], "url=", 4) == 0)
MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_URL,
argv[i] + 4));
else if (strncmp (argv[i], "infile=", 7) == 0)
infile = argv[i] + 7;
else if (strncmp (argv[i], "raw=", 4) == 0)
......