Subject: [PATCH] implement POP3 CRAM-MD5 AUTH diff --git a/libsylph/pop.c b/libsylph/pop.c index 8cb7f5c..5b840a6 100644 --- a/libsylph/pop.c +++ b/libsylph/pop.c @@ -35,6 +35,8 @@ #include "pop.h" #include "md5.h" +#include "md5_hmac.h" +#include "base64.h" #include "prefs.h" #include "prefs_account.h" #include "utils.h" @@ -45,6 +47,8 @@ gint pop3_greeting_recv (Pop3Session *session, gint pop3_getauth_user_send (Pop3Session *session); gint pop3_getauth_pass_send (Pop3Session *session); gint pop3_getauth_apop_send (Pop3Session *session); +gint pop3_getauth_crammd5_send (Pop3Session *session); +gint pop3_getauth_crammd5_digest_send(Pop3Session *session); #if USE_SSL gint pop3_stls_send (Pop3Session *session); gint pop3_stls_recv (Pop3Session *session); @@ -140,6 +144,45 @@ gint pop3_getauth_pass_send(Pop3Session *session) return PS_SUCCESS; } +gint pop3_getauth_crammd5_send(Pop3Session *session) +{ + g_return_val_if_fail(session->user != NULL, -1); + g_return_val_if_fail(session->pass != NULL, -1); + + session->state = POP3_GETAUTH_CRAM_MD5; + pop3_gen_send(session, "AUTH CRAM-MD5"); + return PS_SUCCESS; +} + +gint pop3_getauth_crammd5_digest_send(Pop3Session *session) +{ + gchar hexdigest[33]; + gchar *response; + gchar *response64; + + g_return_val_if_fail(session->user != NULL, -1); + g_return_val_if_fail(session->pass != NULL, -1); + g_return_val_if_fail(session->digest != NULL, -1); + + md5_hex_hmac(hexdigest, (guchar *)session->digest, session->digest_len, + (guchar *)session->pass, strlen(session->pass)); + + response = g_strdup_printf("%s %s", session->user, hexdigest); + log_print("POP3> [Encoded: %s]\n", response); + response64 = g_malloc((strlen(response) + 3) * 2 + 1); + base64_encode(response64, (guchar *)response, strlen(response)); + + pop3_gen_send(session, response64); + session->state = POP3_GETAUTH_CRAM_MD5_DIGEST; + + g_free(response); + g_free(response64); + g_free(session->digest); + session->digest = NULL; + + return PS_SUCCESS; +} + gint pop3_getauth_apop_send(Pop3Session *session) { gchar *start, *end; @@ -193,6 +236,23 @@ gint pop3_getrange_stat_send(Pop3Session *session) return PS_SUCCESS; } +gint pop3_digest_crammd5_recv(Pop3Session *session, const gchar *msg) +{ + if (msg) log_print("POP3< %s\n", msg); + + if(!msg || msg[0] != '+' || msg[1] != ' ') { + return PS_ERROR; + } + + session->digest = g_malloc(strlen(msg + 2) + 1); + session->digest_len = base64_decode((guchar *)session->digest, msg + 2, -1); + session->digest[session->digest_len] = 0; + + log_print("POP3< [Decoded: %s]\n", session->digest); + + return PS_SUCCESS; +} + gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg) { if (sscanf(msg, "%d %lld", &session->count, &session->total_bytes) != 2) { @@ -703,6 +763,8 @@ Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg) case POP3_GETAUTH_USER: case POP3_GETAUTH_PASS: case POP3_GETAUTH_APOP: + case POP3_GETAUTH_CRAM_MD5: + case POP3_GETAUTH_CRAM_MD5_DIGEST: log_warning(_("error occurred on authentication\n")); ok = PS_AUTHFAIL; break; @@ -738,6 +800,7 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg) body = msg; if (pop3_session->state != POP3_GETRANGE_UIDL_RECV && + pop3_session->state != POP3_GETAUTH_CRAM_MD5 && pop3_session->state != POP3_GETSIZE_LIST_RECV) { val = pop3_ok(pop3_session, msg); if (val != PS_SUCCESS) { @@ -772,19 +835,33 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg) val = pop3_stls_send(pop3_session); else #endif - if (pop3_session->ac_prefs->use_apop_auth) + switch(pop3_session->ac_prefs->pop_auth_type) { + case POP3_AUTH_APOP: val = pop3_getauth_apop_send(pop3_session); - else + break; + case POP3_AUTH_LOGIN: val = pop3_getauth_user_send(pop3_session); + break; + case POP3_AUTH_CRAM_MD5: + val = pop3_getauth_crammd5_send(pop3_session); + break; + } break; #if USE_SSL case POP3_STLS: if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS) return -1; - if (pop3_session->ac_prefs->use_apop_auth) + switch(pop3_session->ac_prefs->pop_auth_type) { + case POP3_AUTH_APOP: val = pop3_getauth_apop_send(pop3_session); - else + break; + case POP3_AUTH_LOGIN: val = pop3_getauth_user_send(pop3_session); + break; + case POP3_AUTH_CRAM_MD5: + val = pop3_getauth_crammd5_send(pop3_session); + break; + } break; #endif case POP3_GETAUTH_USER: @@ -792,11 +869,18 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg) break; case POP3_GETAUTH_PASS: case POP3_GETAUTH_APOP: + case POP3_GETAUTH_CRAM_MD5_DIGEST: if (pop3_session->auth_only) val = pop3_logout_send(pop3_session); else val = pop3_getrange_stat_send(pop3_session); break; + case POP3_GETAUTH_CRAM_MD5: + if ((val = pop3_digest_crammd5_recv(pop3_session, body)) != PS_SUCCESS) + return -1; + else + val = pop3_getauth_crammd5_digest_send(pop3_session); + break; case POP3_GETRANGE_STAT: if ((val = pop3_getrange_stat_recv(pop3_session, body)) != PS_SUCCESS) return -1; diff --git a/libsylph/pop.h b/libsylph/pop.h index 050caf5..f8c94a2 100644 --- a/libsylph/pop.h +++ b/libsylph/pop.h @@ -36,6 +36,12 @@ typedef struct _Pop3Session Pop3Session; #define POP3_SESSION(obj) ((Pop3Session *)obj) +typedef enum { + POP3_AUTH_LOGIN = 1 << 0, /* default unencrypted user/pass auth */ + POP3_AUTH_APOP = 1 << 1, /* APop method */ + POP3_AUTH_CRAM_MD5 = 1 << 2, /* cram-md5 digest */ +} Pop3AuthType; + typedef enum { POP3_READY, POP3_GREETING, @@ -43,6 +49,8 @@ typedef enum { POP3_GETAUTH_USER, POP3_GETAUTH_PASS, POP3_GETAUTH_APOP, + POP3_GETAUTH_CRAM_MD5, + POP3_GETAUTH_CRAM_MD5_DIGEST, POP3_GETRANGE_STAT, POP3_GETRANGE_LAST, POP3_GETRANGE_UIDL, @@ -114,8 +122,10 @@ struct _Pop3Session PrefsAccount *ac_prefs; gchar *greeting; + gchar *digest; gchar *user; gchar *pass; + gint digest_len; gint count; gint64 total_bytes; gint cur_msg; diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c index 1aecba9..2a35af9 100644 --- a/libsylph/prefs_account.c +++ b/libsylph/prefs_account.c @@ -34,6 +34,7 @@ #include "customheader.h" #include "account.h" #include "utils.h" +#include "pop.h" static PrefsAccount tmp_ac_prefs; @@ -54,7 +55,7 @@ static PrefParam param[] = { {"inbox", "inbox", &tmp_ac_prefs.inbox, P_STRING}, /* Receive */ - {"use_apop_auth", "FALSE", &tmp_ac_prefs.use_apop_auth, P_BOOL}, + {"pop_auth_method", "0", &tmp_ac_prefs.pop_auth_type, P_ENUM}, {"remove_mail", "TRUE", &tmp_ac_prefs.rmmail, P_BOOL}, {"message_leave_time", "7", &tmp_ac_prefs.msg_leave_time, P_INT}, {"get_all_mail", "FALSE", &tmp_ac_prefs.getall, P_BOOL}, @@ -226,7 +227,7 @@ void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label) if (ac_prefs->protocol == A_APOP) { debug_print("converting protocol A_APOP to new prefs.\n"); ac_prefs->protocol = A_POP3; - ac_prefs->use_apop_auth = TRUE; + ac_prefs->pop_auth_type = POP3_AUTH_APOP; } custom_header_read_config(ac_prefs); diff --git a/libsylph/prefs_account.h b/libsylph/prefs_account.h index ad899f8..249711b 100644 --- a/libsylph/prefs_account.h +++ b/libsylph/prefs_account.h @@ -82,7 +82,7 @@ struct _PrefsAccount gchar *tmp_pass; /* Receive */ - gboolean use_apop_auth; + gint pop_auth_type; gboolean rmmail; gint msg_leave_time; gboolean getall; diff --git a/src/prefs_account_dialog.c b/src/prefs_account_dialog.c index e9cba13..5d8b638 100644 --- a/src/prefs_account_dialog.c +++ b/src/prefs_account_dialog.c @@ -52,6 +52,7 @@ #include "alertpanel.h" #include "smtp.h" #include "imap.h" +#include "pop.h" #include "plugin.h" static gboolean cancelled; @@ -84,7 +85,7 @@ static struct Basic { static struct Receive { GtkWidget *pop3_frame; - GtkWidget *use_apop_chkbtn; + GtkWidget *pop_auth_type_optmenu; GtkWidget *rmmail_chkbtn; GtkWidget *leave_time_entry; GtkWidget *getall_chkbtn; @@ -227,9 +228,14 @@ static void prefs_account_protocol_set_data_from_optmenu(PrefParam *pparam); static void prefs_account_protocol_set_optmenu (PrefParam *pparam); static void prefs_account_protocol_activated (GtkMenuItem *menuitem); +static void prefs_account_pop_auth_type_set_data_from_optmenu + (PrefParam *pparam); +static void prefs_account_pop_auth_type_set_optmenu (PrefParam *pparam); + static void prefs_account_imap_auth_type_set_data_from_optmenu (PrefParam *pparam); static void prefs_account_imap_auth_type_set_optmenu (PrefParam *pparam); + static void prefs_account_smtp_auth_type_set_data_from_optmenu (PrefParam *pparam); static void prefs_account_smtp_auth_type_set_optmenu (PrefParam *pparam); @@ -270,8 +276,9 @@ static PrefsUIData ui_data[] = { prefs_set_data_from_entry, prefs_set_entry}, /* Receive */ - {"use_apop_auth", &receive.use_apop_chkbtn, - prefs_set_data_from_toggle, prefs_set_toggle}, + {"pop_auth_method", &receive.pop_auth_type_optmenu, + prefs_account_pop_auth_type_set_data_from_optmenu, + prefs_account_pop_auth_type_set_optmenu}, {"remove_mail", &receive.rmmail_chkbtn, prefs_set_data_from_toggle, prefs_set_toggle}, {"message_leave_time", &receive.leave_time_entry, @@ -893,7 +900,7 @@ static void prefs_account_receive_create(void) GtkWidget *vbox1; GtkWidget *frame1; GtkWidget *vbox2; - GtkWidget *use_apop_chkbtn; + GtkWidget *pop_auth_type_optmenu; GtkWidget *rmmail_chkbtn; GtkWidget *hbox_spc; GtkWidget *leave_time_label; @@ -910,6 +917,7 @@ static void prefs_account_receive_create(void) GtkWidget *inbox_btn; GtkWidget *imap_frame; + GtkWidget *optmenu_pop; GtkWidget *optmenu; GtkWidget *optmenu_menu; GtkWidget *menuitem; @@ -936,8 +944,26 @@ static void prefs_account_receive_create(void) gtk_container_add (GTK_CONTAINER (frame1), vbox2); gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8); - PACK_CHECK_BUTTON (vbox2, use_apop_chkbtn, - _("Use secure authentication (APOP)")); + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0); + + label = gtk_label_new (_("Authentication method")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox1), label, FALSE, FALSE, 0); + + optmenu = gtk_option_menu_new (); + gtk_widget_show (optmenu); + gtk_box_pack_start (GTK_BOX (hbox1), optmenu, FALSE, FALSE, 0); + + optmenu_menu = gtk_menu_new (); + + MENUITEM_ADD (optmenu_menu, menuitem, _("Unencrypted (standard)"), POP3_AUTH_LOGIN); + MENUITEM_ADD (optmenu_menu, menuitem, _("Use secure authentication (APOP)"), POP3_AUTH_APOP); + MENUITEM_ADD (optmenu_menu, menuitem, "CRAM-MD5", POP3_AUTH_CRAM_MD5); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optmenu), optmenu_menu); + optmenu_pop = optmenu; PACK_CHECK_BUTTON (vbox2, rmmail_chkbtn, _("Remove messages on server when received")); @@ -1106,7 +1132,7 @@ static void prefs_account_receive_create(void) _("`Get all' checks for new messages on this account")); receive.pop3_frame = frame1; - receive.use_apop_chkbtn = use_apop_chkbtn; + receive.pop_auth_type_optmenu = optmenu_pop; receive.rmmail_chkbtn = rmmail_chkbtn; receive.leave_time_entry = leave_time_entry; receive.getall_chkbtn = getall_chkbtn; @@ -2447,7 +2473,7 @@ static void prefs_account_protocol_set_optmenu(PrefParam *pparam) gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); } -static void prefs_account_imap_auth_type_set_data_from_optmenu(PrefParam *pparam) +static void prefs_account_auth_type_set_data_from_optmenu(PrefParam *pparam) { PrefsUIData *ui_data; GtkWidget *menu; @@ -2463,10 +2489,21 @@ static void prefs_account_imap_auth_type_set_data_from_optmenu(PrefParam *pparam (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)); } -static void prefs_account_imap_auth_type_set_optmenu(PrefParam *pparam) +static void prefs_account_imap_auth_type_set_data_from_optmenu(PrefParam *pparam) +{ + prefs_account_auth_type_set_data_from_optmenu(pparam); +} + +static void prefs_account_pop_auth_type_set_data_from_optmenu(PrefParam *pparam) +{ + prefs_account_auth_type_set_data_from_optmenu(pparam); +} + +static void prefs_account_auth_type_set_optmenu(PrefParam *pparam, gboolean is_imap) { PrefsUIData *ui_data; - IMAPAuthType type = *((IMAPAuthType *)pparam->data); + IMAPAuthType itype; + Pop3AuthType ptype; GtkOptionMenu *optmenu; GtkWidget *menu; GtkWidget *menuitem; @@ -2477,19 +2514,38 @@ static void prefs_account_imap_auth_type_set_optmenu(PrefParam *pparam) optmenu = GTK_OPTION_MENU(*ui_data->widget); - switch (type) { - case IMAP_AUTH_LOGIN: - gtk_option_menu_set_history(optmenu, 1); - break; - case IMAP_AUTH_PLAIN: - gtk_option_menu_set_history(optmenu, 2); - break; - case IMAP_AUTH_CRAM_MD5: - gtk_option_menu_set_history(optmenu, 3); - break; - case 0: - default: - gtk_option_menu_set_history(optmenu, 0); + if (is_imap) { + itype = *((IMAPAuthType *)pparam->data); + switch (itype) { + case IMAP_AUTH_LOGIN: + gtk_option_menu_set_history(optmenu, 1); + break; + case IMAP_AUTH_PLAIN: + gtk_option_menu_set_history(optmenu, 2); + break; + case IMAP_AUTH_CRAM_MD5: + gtk_option_menu_set_history(optmenu, 3); + break; + case 0: + default: + gtk_option_menu_set_history(optmenu, 0); + } + } else { + ptype = *((Pop3AuthType *)pparam->data); + switch (ptype) { + case POP3_AUTH_LOGIN: + gtk_option_menu_set_history(optmenu, 0); + break; + case POP3_AUTH_APOP: + gtk_option_menu_set_history(optmenu, 1); + break; + case POP3_AUTH_CRAM_MD5: + gtk_option_menu_set_history(optmenu, 2); + break; + case 0: + default: + gtk_option_menu_set_history(optmenu, 0); + } } menu = gtk_option_menu_get_menu(optmenu); @@ -2497,6 +2553,16 @@ static void prefs_account_imap_auth_type_set_optmenu(PrefParam *pparam) gtk_menu_item_activate(GTK_MENU_ITEM(menuitem)); } +static void prefs_account_imap_auth_type_set_optmenu(PrefParam *pparam) +{ + prefs_account_auth_type_set_optmenu(pparam, TRUE); +} + +static void prefs_account_pop_auth_type_set_optmenu(PrefParam *pparam) +{ + prefs_account_auth_type_set_optmenu(pparam, FALSE); +} + static void prefs_account_smtp_auth_type_set_data_from_optmenu(PrefParam *pparam) { PrefsUIData *ui_data; diff --git a/src/rpop3.c b/src/rpop3.c index 571671f..3f59aa0 100644 --- a/src/rpop3.c +++ b/src/rpop3.c @@ -884,7 +884,8 @@ static gint rpop3_session_recv_msg(Session *session, const gchar *msg) val = pop3_stls_send(pop3_session); else #endif - if (pop3_session->ac_prefs->use_apop_auth) + /* XXX FIXME: need switch block to deal with POP3_AUTH_CRAM_MD5 just like in libsylph/pop.c */ + if (pop3_session->ac_prefs->pop_auth_type == POP3_AUTH_APOP) val = pop3_getauth_apop_send(pop3_session); else val = pop3_getauth_user_send(pop3_session); @@ -893,7 +894,8 @@ static gint rpop3_session_recv_msg(Session *session, const gchar *msg) case POP3_STLS: if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS) break; - if (pop3_session->ac_prefs->use_apop_auth) + /* XXX FIXME: need switch block to deal with POP3_AUTH_CRAM_MD5 just like in libsylph/pop.c */ + if (pop3_session->ac_prefs->pop_auth_type == POP3_AUTH_APOP) val = pop3_getauth_apop_send(pop3_session); else val = pop3_getauth_user_send(pop3_session); -- 2.20.1