From 61a496ffcad857b0dac176861206c4a98edc1620 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 1 Mar 2001 22:23:23 +0000 Subject: Take a GByteArray as input as well. Comment that you can pass %NULL for * camel-sasl.c (camel_sasl_challenge): Take a GByteArray as input as well. Comment that you can pass %NULL for @token to get the initial auth data for mechanisms that are client-initiated. (camel_sasl_challenge_base64): Convenience function for protocols that use base64-encoded SASL. (camel_sasl_authenticated): Implement this... (it was prototyped already) (camel_sasl_new): Function to take a service name, a mechanism name, and a CamelService, and return a CamelSasl for it. (camel_sasl_authtype, camel_sasl_authtype_list): Functions to return CamelServiceAuthType information about SASL mechanisms, to allow providers to deal with them generically. * camel-sasl-anonymous.c, camel-sasl-plain.c: Update/simplify for CamelSasl changes. Both of these are single-round (client-initiated) mechanisms, so they don't need to keep state. (camel_sasl_plain_new): Removed; use camel_sasl_new instead. (Can't get rid of camel_sasl_anonymous_new though...) * camel-sasl-cram-md5.c: Update/simplify for CamelSasl changes. (camel_sasl_cram_md5_new): Removed; use camel_sasl_new instead. (cram_md5_challenge): Use md5_get_digest where possible, and various other minor simplifications. CRAM-MD5 only has a single round, so there's no need to keep track of state. This code is now tested (against Cyrus IMAPd) and known to work. * camel-sasl-kerberos4.h: Update/simplify for CamelSasl changes. Make only a single #ifdef HAVE_KRB4. Remove stuff from priv that isn't needed between rounds. (camel_sasl_kerberos4_new): Removed; use camel_sasl_new instead (krb4_challenge): Fix up the logic I broke in my previous "at least make it compile" fixes, update to match other changes, and remove IMAP-isms that shouldn't be in the generic code. This still isn't tested, because we're stuck behind a NAT right now... svn path=/trunk/; revision=8462 --- camel/ChangeLog | 37 ++++++++ camel/camel-sasl-anonymous.c | 100 ++++++++-------------- camel/camel-sasl-anonymous.h | 1 - camel/camel-sasl-cram-md5.c | 157 +++++++++++++-------------------- camel/camel-sasl-cram-md5.h | 7 +- camel/camel-sasl-kerberos4.c | 200 ++++++++++++++++--------------------------- camel/camel-sasl-kerberos4.h | 7 +- camel/camel-sasl-plain.c | 92 +++++--------------- camel/camel-sasl-plain.h | 7 +- camel/camel-sasl.c | 175 ++++++++++++++++++++++++++++++++++--- camel/camel-sasl.h | 16 +++- 11 files changed, 413 insertions(+), 386 deletions(-) (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index c92aa7d66b..4d17c7e248 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,40 @@ +2001-03-01 Dan Winship + + * camel-sasl.c (camel_sasl_challenge): Take a GByteArray as input + as well. Comment that you can pass %NULL for @token to get the + initial auth data for mechanisms that are client-initiated. + (camel_sasl_challenge_base64): Convenience function for protocols + that use base64-encoded SASL. + (camel_sasl_authenticated): Implement this... (it was prototyped + already) + (camel_sasl_new): Function to take a service name, a mechanism + name, and a CamelService, and return a CamelSasl for it. + (camel_sasl_authtype, camel_sasl_authtype_list): Functions to + return CamelServiceAuthType information about SASL mechanisms, to + allow providers to deal with them generically. + + * camel-sasl-anonymous.c, camel-sasl-plain.c: Update/simplify for + CamelSasl changes. Both of these are single-round + (client-initiated) mechanisms, so they don't need to keep state. + (camel_sasl_plain_new): Removed; use camel_sasl_new instead. + (Can't get rid of camel_sasl_anonymous_new though...) + + * camel-sasl-cram-md5.c: Update/simplify for CamelSasl changes. + (camel_sasl_cram_md5_new): Removed; use camel_sasl_new instead. + (cram_md5_challenge): Use md5_get_digest where possible, and + various other minor simplifications. CRAM-MD5 only has a single + round, so there's no need to keep track of state. This code is now + tested (against Cyrus IMAPd) and known to work. + + * camel-sasl-kerberos4.h: Update/simplify for CamelSasl changes. + Make only a single #ifdef HAVE_KRB4. Remove stuff from priv that + isn't needed between rounds. + (camel_sasl_kerberos4_new): Removed; use camel_sasl_new instead + (krb4_challenge): Fix up the logic I broke in my previous "at + least make it compile" fixes, update to match other changes, and + remove IMAP-isms that shouldn't be in the generic code. This still + isn't tested, because we're stuck behind a NAT right now... + 2001-03-01 Jeffrey Stedfast * camel-sasl-anonymous.c (anon_challenge): Don't base64 encode the diff --git a/camel/camel-sasl-anonymous.c b/camel/camel-sasl-anonymous.c index ec9fe38f07..f6d7d4ec24 100644 --- a/camel/camel-sasl-anonymous.c +++ b/camel/camel-sasl-anonymous.c @@ -30,16 +30,7 @@ static CamelSaslClass *parent_class = NULL; /* Returns the class for a CamelSaslAnonymous */ #define CSA_CLASS(so) CAMEL_SASL_ANONYMOUS_CLASS (CAMEL_OBJECT_GET_CLASS (so)) -static GByteArray *anon_challenge (CamelSasl *sasl, const char *token, CamelException *ex); - -enum { - STATE_AUTH, - STATE_FINAL -}; - -struct _CamelSaslAnonymousPrivate { - int state; -}; +static GByteArray *anon_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); static void camel_sasl_anonymous_class_init (CamelSaslAnonymousClass *camel_sasl_anonymous_class) @@ -52,21 +43,12 @@ camel_sasl_anonymous_class_init (CamelSaslAnonymousClass *camel_sasl_anonymous_c camel_sasl_class->challenge = anon_challenge; } -static void -camel_sasl_anonymous_init (gpointer object, gpointer klass) -{ - CamelSaslAnonymous *sasl_anon = CAMEL_SASL_ANONYMOUS (object); - - sasl_anon->priv = g_new0 (struct _CamelSaslAnonymousPrivate, 1); -} - static void camel_sasl_anonymous_finalize (CamelObject *object) { CamelSaslAnonymous *sasl = CAMEL_SASL_ANONYMOUS (object); g_free (sasl->trace_info); - g_free (sasl->priv); } @@ -82,7 +64,7 @@ camel_sasl_anonymous_get_type (void) sizeof (CamelSaslAnonymousClass), (CamelObjectClassInitFunc) camel_sasl_anonymous_class_init, NULL, - (CamelObjectInitFunc) camel_sasl_anonymous_init, + NULL, (CamelObjectFinalizeFunc) camel_sasl_anonymous_finalize); } @@ -104,60 +86,52 @@ camel_sasl_anonymous_new (CamelSaslAnonTraceType type, const char *trace_info) } static GByteArray * -anon_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +anon_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { CamelSaslAnonymous *sasl_anon = CAMEL_SASL_ANONYMOUS (sasl); - struct _CamelSaslAnonymousPrivate *priv = sasl_anon->priv; CamelInternetAddress *cia; GByteArray *ret = NULL; - char *buf = NULL; - - switch (priv->state) { - case STATE_AUTH: - switch (sasl_anon->type) { - case CAMEL_SASL_ANON_TRACE_EMAIL: - cia = camel_internet_address_new (); - if (camel_internet_address_add (cia, NULL, sasl_anon->trace_info) != 1) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Invalid email address trace information:\n%s"), - sasl_anon->trace_info); - camel_object_unref (CAMEL_OBJECT (cia)); - return NULL; - } - camel_object_unref (CAMEL_OBJECT (cia)); - buf = sasl_anon->trace_info; - break; - case CAMEL_SASL_ANON_TRACE_OPAQUE: - if (strchr (sasl_anon->trace_info, '@')) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Invalid opaque trace information:\n%s"), - sasl_anon->trace_info); - return NULL; - } - buf = sasl_anon->trace_info; - break; - case CAMEL_SASL_ANON_TRACE_EMPTY: - buf = ""; - break; - default: + + if (token) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Authentication failed.")); + return NULL; + } + + switch (sasl_anon->type) { + case CAMEL_SASL_ANON_TRACE_EMAIL: + cia = camel_internet_address_new (); + if (camel_internet_address_add (cia, NULL, sasl_anon->trace_info) != 1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Invalid trace information:\n%s"), + _("Invalid email address trace information:\n%s"), sasl_anon->trace_info); + camel_object_unref (CAMEL_OBJECT (cia)); return NULL; } + camel_object_unref (CAMEL_OBJECT (cia)); + ret = g_byte_array_new (); + g_byte_array_append (ret, sasl_anon->trace_info, strlen (sasl_anon->trace_info)); break; - case STATE_FINAL: - sasl->authenticated = TRUE; - default: + case CAMEL_SASL_ANON_TRACE_OPAQUE: + if (strchr (sasl_anon->trace_info, '@')) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Invalid opaque trace information:\n%s"), + sasl_anon->trace_info); + return NULL; + } + ret = g_byte_array_new (); + g_byte_array_append (ret, sasl_anon->trace_info, strlen (sasl_anon->trace_info)); break; - } - - priv->state++; - - if (buf) { + case CAMEL_SASL_ANON_TRACE_EMPTY: ret = g_byte_array_new (); - g_byte_array_append (ret, buf, strlen (buf)); + break; + default: + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Invalid trace information:\n%s"), + sasl_anon->trace_info); + return NULL; } - + + sasl->authenticated = TRUE; return ret; } diff --git a/camel/camel-sasl-anonymous.h b/camel/camel-sasl-anonymous.h index ba2845d68a..3b27aeb183 100644 --- a/camel/camel-sasl-anonymous.h +++ b/camel/camel-sasl-anonymous.h @@ -43,7 +43,6 @@ typedef enum { typedef struct _CamelSaslAnonymous { CamelSasl parent_object; - struct _CamelSaslAnonymousPrivate *priv; char *trace_info; CamelSaslAnonTraceType type; diff --git a/camel/camel-sasl-cram-md5.c b/camel/camel-sasl-cram-md5.c index 464039b251..458517c5d5 100644 --- a/camel/camel-sasl-cram-md5.c +++ b/camel/camel-sasl-cram-md5.c @@ -22,25 +22,28 @@ #include #include "camel-sasl-cram-md5.h" +#include "camel-mime-utils.h" +#include "camel-service.h" #include #include #include +CamelServiceAuthType camel_sasl_cram_md5_authtype = { + N_("CRAM-MD5"), + + N_("This option will connect to the server using a " + "secure CRAM-MD5 password, if the server supports it."), + + "CRAM-MD5", + TRUE +}; + static CamelSaslClass *parent_class = NULL; /* Returns the class for a CamelSaslCramMd5 */ #define CSCM_CLASS(so) CAMEL_SASL_CRAM_MD5_CLASS (CAMEL_OBJECT_GET_CLASS (so)) -static GByteArray *cram_md5_challenge (CamelSasl *sasl, const char *token, CamelException *ex); - -enum { - STATE_AUTH, - STATE_FINAL -}; - -struct _CamelSaslCramMd5Private { - int state; -}; +static GByteArray *cram_md5_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); static void camel_sasl_cram_md5_class_init (CamelSaslCramMd5Class *camel_sasl_cram_md5_class) @@ -53,24 +56,6 @@ camel_sasl_cram_md5_class_init (CamelSaslCramMd5Class *camel_sasl_cram_md5_class camel_sasl_class->challenge = cram_md5_challenge; } -static void -camel_sasl_cram_md5_init (gpointer object, gpointer klass) -{ - CamelSaslCramMd5 *sasl_cram = CAMEL_SASL_CRAM_MD5 (object); - - sasl_cram->priv = g_new0 (struct _CamelSaslCramMd5Private, 1); -} - -static void -camel_sasl_cram_md5_finalize (CamelObject *object) -{ - CamelSaslCramMd5 *sasl = CAMEL_SASL_CRAM_MD5 (object); - - g_free (sasl->username); - g_free (sasl->priv); -} - - CamelType camel_sasl_cram_md5_get_type (void) { @@ -83,96 +68,72 @@ camel_sasl_cram_md5_get_type (void) sizeof (CamelSaslCramMd5Class), (CamelObjectClassInitFunc) camel_sasl_cram_md5_class_init, NULL, - (CamelObjectInitFunc) camel_sasl_cram_md5_init, - (CamelObjectFinalizeFunc) camel_sasl_cram_md5_finalize); + NULL, + NULL); } return type; } -CamelSasl * -camel_sasl_cram_md5_new (const char *username, const char *passwd) -{ - CamelSaslCramMd5 *sasl_cram; - - if (!username) return NULL; - if (!passwd) return NULL; - - sasl_cram = CAMEL_SASL_CRAM_MD5 (camel_object_new (camel_sasl_cram_md5_get_type ())); - sasl_cram->username = g_strdup (username); - sasl_cram->passwd = g_strdup (passwd); - - return CAMEL_SASL (sasl_cram); -} - /* CRAM-MD5 algorithm: * MD5 ((passwd XOR opad), MD5 ((passwd XOR ipad), timestamp)) */ static GByteArray * -cram_md5_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +cram_md5_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { - CamelSaslCramMd5 *sasl_cram = CAMEL_SASL_CRAM_MD5 (sasl); - struct _CamelSaslCramMd5Private *priv = sasl_cram->priv; + char *passwd; guchar digest[16], md5asc[33], *s, *p; - char *timestamp, *passwd; GByteArray *ret = NULL; guchar ipad[64]; guchar opad[64]; MD5Context ctx; int i, pw_len; - - switch (priv->state) { - case STATE_AUTH: - timestamp = (char *) token; - - passwd = sasl_cram->passwd; - pw_len = strlen (sasl_cram->passwd); - if (pw_len > 64) { - md5_init (&ctx); - md5_update (&ctx, passwd, pw_len); - md5_final (&ctx, digest); - passwd = g_strdup (digest); - pw_len = 16; - } - - memset (ipad, 0, sizeof (ipad)); - memset (opad, 0, sizeof (opad)); + + /* Need to wait for the server */ + if (!token) + return NULL; + + g_return_val_if_fail (sasl->service->url->passwd != NULL, NULL); + + memset (ipad, 0, sizeof (ipad)); + memset (opad, 0, sizeof (opad)); + + passwd = sasl->service->url->passwd; + pw_len = strlen (passwd); + if (pw_len <= 64) { memcpy (ipad, passwd, pw_len); memcpy (opad, passwd, pw_len); - - for (i = 0; i < 64; i++) { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - md5_init (&ctx); - md5_update (&ctx, ipad, 64); - md5_update (&ctx, timestamp, strlen (timestamp)); - md5_final (&ctx, digest); - - md5_init (&ctx); - md5_update (&ctx, opad, 64); - md5_update (&ctx, digest, 16); - md5_final (&ctx, digest); - - /* lowercase hexify that bad-boy... */ - for (s = digest, p = md5asc; p < md5asc + 32; s++, p += 2) - sprintf (p, "%.2x", *s); - - ret = g_byte_array_new (); - g_byte_array_append (ret, sasl_cram->username, strlen (sasl_cram->username)); - g_byte_array_append (ret, " ", 1); - g_byte_array_append (ret, md5asc, strlen (md5asc)); - - break; - case STATE_FINAL: - sasl->authenticated = TRUE; - default: - break; + } else { + md5_get_digest (passwd, pw_len, ipad); + memcpy (opad, ipad, 16); } - - priv->state++; + + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + md5_init (&ctx); + md5_update (&ctx, ipad, 64); + md5_update (&ctx, token->data, token->len); + md5_final (&ctx, digest); + + md5_init (&ctx); + md5_update (&ctx, opad, 64); + md5_update (&ctx, digest, 16); + md5_final (&ctx, digest); + + /* lowercase hexify that bad-boy... */ + for (s = digest, p = md5asc; p < md5asc + 32; s++, p += 2) + sprintf (p, "%.2x", *s); + + ret = g_byte_array_new (); + g_byte_array_append (ret, sasl->service->url->user, strlen (sasl->service->url->user)); + g_byte_array_append (ret, " ", 1); + g_byte_array_append (ret, md5asc, 32); + + sasl->authenticated = TRUE; return ret; } diff --git a/camel/camel-sasl-cram-md5.h b/camel/camel-sasl-cram-md5.h index 12f4d211fa..801dc4a4bb 100644 --- a/camel/camel-sasl-cram-md5.h +++ b/camel/camel-sasl-cram-md5.h @@ -37,9 +37,7 @@ extern "C" { typedef struct _CamelSaslCramMd5 { CamelSasl parent_object; - struct _CamelSaslCramMd5Private *priv; - - char *username, *passwd; + } CamelSaslCramMd5; @@ -52,8 +50,7 @@ typedef struct _CamelSaslCramMd5Class { /* Standard Camel function */ CamelType camel_sasl_cram_md5_get_type (void); -/* public methods */ -CamelSasl * camel_sasl_cram_md5_new (const char *username, const char *passwd); +extern CamelServiceAuthType camel_sasl_cram_md5_authtype; #ifdef __cplusplus } diff --git a/camel/camel-sasl-kerberos4.c b/camel/camel-sasl-kerberos4.c index bb61a440a1..0079a88b25 100644 --- a/camel/camel-sasl-kerberos4.c +++ b/camel/camel-sasl-kerberos4.c @@ -23,17 +23,27 @@ #include #ifdef HAVE_KRB4 + #include /* MIT krb4 des.h #defines _. Sigh. We don't need it. #undef it here * so we get the gettexty _ definition later. */ #undef _ -#endif #include "camel-sasl-kerberos4.h" -#include "camel-mime-utils.h" +#include "camel-service.h" #include +CamelServiceAuthType camel_sasl_kerberos4_authtype = { + N_("Kerberos 4"), + + N_("This option will connect to the server using " + "Kerberos 4 authentication."), + + "KERBEROS_V4", + FALSE +}; + #define KERBEROS_V4_PROTECTION_NONE 1 #define KERBEROS_V4_PROTECTION_INTEGRITY 2 #define KERBEROS_V4_PROTECTION_PRIVACY 4 @@ -43,44 +53,27 @@ static CamelSaslClass *parent_class = NULL; /* Returns the class for a CamelSaslKerberos4 */ #define CSK4_CLASS(so) CAMEL_SASL_KERBEROS4_CLASS (CAMEL_OBJECT_GET_CLASS (so)) -#ifdef HAVE_KRB4 -static GByteArray *krb4_challenge (CamelSasl *sasl, const char *token, CamelException *ex); -#endif - -enum { - STATE_NONCE, - STATE_NONCE_PLUS_ONE, - STATE_FINAL -}; +static GByteArray *krb4_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); struct _CamelSaslKerberos4Private { int state; guint32 nonce_n; guint32 nonce_h; - guint32 plus1; -#ifdef HAVE_KRB4 - KTEXT_ST authenticator; - CREDENTIALS credentials; des_cblock session; des_key_schedule schedule; -#endif /* HAVE_KRB4 */ }; static void camel_sasl_kerberos4_class_init (CamelSaslKerberos4Class *camel_sasl_kerberos4_class) { -#ifdef HAVE_KRB4 CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (camel_sasl_kerberos4_class); -#endif parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ())); /* virtual method overload */ -#ifdef HAVE_KRB4 camel_sasl_class->challenge = krb4_challenge; -#endif } static void @@ -95,10 +88,11 @@ static void camel_sasl_kerberos4_finalize (CamelObject *object) { CamelSaslKerberos4 *sasl = CAMEL_SASL_KERBEROS4 (object); - - g_free (sasl->protocol); - g_free (sasl->username); - g_free (sasl->priv); + + if (sasl->priv) { + memset (sasl->priv, 0, sizeof (sasl->priv)); + g_free (sasl->priv); + } } @@ -121,148 +115,106 @@ camel_sasl_kerberos4_get_type (void) return type; } -CamelSasl * -camel_sasl_kerberos4_new (const char *protocol, const char *username, struct hostent *host) -{ - CamelSaslKerberos4 *sasl_krb4; - - if (!protocol) return NULL; - if (!username) return NULL; - if (!host) return NULL; - -#ifdef HAVE_KRB4 - sasl_krb4 = CAMEL_SASL_KERBEROS4 (camel_object_new (camel_sasl_kerberos4_get_type ())); - sasl_krb4->protocol = g_strdup (protocol); - g_strdown (sasl_krb4->protocol); - sasl_krb4->username = g_strdup (username); - sasl_krb4->host = host; - - return CAMEL_SASL (sasl_krb4); -#else - return NULL; -#endif /* HAVE_KRB4 */ -} - -#ifdef HAVE_KRB4 static GByteArray * -krb4_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +krb4_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { - CamelSaslKerberos4 *sasl_krb4 = CAMEL_SASL_KERBEROS4 (sasl); - struct _CamelSaslKerberos4Private *priv = sasl_krb4->priv; - char *buf = NULL, *data = NULL; + struct _CamelSaslKerberos4Private *priv = CAMEL_SASL_KERBEROS4 (sasl)->priv; GByteArray *ret = NULL; - char *inst, *realm; + char *inst, *realm, *username; struct hostent *h; int status, len; KTEXT_ST authenticator; CREDENTIALS credentials; - des_cblock session; - des_key_schedule schedule; - - if (token) - data = g_strdup (token); - else - goto fail; - + guint32 plus1; + + /* Need to wait for the server */ + if (!token) + return NULL; + switch (priv->state) { - case STATE_NONCE: - if (strlen (data) != 8 || base64_decode_simple (data, 8) != 4) - goto break_and_lose; - - memcpy (&priv->nonce_n, data, 4); + case 0: + if (token->len != 4) + goto lose; + + memcpy (&priv->nonce_n, token->data, 4); priv->nonce_h = ntohl (priv->nonce_n); - + /* Our response is an authenticator including that number. */ - h = sasl_krb4->host; + h = camel_service_gethost (sasl->service, ex); inst = g_strndup (h->h_name, strcspn (h->h_name, ".")); g_strdown (inst); realm = g_strdup (krb_realmofhost (h->h_name)); - status = krb_mk_req (&authenticator, sasl_krb4->protocol, inst, realm, priv->nonce_h); + status = krb_mk_req (&authenticator, sasl->service_name, inst, realm, priv->nonce_h); if (status == KSUCCESS) { - status = krb_get_cred (sasl_krb4->protocol, inst, realm, &credentials); - memcpy (session, credentials.session, sizeof (session)); + status = krb_get_cred (sasl->service_name, inst, realm, &credentials); + memcpy (priv->session, credentials.session, sizeof (priv->session)); memset (&credentials, 0, sizeof (credentials)); } g_free (inst); g_free (realm); - + if (status != KSUCCESS) { camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, _("Could not get Kerberos ticket:\n%s"), krb_err_txt[status]); - goto break_and_lose; + goto lose; } - des_key_sched (&session, schedule); - - buf = base64_encode_simple (authenticator.dat, authenticator.length); + des_key_sched (&priv->session, priv->schedule); + + ret = g_byte_array_new (); + g_byte_array_append (ret, (const guint8 *)authenticator.dat, authenticator.length); break; - case STATE_NONCE_PLUS_ONE: - len = strlen (data); - base64_decode_simple (data, len); - + + case 1: + if (token->len != 8) + goto lose; + /* This one is encrypted. */ - des_ecb_encrypt ((des_cblock *)data, (des_cblock *)data, schedule, 0); - + des_ecb_encrypt ((des_cblock *)token->data, (des_cblock *)token->data, priv->schedule, 0); + /* Check that the returned value is the original nonce plus one. */ - memcpy (&priv->plus1, data, 4); - if (ntohl (priv->plus1) != priv->nonce_h + 1) + memcpy (&plus1, token->data, 4); + if (ntohl (plus1) != priv->nonce_h + 1) goto lose; - + /* "the fifth octet contain[s] a bit-mask specifying the * protection mechanisms supported by the server" */ - if (!(data[4] & KERBEROS_V4_PROTECTION_NONE)) { + if (!(token->data[4] & KERBEROS_V4_PROTECTION_NONE)) { g_warning ("Server does not support `no protection' :-("); - goto break_and_lose; + goto lose; } - - len = strlen (sasl_krb4->username) + 9; + + username = sasl->service->url->user; + len = strlen (username) + 9; len += 8 - len % 8; - data = g_malloc0 (len); - memcpy (data, &priv->nonce_n, 4); - data[4] = KERBEROS_V4_PROTECTION_NONE; - data[5] = data[6] = data[7] = 0; - strcpy (data + 8, sasl_krb4->username); - - des_pcbc_encrypt ((void *)data, (void *)data, len, - schedule, &session, 1); - memset (&session, 0, sizeof (session)); - buf = base64_encode_simple (data, len); - break; - case STATE_FINAL: + ret = g_byte_array_new (); + g_byte_array_set_size (ret, len); + memset (ret->data, 0, len); + memcpy (ret->data, &priv->nonce_n, 4); + ret->data[4] = KERBEROS_V4_PROTECTION_NONE; + ret->data[5] = ret->data[6] = ret->data[7] = 0; + strcpy (ret->data + 8, username); + + des_pcbc_encrypt ((void *)ret->data, (void *)ret->data, len, + priv->schedule, &priv->session, 1); + memset (&priv->session, 0, sizeof (priv->session)); + sasl->authenticated = TRUE; break; - default: - break; } - - g_free (data); + priv->state++; - - if (buf) { - ret = g_byte_array_new (); - g_byte_array_append (ret, buf, strlen (buf)); - g_free (buf); - } - return ret; - - break_and_lose: - /* Get the server out of "waiting for continuation data" mode. */ - g_free (data); - ret = g_byte_array_new (); - g_byte_array_append (ret, "*", 1); - return ret; - + lose: - memset (&session, 0, sizeof (session)); - + memset (&priv->session, 0, sizeof (priv->session)); + if (!camel_exception_is_set (ex)) { camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, _("Bad authentication response from server.")); } - fail: - g_free (data); return NULL; } -#endif HAVE_KRB4 + +#endif /* HAVE_KRB4 */ diff --git a/camel/camel-sasl-kerberos4.h b/camel/camel-sasl-kerberos4.h index 806c758664..38ac9d7e4a 100644 --- a/camel/camel-sasl-kerberos4.h +++ b/camel/camel-sasl-kerberos4.h @@ -40,9 +40,7 @@ extern "C" { typedef struct _CamelSaslKerberos4 { CamelSasl parent_object; struct _CamelSaslKerberos4Private *priv; - - struct hostent *host; - char *protocol, *username; + } CamelSaslKerberos4; @@ -55,8 +53,7 @@ typedef struct _CamelSaslKerberos4Class { /* Standard Camel function */ CamelType camel_sasl_kerberos4_get_type (void); -/* public methods */ -CamelSasl * camel_sasl_kerberos4_new (const char *protocol, const char *username, struct hostent *host); +extern CamelServiceAuthType camel_sasl_kerberos4_authtype; #ifdef __cplusplus } diff --git a/camel/camel-sasl-plain.c b/camel/camel-sasl-plain.c index 6b7a4de2bc..e4e4612dc8 100644 --- a/camel/camel-sasl-plain.c +++ b/camel/camel-sasl-plain.c @@ -22,6 +22,7 @@ #include #include "camel-sasl-plain.h" +#include "camel-service.h" #include static CamelSaslClass *parent_class = NULL; @@ -29,16 +30,7 @@ static CamelSaslClass *parent_class = NULL; /* Returns the class for a CamelSaslPlain */ #define CSP_CLASS(so) CAMEL_SASL_PLAIN_CLASS (CAMEL_OBJECT_GET_CLASS (so)) -static GByteArray *plain_challenge (CamelSasl *sasl, const char *token, CamelException *ex); - -enum { - STATE_LOGIN, - STATE_FINAL -}; - -struct _CamelSaslPlainPrivate { - int state; -}; +static GByteArray *plain_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); static void camel_sasl_plain_class_init (CamelSaslPlainClass *camel_sasl_plain_class) @@ -51,26 +43,6 @@ camel_sasl_plain_class_init (CamelSaslPlainClass *camel_sasl_plain_class) camel_sasl_class->challenge = plain_challenge; } -static void -camel_sasl_plain_init (gpointer object, gpointer klass) -{ - CamelSaslPlain *sasl_plain = CAMEL_SASL_PLAIN (object); - - sasl_plain->priv = g_new0 (struct _CamelSaslPlainPrivate, 1); -} - -static void -camel_sasl_plain_finalize (CamelObject *object) -{ - CamelSaslPlain *sasl = CAMEL_SASL_PLAIN (object); - - g_free (sasl->login); - g_free (sasl->auth_id); - g_free (sasl->passwd); - g_free (sasl->priv); -} - - CamelType camel_sasl_plain_get_type (void) { @@ -83,55 +55,35 @@ camel_sasl_plain_get_type (void) sizeof (CamelSaslPlainClass), (CamelObjectClassInitFunc) camel_sasl_plain_class_init, NULL, - (CamelObjectInitFunc) camel_sasl_plain_init, - (CamelObjectFinalizeFunc) camel_sasl_plain_finalize); + NULL, + NULL); } return type; } -CamelSasl * -camel_sasl_plain_new (const char *login, const char *auth_id, const char *passwd) -{ - CamelSaslPlain *sasl_plain; - - if (!auth_id) return NULL; - if (!passwd) return NULL; - - sasl_plain = CAMEL_SASL_PLAIN (camel_object_new (camel_sasl_plain_get_type ())); - sasl_plain->login = g_strdup (login); - sasl_plain->auth_id = g_strdup (auth_id); - sasl_plain->passwd = g_strdup (passwd); - - return CAMEL_SASL (sasl_plain); -} - static GByteArray * -plain_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +plain_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { - CamelSaslPlain *sasl_plain = CAMEL_SASL_PLAIN (sasl); - struct _CamelSaslPlainPrivate *priv = sasl_plain->priv; GByteArray *buf = NULL; - - switch (priv->state) { - case STATE_LOGIN: - /* FIXME: make sure these are "UTF8-SAFE" */ - buf = g_byte_array_new (); - if (sasl_plain->login) - g_byte_array_append (buf, sasl_plain->login, strlen (sasl_plain->login)); - g_byte_array_append (buf, "", 1); - g_byte_array_append (buf, sasl_plain->auth_id, strlen (sasl_plain->auth_id)); - g_byte_array_append (buf, "", 1); - g_byte_array_append (buf, sasl_plain->passwd, strlen (sasl_plain->passwd)); - break; - case STATE_FINAL: - sasl->authenticated = TRUE; - break; - default: - break; + CamelURL *url = sasl->service->url; + + if (token) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Authentication failed.")); + return NULL; } - - priv->state++; + + g_return_val_if_fail (url->passwd != NULL, NULL); + + /* FIXME: make sure these are "UTF8-SAFE" */ + buf = g_byte_array_new (); + g_byte_array_append (buf, "", 1); + g_byte_array_append (buf, url->user, strlen (url->user)); + g_byte_array_append (buf, "", 1); + g_byte_array_append (buf, url->passwd, strlen (url->passwd)); + + sasl->authenticated = TRUE; return buf; } diff --git a/camel/camel-sasl-plain.h b/camel/camel-sasl-plain.h index cf4d3d4a97..94efb317f9 100644 --- a/camel/camel-sasl-plain.h +++ b/camel/camel-sasl-plain.h @@ -37,9 +37,7 @@ extern "C" { typedef struct _CamelSaslPlain { CamelSasl parent_object; - struct _CamelSaslPlainPrivate *priv; - - char *login, *auth_id, *passwd; + } CamelSaslPlain; @@ -52,9 +50,6 @@ typedef struct _CamelSaslPlainClass { /* Standard Camel function */ CamelType camel_sasl_plain_get_type (void); -/* public methods */ -CamelSasl * camel_sasl_plain_new (const char *login, const char *auth_id, const char *passwd); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/camel-sasl.c b/camel/camel-sasl.c index a5aebe737c..1e7fc9108c 100644 --- a/camel/camel-sasl.c +++ b/camel/camel-sasl.c @@ -22,13 +22,19 @@ #include #include "camel-sasl.h" +#include "camel-mime-utils.h" +#include "camel-service.h" + +#include "camel-sasl-cram-md5.h" +#include "camel-sasl-kerberos4.h" +#include "camel-sasl-plain.h" static CamelObjectClass *parent_class = NULL; /* Returns the class for a CamelSasl */ #define CS_CLASS(so) CAMEL_SASL_CLASS (CAMEL_OBJECT_GET_CLASS (so)) -static GByteArray *sasl_challenge (CamelSasl *sasl, const char *token, CamelException *ex); +static GByteArray *sasl_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); static void camel_sasl_class_init (CamelSaslClass *camel_sasl_class) @@ -39,6 +45,13 @@ camel_sasl_class_init (CamelSaslClass *camel_sasl_class) camel_sasl_class->challenge = sasl_challenge; } +static void +camel_sasl_finalize (CamelSasl *sasl) +{ + g_free (sasl->service_name); + camel_object_unref (CAMEL_OBJECT (sasl->service)); +} + CamelType camel_sasl_get_type (void) { @@ -52,7 +65,7 @@ camel_sasl_get_type (void) (CamelObjectClassInitFunc) camel_sasl_class_init, NULL, NULL, - NULL ); + (CamelObjectFinalizeFunc) camel_sasl_finalize); } return type; @@ -60,7 +73,7 @@ camel_sasl_get_type (void) static GByteArray * -sasl_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +sasl_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { g_warning ("sasl_challenge: Using default implementation!"); return NULL; @@ -68,20 +81,160 @@ sasl_challenge (CamelSasl *sasl, const char *token, CamelException *ex) /** * camel_sasl_challenge: - * @sasl: a sasl object - * @token: a token + * @sasl: a SASL object + * @token: a token, or %NULL * @ex: exception * - * Generate the next sasl challenge to send to the server. + * If @token is %NULL, generate the initial SASL message to send to + * the server. (This will be %NULL if the client doesn't initiate the + * exchange.) Otherwise, @token is a challenge from the server, and + * the return value is the response. * - * Return value: a string containing the base64 encoded sasl challenge - * or NULL on either an error or if the negotiation is complete. If an - * error has occured, @ex will also be set. + * Return value: The SASL response or %NULL. If an error occurred, @ex + * will also be set. **/ GByteArray * -camel_sasl_challenge (CamelSasl *sasl, const char *token, CamelException *ex) +camel_sasl_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) { g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL); - + return CS_CLASS (sasl)->challenge (sasl, token, ex); } + +/** + * camel_sasl_challenge_base64: + * @sasl: a SASL object + * @token: a base64-encoded token + * @ex: exception + * + * As with camel_sasl_challenge(), but the challenge @token and the + * response are both base64-encoded. + * + * Return value: As with camel_sasl_challenge(), but base64-encoded. + **/ +char * +camel_sasl_challenge_base64 (CamelSasl *sasl, const char *token, CamelException *ex) +{ + GByteArray *token_binary, *ret_binary; + char *ret; + int len; + + g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL); + + if (token) { + token_binary = g_byte_array_new (); + len = strlen (token); + g_byte_array_append (token_binary, token, len); + token_binary->len = base64_decode_simple (token_binary->data, len); + } else + token_binary = NULL; + + ret_binary = camel_sasl_challenge (sasl, token_binary, ex); + if (token_binary) + g_byte_array_free (token_binary, TRUE); + if (!ret_binary) + return NULL; + + ret = base64_encode_simple (ret_binary->data, ret_binary->len); + g_byte_array_free (ret_binary, TRUE); + + return ret; +} + +/** + * camel_sasl_authenticated: + * @sasl: a SASL object + * + * Return value: whether or not @sasl has successfully authenticated + * the user. This will be %TRUE after it returns the last needed response. + * The caller must still pass that information on to the server and verify + * that it has accepted it. + **/ +gboolean +camel_sasl_authenticated (CamelSasl *sasl) +{ + return sasl->authenticated; +} + + +/** + * camel_sasl_new: + * @service_name: the SASL service name + * @mechanism: the SASL mechanism + * @service: the CamelService that will be using this SASL + * + * Return value: a new CamelSasl for the given @service_name, + * @mechanism, and @service, or %NULL if the mechanism is not + * supported. + **/ +CamelSasl * +camel_sasl_new (const char *service_name, const char *mechanism, CamelService *service) +{ + CamelSasl *sasl; + + g_return_val_if_fail (service_name != NULL, NULL); + g_return_val_if_fail (mechanism != NULL, NULL); + g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL); + + /* We don't do ANONYMOUS here, because it's a little bit weird. */ + + if (!strcmp (mechanism, "CRAM-MD5")) + sasl = (CamelSasl *)camel_object_new (CAMEL_SASL_CRAM_MD5_TYPE); +#ifdef HAVE_KRB4 + else if (!strcmp (mechanism, "KERBEROS_V4")) + sasl = (CamelSasl *)camel_object_new (CAMEL_SASL_KERBEROS4_TYPE); +#endif + else if (!strcmp (mechanism, "PLAIN")) + sasl = (CamelSasl *)camel_object_new (CAMEL_SASL_PLAIN_TYPE); + else + return NULL; + + sasl->service_name = g_strdup (service_name); + sasl->service = service; + camel_object_ref (CAMEL_OBJECT (service)); + + return sasl; +} + +/** + * camel_sasl_authtype_list: + * + * Return value: a GList of SASL-supported authtypes. The caller must + * free the list, but not the contents. + **/ +GList * +camel_sasl_authtype_list (void) +{ + GList *types = NULL; + + /* We don't do PLAIN here, because it's considered to be + * normal password authentication, just behind SSL. + */ + + types = g_list_prepend (types, &camel_sasl_cram_md5_authtype); +#ifdef HAVE_KRB4 + types = g_list_prepend (types, &camel_sasl_kerberos4_authtype); +#endif + + return types; +} + +/** + * camel_sasl_authtype: + * @mechanism: the SASL mechanism to get an authtype for + * + * Return value: a CamelServiceAuthType for the given mechanism, if + * it is supported. + **/ +CamelServiceAuthType * +camel_sasl_authtype (const char *mechanism) +{ + if (!strcmp (mechanism, "CRAM-MD5")) + return &camel_sasl_cram_md5_authtype; +#ifdef HAVE_KRB4 + else if (!strcmp (mechanism, "KERBEROS_V4")) + return &camel_sasl_kerberos4_authtype; +#endif + else + return NULL; +} diff --git a/camel/camel-sasl.h b/camel/camel-sasl.h index 24201a0b03..02f05eb8cf 100644 --- a/camel/camel-sasl.h +++ b/camel/camel-sasl.h @@ -31,6 +31,7 @@ extern "C" { #include #include #include +#include #define CAMEL_SASL_TYPE (camel_sasl_get_type ()) #define CAMEL_SASL(obj) (CAMEL_CHECK_CAST((obj), CAMEL_SASL_TYPE, CamelSasl)) @@ -40,6 +41,8 @@ extern "C" { typedef struct _CamelSasl { CamelObject parent_object; + char *service_name; + CamelService *service; gboolean authenticated; } CamelSasl; @@ -47,7 +50,7 @@ typedef struct _CamelSasl { typedef struct _CamelSaslClass { CamelObjectClass parent_class; - GByteArray * (*challenge) (CamelSasl *sasl, const char *token, CamelException *ex); + GByteArray * (*challenge) (CamelSasl *sasl, GByteArray *token, CamelException *ex); } CamelSaslClass; @@ -56,9 +59,16 @@ typedef struct _CamelSaslClass { CamelType camel_sasl_get_type (void); /* public methods */ -GByteArray *camel_sasl_challenge (CamelSasl *sasl, const char *token, CamelException *ex); +GByteArray *camel_sasl_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex); +char *camel_sasl_challenge_base64 (CamelSasl *sasl, const char *token, CamelException *ex); -gboolean camel_sasl_authenticated (CamelSasl *sasl); +gboolean camel_sasl_authenticated (CamelSasl *sasl); + +/* utility functions */ +CamelSasl *camel_sasl_new (const char *service_name, const char *mechanism, CamelService *service); + +GList *camel_sasl_authtype_list (void); +CamelServiceAuthType *camel_sasl_authtype (const char *mechanism); #ifdef __cplusplus } -- cgit v1.2.3