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=.
Showing
7 changed files
with
176 additions
and
27 deletions
... | @@ -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) | ... | ... |
-
Please register or sign in to post a comment