1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
* Copyright (C) 2011, 2013, 2014 Red Hat, Inc.
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General
16
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
* Authors: David Zeuthen <davidz@redhat.com>
19
* Debarshi Ray <debarshir@gnome.org>
26
#include <glib/gi18n-lib.h>
28
#include "goasmtpauth.h"
29
#include "goaprovider.h"
35
* @short_description: Authentication mechanisms for SMTP
37
* #GoaSmtpAuth implements the <ulink
38
* url="http://tools.ietf.org/html/rfc4616">PLAIN</ulink> and <ulink
39
* url="http://msdn.microsoft.com/en-us/library/cc433484(v=EXCHG.80).aspx">LOGIN</ulink>
40
* SASL mechanisms (e.g. using usernames / passwords) for SMTP.
46
* The #GoaSmtpAuth structure contains only private data and should
47
* only be accessed using the provided API.
51
GoaMailAuth parent_instance;
53
GoaProvider *provider;
55
gboolean auth_supported;
56
gboolean greeting_absent;
57
gboolean login_supported;
58
gboolean plain_supported;
66
GoaMailAuthClass parent_class;
80
static gboolean goa_smtp_auth_is_needed (GoaMailAuth *_auth);
81
static gboolean goa_smtp_auth_run_sync (GoaMailAuth *_auth,
82
GCancellable *cancellable,
84
static gboolean goa_smtp_auth_starttls_sync (GoaMailAuth *_auth,
85
GCancellable *cancellable,
88
G_DEFINE_TYPE (GoaSmtpAuth, goa_smtp_auth, GOA_TYPE_MAIL_AUTH);
90
/* ---------------------------------------------------------------------------------------------------- */
93
smtp_auth_check_not_220 (const gchar *response, GError **error)
95
if (!g_str_has_prefix (response, "220"))
99
GOA_ERROR_FAILED, /* TODO: more specific */
100
"Unexpected response `%s'",
109
smtp_auth_check_not_235 (const gchar *response, GError **error)
111
if (!g_str_has_prefix (response, "235"))
115
GOA_ERROR_FAILED, /* TODO: more specific */
116
_("Authentication failed"));
124
smtp_auth_check_not_250 (const gchar *response, GError **error)
126
if (!g_str_has_prefix (response, "250") || strlen (response) < 4)
130
GOA_ERROR_FAILED, /* TODO: more specific */
131
"Unexpected response `%s'",
140
smtp_auth_check_not_334_login_password (const gchar *response, GError **error)
142
if (!g_str_has_prefix (response, "334 UGFzc3dvcmQ6"))
146
GOA_ERROR_FAILED, /* TODO: more specific */
147
"Unexpected response `%s'",
156
smtp_auth_check_421 (const gchar *response, GError **error)
158
if (g_str_has_prefix (response, "421"))
162
GOA_ERROR_FAILED, /* TODO: more specific */
163
_("Service not available"));
171
smtp_auth_check_454 (const gchar *response, GError **error)
173
if (g_str_has_prefix (response, "454"))
177
GOA_ERROR_FAILED, /* TODO: more specific */
178
_("TLS not available"));
185
/* ---------------------------------------------------------------------------------------------------- */
188
smtp_auth_check_greeting (GDataInputStream *input, GCancellable *cancellable, GError **error)
197
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
198
if (response == NULL)
200
g_debug ("< %s", response);
201
if (smtp_auth_check_421 (response, error))
203
if (smtp_auth_check_not_220 (response, error))
206
if (response[3] == '-')
208
g_clear_pointer (&response, g_free);
219
/* ---------------------------------------------------------------------------------------------------- */
222
smtp_auth_get_domain (GoaSmtpAuth *auth,
229
if (auth->domain != NULL)
231
domain = g_strdup (auth->domain);
233
else if (auth->object != NULL)
236
gchar *email_address;
238
mail = goa_object_get_mail (auth->object);
243
GOA_ERROR_FAILED, /* TODO: more specific */
244
_("org.gnome.OnlineAccounts.Mail is not available"));
248
email_address = goa_mail_dup_email_address (mail);
249
if (!goa_utils_parse_email_address (email_address, NULL, &domain))
253
GOA_ERROR_FAILED, /* TODO: more specific */
254
_("Failed to parse email address"));
258
g_free (email_address);
259
g_object_unref (mail);
265
GOA_ERROR_FAILED, /* TODO: more specific */
266
_("Cannot do SMTP authentication without a domain"));
275
smtp_auth_get_password (GoaSmtpAuth *auth,
276
GCancellable *cancellable,
283
if (auth->password != NULL)
285
password = g_strdup (auth->password);
287
else if (auth->provider != NULL && auth->object != NULL)
289
GVariant *credentials;
290
credentials = goa_utils_lookup_credentials_sync (auth->provider,
294
if (credentials == NULL)
296
g_prefix_error (error, "Error looking up credentials for SMTP in keyring: ");
299
if (!g_variant_lookup (credentials, "smtp-password", "s", &password))
303
GOA_ERROR_FAILED, /* TODO: more specific */
304
_("Did not find smtp-password in credentials"));
305
g_variant_unref (credentials);
308
g_variant_unref (credentials);
314
GOA_ERROR_FAILED, /* TODO: more specific */
315
_("Cannot do SMTP authentication without a password"));
323
/* ---------------------------------------------------------------------------------------------------- */
326
goa_smtp_auth_finalize (GObject *object)
328
GoaSmtpAuth *auth = GOA_SMTP_AUTH (object);
330
g_clear_object (&auth->provider);
331
g_clear_object (&auth->object);
332
g_free (auth->domain);
333
g_free (auth->username);
334
g_free (auth->password);
336
G_OBJECT_CLASS (goa_smtp_auth_parent_class)->finalize (object);
340
goa_smtp_auth_get_property (GObject *object,
345
GoaSmtpAuth *auth = GOA_SMTP_AUTH (object);
350
g_value_set_object (value, auth->provider);
354
g_value_set_object (value, auth->object);
358
g_value_set_string (value, auth->domain);
362
g_value_set_string (value, auth->username);
366
g_value_set_string (value, auth->password);
370
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376
goa_smtp_auth_set_property (GObject *object,
381
GoaSmtpAuth *auth = GOA_SMTP_AUTH (object);
386
auth->provider = g_value_dup_object (value);
390
auth->object = g_value_dup_object (value);
394
auth->domain = g_value_dup_string (value);
398
auth->username = g_value_dup_string (value);
402
auth->password = g_value_dup_string (value);
406
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411
/* ---------------------------------------------------------------------------------------------------- */
415
goa_smtp_auth_init (GoaSmtpAuth *client)
420
goa_smtp_auth_class_init (GoaSmtpAuthClass *klass)
422
GObjectClass *gobject_class;
423
GoaMailAuthClass *auth_class;
425
gobject_class = G_OBJECT_CLASS (klass);
426
gobject_class->finalize = goa_smtp_auth_finalize;
427
gobject_class->get_property = goa_smtp_auth_get_property;
428
gobject_class->set_property = goa_smtp_auth_set_property;
430
auth_class = GOA_MAIL_AUTH_CLASS (klass);
431
auth_class->is_needed = goa_smtp_auth_is_needed;
432
auth_class->run_sync = goa_smtp_auth_run_sync;
433
auth_class->starttls_sync = goa_smtp_auth_starttls_sync;
436
* GoaSmtpAuth:provider:
438
* The #GoaProvider object for the account or %NULL.
440
g_object_class_install_property (gobject_class,
442
g_param_spec_object ("provider",
448
G_PARAM_CONSTRUCT_ONLY |
449
G_PARAM_STATIC_STRINGS));
452
* GoaSmtpAuth:object:
454
* The #GoaObject object for the account.
456
g_object_class_install_property (gobject_class,
458
g_param_spec_object ("object",
464
G_PARAM_CONSTRUCT_ONLY |
465
G_PARAM_STATIC_STRINGS));
468
* GoaSmtpAuth:domain:
470
* The domail or %NULL.
472
* If this is %NULL, the domain is obtained from the
473
* email address associated with the #GoaObject.
475
g_object_class_install_property (gobject_class,
477
g_param_spec_string ("domain",
483
G_PARAM_CONSTRUCT_ONLY |
484
G_PARAM_STATIC_STRINGS));
487
* GoaSmtpAuth:user-name:
491
g_object_class_install_property (gobject_class,
493
g_param_spec_string ("user-name",
499
G_PARAM_CONSTRUCT_ONLY |
500
G_PARAM_STATIC_STRINGS));
503
* GoaSmtpAuth:password:
505
* The password or %NULL.
507
* If this is %NULL, the credentials are looked up using
508
* goa_utils_lookup_credentials_sync() using the
509
* #GoaSmtpAuth:provider and #GoaSmtpAuth:object for @provider and
510
* @object. The credentials are expected to be a %G_VARIANT_VARDICT
511
* and the key <literal>smtp-password</literal> is used to look up
514
g_object_class_install_property (gobject_class,
516
g_param_spec_string ("password",
522
G_PARAM_CONSTRUCT_ONLY |
523
G_PARAM_STATIC_STRINGS));
526
/* ---------------------------------------------------------------------------------------------------- */
530
* @provider: (allow-none): A #GoaPlainProvider or %NULL.
531
* @object: (allow-none): An account object or %NULL.
532
* @domain: (allow-none): The domain to use or %NULL to look it up
533
* (see the #GoaSmtpAuth:domain property).
534
* @username: The user name to use.
535
* @password: (allow-none): The password to use or %NULL to look it up
536
* (see the #GoaSmtpAuth:password property).
538
* Creates a new #GoaMailAuth to be used for username/password
539
* authentication using LOGIN or PLAIN over SMTP.
541
* Returns: (type GoaSmtpAuth): A #GoaSmtpAuth. Free with
545
goa_smtp_auth_new (GoaProvider *provider,
548
const gchar *username,
549
const gchar *password)
551
g_return_val_if_fail (provider == NULL || GOA_IS_PROVIDER (provider), NULL);
552
g_return_val_if_fail (object == NULL || GOA_IS_OBJECT (object), NULL);
553
g_return_val_if_fail (username != NULL, NULL);
554
return GOA_MAIL_AUTH (g_object_new (GOA_TYPE_SMTP_AUTH,
555
"provider", provider,
558
"user-name", username,
559
"password", password,
563
/* ---------------------------------------------------------------------------------------------------- */
566
goa_smtp_auth_is_login (GoaSmtpAuth *auth)
568
return auth->login_supported;
572
goa_smtp_auth_is_plain (GoaSmtpAuth *auth)
574
return auth->plain_supported;
577
/* ---------------------------------------------------------------------------------------------------- */
580
goa_smtp_auth_is_needed (GoaMailAuth *_auth)
582
GoaSmtpAuth *auth = GOA_SMTP_AUTH (_auth);
583
return auth->auth_supported;
586
/* ---------------------------------------------------------------------------------------------------- */
589
goa_smtp_auth_run_sync (GoaMailAuth *_auth,
590
GCancellable *cancellable,
593
GoaSmtpAuth *auth = GOA_SMTP_AUTH (_auth);
594
GDataInputStream *input;
595
GDataOutputStream *output;
597
gchar *auth_arg_base64;
598
gchar *auth_arg_plain;
603
gsize auth_arg_plain_len;
605
auth_arg_base64 = NULL;
606
auth_arg_plain = NULL;
614
password = smtp_auth_get_password (auth, cancellable, error);
615
if (password == NULL)
618
domain = smtp_auth_get_domain (auth, error);
622
input = goa_mail_auth_get_input (_auth);
623
output = goa_mail_auth_get_output (_auth);
625
/* Check the greeting, if there is one */
627
if (!auth->greeting_absent)
629
if (!smtp_auth_check_greeting (input, cancellable, error))
635
request = g_strdup_printf ("EHLO %s\r\n", domain);
636
g_debug ("> %s", request);
637
if (!g_data_output_stream_put_string (output, request, cancellable, error))
639
g_clear_pointer (&request, g_free);
641
/* Check which SASL mechanisms are supported */
644
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
645
if (response == NULL)
647
g_debug ("< %s", response);
648
if (smtp_auth_check_421 (response, error))
650
if (smtp_auth_check_not_250 (response, error))
653
if (g_str_has_prefix (response + 4, "AUTH"))
655
auth->auth_supported = TRUE;
656
if (strstr (response, "PLAIN") != NULL)
657
auth->plain_supported = TRUE;
658
else if (strstr (response, "LOGIN") != NULL)
659
auth->login_supported = TRUE;
662
if (response[3] == '-')
667
else if (!auth->auth_supported)
672
else if (!auth->login_supported && !auth->plain_supported)
676
GOA_ERROR_NOT_SUPPORTED,
677
_("Unknown authentication mechanism"));
680
g_clear_pointer (&response, g_free);
682
/* Try different SASL mechanisms */
684
if (auth->plain_supported)
688
auth_arg_plain = g_strdup_printf ("%s%c%s%c%s", auth->username, '\0', auth->username, '\0', password);
689
auth_arg_plain_len = 2 * strlen (auth->username) + 2 + strlen (password);
690
auth_arg_base64 = g_base64_encode ((guchar *) auth_arg_plain, auth_arg_plain_len);
692
request = g_strdup_printf ("AUTH PLAIN %s\r\n", auth_arg_base64);
693
g_debug ("> AUTH PLAIN ********************");
694
if (!g_data_output_stream_put_string (output, request, cancellable, error))
696
g_clear_pointer (&request, g_free);
702
auth_arg_plain = g_strdup (auth->username);
703
auth_arg_plain_len = strlen (auth->username);
704
auth_arg_base64 = g_base64_encode ((guchar *) auth_arg_plain, auth_arg_plain_len);
706
request = g_strdup_printf ("AUTH LOGIN %s\r\n", auth_arg_base64);
707
g_debug ("> AUTH LOGIN ********************");
708
if (!g_data_output_stream_put_string (output, request, cancellable, error))
710
g_clear_pointer (&request, g_free);
712
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
713
if (response == NULL)
715
g_debug ("< %s", response);
716
if (smtp_auth_check_not_334_login_password (response, error))
719
g_free (auth_arg_plain);
720
g_free (auth_arg_base64);
722
auth_arg_plain = g_strdup (password);
723
auth_arg_plain_len = strlen (password);
724
auth_arg_base64 = g_base64_encode ((guchar *) auth_arg_plain, auth_arg_plain_len);
726
request = g_strdup_printf ("%s\r\n", auth_arg_base64);
727
g_debug ("> ********************");
728
if (!g_data_output_stream_put_string (output, request, cancellable, error))
730
g_clear_pointer (&request, g_free);
733
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
734
if (response == NULL)
736
g_debug ("< %s", response);
737
if (smtp_auth_check_not_235 (response, error))
739
g_clear_pointer (&response, g_free);
744
g_free (auth_arg_base64);
745
g_free (auth_arg_plain);
753
/* ---------------------------------------------------------------------------------------------------- */
756
goa_smtp_auth_starttls_sync (GoaMailAuth *_auth,
757
GCancellable *cancellable,
760
GoaSmtpAuth *auth = GOA_SMTP_AUTH (_auth);
761
GDataInputStream *input;
762
GDataOutputStream *output;
764
gboolean starttls_supported;
769
starttls_supported = FALSE;
776
domain = smtp_auth_get_domain (auth, error);
780
input = goa_mail_auth_get_input (_auth);
781
output = goa_mail_auth_get_output (_auth);
783
/* Check the greeting */
785
if (!smtp_auth_check_greeting (input, cancellable, error))
790
request = g_strdup_printf ("EHLO %s\r\n", domain);
791
g_debug ("> %s", request);
792
if (!g_data_output_stream_put_string (output, request, cancellable, error))
794
g_clear_pointer (&request, g_free);
796
/* Check if STARTTLS is supported or not */
799
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
800
if (response == NULL)
802
g_debug ("< %s", response);
803
if (smtp_auth_check_421 (response, error))
805
if (smtp_auth_check_not_250 (response, error))
808
if (g_str_has_prefix (response + 4, "STARTTLS"))
809
starttls_supported = TRUE;
811
if (response[3] == '-')
816
else if (!starttls_supported)
820
GOA_ERROR_NOT_SUPPORTED,
821
_("Server does not support STARTTLS"));
824
g_clear_pointer (&response, g_free);
828
request = g_strdup ("STARTTLS\r\n");
829
g_debug ("> %s", request);
830
if (!g_data_output_stream_put_string (output, request, cancellable, error))
832
g_clear_pointer (&request, g_free);
834
response = g_data_input_stream_read_line (input, NULL, cancellable, error);
835
if (response == NULL)
837
g_debug ("< %s", response);
838
if (smtp_auth_check_454 (response, error))
840
if (smtp_auth_check_not_220 (response, error))
842
g_clear_pointer (&response, g_free);
844
/* There won't be a greeting after this */
845
auth->greeting_absent = TRUE;