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 ...@@ -30,10 +30,11 @@ enum
30 MU_SMTP_PARAM_PASSWORD, 30 MU_SMTP_PARAM_PASSWORD,
31 MU_SMTP_PARAM_SERVICE, 31 MU_SMTP_PARAM_SERVICE,
32 MU_SMTP_PARAM_REALM, 32 MU_SMTP_PARAM_REALM,
33 MU_SMTP_PARAM_HOST 33 MU_SMTP_PARAM_HOST,
34 MU_SMTP_PARAM_URL
34 }; 35 };
35 36
36 #define MU_SMTP_MAX_PARAM (MU_SMTP_PARAM_HOST+1) 37 #define MU_SMTP_MAX_PARAM (MU_SMTP_PARAM_URL+1)
37 38
38 int mu_smtp_create (mu_smtp_t *); 39 int mu_smtp_create (mu_smtp_t *);
39 void mu_smtp_destroy (mu_smtp_t *); 40 void mu_smtp_destroy (mu_smtp_t *);
......
...@@ -22,12 +22,13 @@ ...@@ -22,12 +22,13 @@
22 # include <mailutils/types.h> 22 # include <mailutils/types.h>
23 # include <mailutils/smtp.h> 23 # include <mailutils/smtp.h>
24 24
25 # define _MU_SMTP_ESMTP 0x01 /* Connection supports ESMTP */ 25 # define _MU_SMTP_ESMTP 0x01 /* Connection supports ESMTP */
26 # define _MU_SMTP_TRACE 0x02 /* Session trace required/enabled */ 26 # define _MU_SMTP_TRACE 0x02 /* Session trace required/enabled */
27 # define _MU_SMTP_ERR 0x04 /* Object in error state */ 27 # define _MU_SMTP_ERR 0x04 /* Object in error state */
28 # define _MU_SMTP_MLREPL 0x08 /* Last reply was multi-line */ 28 # define _MU_SMTP_MLREPL 0x08 /* Last reply was multi-line */
29 # define _MU_SMTP_TLS 0x10 /* TLS initiated */ 29 # define _MU_SMTP_TLS 0x10 /* TLS initiated */
30 # define _MU_SMTP_AUTH 0x20 /* Authorization passed */ 30 # define _MU_SMTP_AUTH 0x20 /* Authorization passed */
31 # define _MU_SMTP_CLNPASS 0x40 /* Password has been de-obfuscated */
31 32
32 #define MU_SMTP_XSCRIPT_MASK(n) (0x100<<(n)) 33 #define MU_SMTP_XSCRIPT_MASK(n) (0x100<<(n))
33 34
...@@ -55,6 +56,7 @@ struct _mu_smtp ...@@ -55,6 +56,7 @@ struct _mu_smtp
55 /* User-supplied data */ 56 /* User-supplied data */
56 char *param[MU_SMTP_MAX_PARAM]; 57 char *param[MU_SMTP_MAX_PARAM];
57 mu_list_t authmech; /* List of allowed authentication mechs */ 58 mu_list_t authmech; /* List of allowed authentication mechs */
59 mu_secret_t secret;
58 60
59 /* I/O buffers */ 61 /* I/O buffers */
60 char replcode[4]; 62 char replcode[4];
......
...@@ -19,9 +19,103 @@ ...@@ -19,9 +19,103 @@
19 #endif 19 #endif
20 20
21 #include <errno.h> 21 #include <errno.h>
22 #include <stdlib.h>
23 #include <mailutils/diag.h>
22 #include <mailutils/errno.h> 24 #include <mailutils/errno.h>
23 #include <mailutils/smtp.h> 25 #include <mailutils/smtp.h>
24 #include <mailutils/sys/smtp.h> 26 #include <mailutils/sys/smtp.h>
27 #include <mailutils/mailbox.h> /* Strange, mu_ticket_file is declared here */
28 #include <mailutils/mutil.h>
29 #include <mailutils/auth.h>
30 #include <mailutils/url.h>
31
32 static int
33 get_ticket (mu_ticket_t *pticket)
34 {
35 char *filename = mu_tilde_expansion (mu_ticket_file, "/", NULL);
36 mu_wicket_t wicket;
37 int rc;
38
39 rc = mu_file_wicket_create (&wicket, filename);
40
41 if (rc == 0)
42 rc = mu_wicket_get_ticket (wicket, NULL, pticket);
43 mu_wicket_destroy (&wicket);
44 free (filename);
45 return rc;
46 }
47
48 #define _HAS_USERNAME 0x01
49 #define _HAS_PASSWORD 0x02
50
51 static int
52 _mu_smtp_fixup_params (mu_smtp_t smtp)
53 {
54 const char *str;
55 mu_url_t url;
56 mu_ticket_t ticket = NULL;
57 int flags = 0;
58 int rc;
59
60 if (smtp->param[MU_SMTP_PARAM_USERNAME])
61 flags |= _HAS_USERNAME;
62
63 if (smtp->secret)
64 flags |= _HAS_PASSWORD;
65
66 if ((flags & (_HAS_USERNAME|_HAS_PASSWORD)) == (_HAS_USERNAME|_HAS_PASSWORD))
67 return 0; /* Nothing to do */
68
69 if (!smtp->param[MU_SMTP_PARAM_URL])
70 return 0;
71
72 rc = mu_url_create (&url, smtp->param[MU_SMTP_PARAM_URL]);
73 if (rc)
74 {
75 mu_diag_output (MU_DIAG_ERROR, "cannot create URL: %s",
76 mu_strerror (rc));
77 return rc;
78 }
79
80 rc = mu_url_parse (url);
81 if (rc)
82 {
83 mu_diag_output (MU_DIAG_ERROR, "cannot parse URL: %s",
84 mu_strerror (rc));
85 mu_url_destroy (&url);
86 return rc;
87 }
88
89 if (!(flags & _HAS_USERNAME))
90 {
91 rc = mu_url_sget_user (url, &str);
92 if (rc == 0 &&
93 mu_smtp_set_param (smtp, MU_SMTP_PARAM_USERNAME, str) == 0)
94 flags |= _HAS_USERNAME;
95 }
96
97 if (!(flags & _HAS_PASSWORD) && mu_url_get_secret (url, &smtp->secret) == 0)
98 flags |= _HAS_PASSWORD;
99
100 if ((!(flags & _HAS_USERNAME) ||
101 !(flags & _HAS_PASSWORD)) &&
102 get_ticket (&ticket) == 0)
103 {
104 if (!(flags & _HAS_USERNAME) &&
105 mu_ticket_get_cred (ticket, url, "SMTP User: ",
106 &smtp->param[MU_SMTP_PARAM_USERNAME],
107 NULL) == 0)
108 flags |= _HAS_USERNAME;
109
110 if (!(flags & _HAS_PASSWORD) && !smtp->secret)
111 mu_ticket_get_cred (ticket, url, "SMTP Passwd: ",
112 NULL, &smtp->secret);
113 mu_ticket_destroy (&ticket);
114 }
115
116 mu_url_destroy (&url);
117 return 0;
118 }
25 119
26 int 120 int
27 mu_smtp_auth (mu_smtp_t smtp) 121 mu_smtp_auth (mu_smtp_t smtp)
...@@ -34,7 +128,19 @@ mu_smtp_auth (mu_smtp_t smtp) ...@@ -34,7 +128,19 @@ mu_smtp_auth (mu_smtp_t smtp)
34 return MU_ERR_SEQ; 128 return MU_ERR_SEQ;
35 if (smtp->state != MU_SMTP_MAIL) 129 if (smtp->state != MU_SMTP_MAIL)
36 return MU_ERR_SEQ; 130 return MU_ERR_SEQ;
37 131
132 /* Obtain missing authentication credentials either from the
133 URL (when supplied) or from the user ticket file, or by
134 asking the user, if anything else fails.
135
136 FIXME: This needs some more work. First of all, it should
137 be called only when really needed (e.g. by mu_smtp_get_param).
138 Secondly, it should ask the user even if no URL was supplied
139 (presently it does not). Thirdly, there should be an API to
140 let caller determine the way of inputting missing data (by
141 default it does that on tty, which obviously will not suite
142 GUI applications). */
143 _mu_smtp_fixup_params (smtp);
38 #if defined(WITH_GSASL) 144 #if defined(WITH_GSASL)
39 return _mu_smtp_gsasl_auth (smtp); 145 return _mu_smtp_gsasl_auth (smtp);
40 #else 146 #else
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
20 #include <errno.h> 20 #include <errno.h>
21 #include <stdlib.h> 21 #include <stdlib.h>
22 #include <mailutils/list.h> 22 #include <mailutils/list.h>
23 #include <mailutils/secret.h>
23 #include <mailutils/smtp.h> 24 #include <mailutils/smtp.h>
24 #include <mailutils/stream.h> 25 #include <mailutils/stream.h>
25 #include <mailutils/sys/smtp.h> 26 #include <mailutils/sys/smtp.h>
...@@ -59,8 +60,19 @@ mu_smtp_destroy (mu_smtp_t *psmtp) ...@@ -59,8 +60,19 @@ mu_smtp_destroy (mu_smtp_t *psmtp)
59 mu_list_destroy (&smtp->mlrepl); 60 mu_list_destroy (&smtp->mlrepl);
60 61
61 mu_list_destroy (&smtp->authmech); 62 mu_list_destroy (&smtp->authmech);
63 if (smtp->secret)
64 {
65 if (MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
66 mu_secret_password_unref (smtp->secret);
67 mu_secret_destroy (&smtp->secret);
68 }
69
62 for (i = 0; i < MU_SMTP_MAX_PARAM; i++) 70 for (i = 0; i < MU_SMTP_MAX_PARAM; i++)
63 free (smtp->param[i]); 71 {
72 if (i == MU_SMTP_PARAM_PASSWORD)
73 continue;
74 free (smtp->param[i]);
75 }
64 76
65 free (smtp); 77 free (smtp);
66 *psmtp = NULL; 78 *psmtp = NULL;
......
...@@ -75,44 +75,48 @@ _smtp_callback (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) ...@@ -75,44 +75,48 @@ _smtp_callback (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
75 { 75 {
76 int rc = GSASL_OK; 76 int rc = GSASL_OK;
77 mu_smtp_t smtp = gsasl_callback_hook_get (ctx); 77 mu_smtp_t smtp = gsasl_callback_hook_get (ctx);
78 char *p; 78 const char *p = NULL;
79 79
80 switch (prop) 80 switch (prop)
81 { 81 {
82 case GSASL_PASSWORD: 82 case GSASL_PASSWORD:
83 gsasl_property_set (sctx, prop, smtp->param[MU_SMTP_PARAM_PASSWORD]); 83 if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_PASSWORD, &p) == 0 && p)
84 gsasl_property_set (sctx, prop, p);
85 else
86 rc = GSASL_NO_PASSWORD;
84 break; 87 break;
85 88
86 case GSASL_AUTHID: 89 case GSASL_AUTHID:
87 case GSASL_ANONYMOUS_TOKEN: 90 case GSASL_ANONYMOUS_TOKEN:
88 gsasl_property_set (sctx, prop, smtp->param[MU_SMTP_PARAM_USERNAME]); 91 if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_USERNAME, &p) == 0 && p)
92 gsasl_property_set (sctx, prop, p);
93 else if (prop == GSASL_AUTHID)
94 rc = GSASL_NO_AUTHID;
95 else
96 rc = GSASL_NO_ANONYMOUS_TOKEN;
89 break; 97 break;
90 98
91 case GSASL_AUTHZID: 99 case GSASL_AUTHZID:
92 gsasl_property_set (sctx, prop, NULL); 100 rc = GSASL_NO_AUTHZID;
93 break; 101 break;
94 102
95 case GSASL_SERVICE: 103 case GSASL_SERVICE:
96 gsasl_property_set (sctx, prop, 104 if (mu_smtp_get_param (smtp, MU_SMTP_PARAM_SERVICE, &p) || !p)
97 smtp->param[MU_SMTP_PARAM_SERVICE] ? 105 p = "smtp";
98 smtp->param[MU_SMTP_PARAM_SERVICE] : "smtp"); 106 gsasl_property_set (sctx, prop, p);
99 break; 107 break;
100 108
101 case GSASL_REALM: 109 case GSASL_REALM:
102 p = smtp->param[MU_SMTP_PARAM_REALM] ? 110 if ((mu_smtp_get_param (smtp, MU_SMTP_PARAM_REALM, &p) || !p) &&
103 smtp->param[MU_SMTP_PARAM_REALM] : 111 (mu_smtp_get_param (smtp, MU_SMTP_PARAM_DOMAIN, &p) || !p))
104 smtp->param[MU_SMTP_PARAM_DOMAIN];
105
106 if (!p)
107 rc = GSASL_NO_HOSTNAME; 112 rc = GSASL_NO_HOSTNAME;
108 else 113 else
109 gsasl_property_set (sctx, prop, p); 114 gsasl_property_set (sctx, prop, p);
110 break; 115 break;
111 116
112 case GSASL_HOSTNAME: 117 case GSASL_HOSTNAME:
113 if (smtp->param[MU_SMTP_PARAM_HOST]) 118 if ((mu_smtp_get_param (smtp, MU_SMTP_PARAM_HOST, &p) || !p) &&
114 p = smtp->param[MU_SMTP_PARAM_HOST]; 119 mu_get_host_name ((char**)&p))
115 else if (mu_get_host_name (&p))
116 { 120 {
117 rc = GSASL_NO_HOSTNAME; 121 rc = GSASL_NO_HOSTNAME;
118 break; 122 break;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
27 #include <mailutils/smtp.h> 27 #include <mailutils/smtp.h>
28 #include <mailutils/sys/smtp.h> 28 #include <mailutils/sys/smtp.h>
29 29
30 int 30 int
31 mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam) 31 mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam)
32 { 32 {
33 char *param; 33 char *param;
...@@ -36,6 +36,20 @@ mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam) ...@@ -36,6 +36,20 @@ mu_smtp_set_param (mu_smtp_t smtp, int pcode, const char *newparam)
36 return EINVAL; 36 return EINVAL;
37 if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM) 37 if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM)
38 return EINVAL; 38 return EINVAL;
39
40 if (pcode == MU_SMTP_PARAM_PASSWORD)
41 {
42 /* Special handling for passwords */
43 smtp->param[MU_SMTP_PARAM_PASSWORD] = NULL;
44 if (smtp->secret)
45 {
46 if (MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
47 mu_secret_password_unref (smtp->secret);
48 mu_secret_destroy (&smtp->secret);
49 }
50 MU_SMTP_FCLR (smtp, _MU_SMTP_CLNPASS);
51 return mu_secret_create (&smtp->secret, newparam, strlen (newparam));
52 }
39 53
40 param = strdup (newparam); 54 param = strdup (newparam);
41 if (!param) 55 if (!param)
...@@ -52,6 +66,13 @@ mu_smtp_get_param (mu_smtp_t smtp, int pcode, const char **pparam) ...@@ -52,6 +66,13 @@ mu_smtp_get_param (mu_smtp_t smtp, int pcode, const char **pparam)
52 return EINVAL; 66 return EINVAL;
53 if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM) 67 if (pcode < 0 || pcode >= MU_SMTP_MAX_PARAM)
54 return EINVAL; 68 return EINVAL;
69 if (pcode == MU_SMTP_PARAM_PASSWORD && smtp->secret &&
70 !MU_SMTP_FISSET (smtp, _MU_SMTP_CLNPASS))
71 {
72 smtp->param[pcode] = mu_secret_password (smtp->secret);
73 MU_SMTP_FSET (smtp, _MU_SMTP_CLNPASS);
74 }
75
55 *pparam = smtp->param[pcode]; 76 *pparam = smtp->param[pcode];
56 return 0; 77 return 0;
57 } 78 }
......
...@@ -27,7 +27,7 @@ static char usage_text[] = ...@@ -27,7 +27,7 @@ static char usage_text[] =
27 "usage: %s hostname [port=N] [trace=N] [tls=N] [from=STRING] [rcpt=STRING]\n" 27 "usage: %s hostname [port=N] [trace=N] [tls=N] [from=STRING] [rcpt=STRING]\n"
28 " [domain=STRING] [user=STRING] [pass=STRING]\n" 28 " [domain=STRING] [user=STRING] [pass=STRING]\n"
29 " [service=STRING] [realm=STRING] [host=STRING]\n" 29 " [service=STRING] [realm=STRING] [host=STRING]\n"
30 " [auth=method[,...]] [input=FILE] [raw=N]\n"; 30 " [auth=method[,...]] [url=STRING] [input=FILE] [raw=N]\n";
31 31
32 static void 32 static void
33 usage () 33 usage ()
...@@ -107,6 +107,9 @@ main (int argc, char **argv) ...@@ -107,6 +107,9 @@ main (int argc, char **argv)
107 else if (strncmp (argv[i], "host=", 5) == 0) 107 else if (strncmp (argv[i], "host=", 5) == 0)
108 MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_HOST, 108 MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_HOST,
109 argv[i] + 5)); 109 argv[i] + 5));
110 else if (strncmp (argv[i], "url=", 4) == 0)
111 MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_URL,
112 argv[i] + 4));
110 else if (strncmp (argv[i], "infile=", 7) == 0) 113 else if (strncmp (argv[i], "infile=", 7) == 0)
111 infile = argv[i] + 7; 114 infile = argv[i] + 7;
112 else if (strncmp (argv[i], "raw=", 4) == 0) 115 else if (strncmp (argv[i], "raw=", 4) == 0)
......