]> granicus.if.org Git - curl/commitdiff
sasl: implement EXTERNAL authentication mechanism.
authorPatrick Monnerat <pm@datasphere.ch>
Tue, 27 Jan 2015 16:24:55 +0000 (17:24 +0100)
committerPatrick Monnerat <pm@datasphere.ch>
Tue, 27 Jan 2015 16:24:55 +0000 (17:24 +0100)
  Its use is only enabled by explicit requirement in URL (;AUTH=EXTERNAL) and
by not setting the password.

lib/curl_sasl.c
lib/curl_sasl.h
lib/imap.c
lib/pop3.c
lib/smtp.c

index 347f1a30268e3f94c5c76fe316c8eb7de519acfa..2d55836cd3eaccf8580501a43f2976f2cedd791a 100644 (file)
@@ -368,6 +368,30 @@ static CURLcode sasl_create_login_message(struct SessionHandle *data,
   return Curl_base64_encode(data, valuep, vlen, outptr, outlen);
 }
 
+/*
+ * sasl_create_external_message()
+ *
+ * This is used to generate an already encoded EXTERNAL message containing
+ * the user name ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data    [in]     - The session handle.
+ * user    [in]     - The user name.
+ * outptr  [in/out] - The address where a pointer to newly allocated memory
+ *                    holding the result will be stored upon completion.
+ * outlen  [out]    - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+static CURLcode sasl_create_external_message(struct SessionHandle *data,
+                                          const char *user, char **outptr,
+                                          size_t *outlen)
+{
+  /* This is the same formatting as the login message. */
+  return sasl_create_login_message(data, user, outptr, outlen);
+}
+
 #ifndef CURL_DISABLE_CRYPTO_AUTH
  /*
  * sasl_decode_cram_md5_message()
@@ -1257,7 +1281,7 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
     }
 
     if(strnequal(value, "*", len))
-      sasl->prefmech = SASL_AUTH_ANY;
+      sasl->prefmech = SASL_AUTH_DEFAULT;
     else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) &&
             mechlen == len)
       sasl->prefmech |= mechbit;
@@ -1277,7 +1301,7 @@ void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
   sasl->params = params;           /* Set protocol dependent parameters */
   sasl->state = SASL_STOP;         /* Not yet running */
   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
-  sasl->prefmech = SASL_AUTH_ANY;  /* Prefer all mechanisms */
+  sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
   sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
@@ -1299,6 +1323,7 @@ static void state(struct SASL *sasl,
     "PLAIN",
     "LOGIN",
     "LOGIN_PASSWD",
+    "EXTERNAL",
     "CRAMMD5",
     "DIGESTMD5",
     "DIGESTMD5_RESP",
@@ -1321,6 +1346,23 @@ static void state(struct SASL *sasl,
   sasl->state = newstate;
 }
 
+/*
+ * Curl_sasl_can_authenticate()
+ *
+ * Check if we have enough auth data and capabilities to authenticate.
+ */
+
+bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
+{
+  if(conn->bits.user_passwd)
+    return TRUE;        /* Credentials provided */
+
+  if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
+    return TRUE;        /* Can authenticate without password */
+
+  return FALSE;
+}
+
 /*
  * Curl_sasl_start()
  *
@@ -1345,80 +1387,89 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
 
   /* Calculate the supported authentication mechanism, by decreasing order of
    *      security, as well as the initial response where appropriate */
-#if defined(USE_KERBEROS5)
-  if(enabledmechs & SASL_MECH_GSSAPI) {
-    sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
-    mech = SASL_MECH_STRING_GSSAPI;
-    state1 = SASL_GSSAPI;
-    state2 = SASL_GSSAPI_TOKEN;
-    sasl->authused = SASL_MECH_GSSAPI;
+  if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
+    mech = SASL_MECH_STRING_EXTERNAL;
+    state1 = SASL_EXTERNAL;
+    sasl->authused = SASL_MECH_EXTERNAL;
 
     if(force_ir || data->set.sasl_ir)
-      result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                    conn->passwd,
-                                                    sasl->params->service,
-                                                    sasl->mutual_auth,
-                                                    NULL, &conn->krb5,
-                                                    &resp, &len);
+      result = sasl_create_external_message(data, conn->user, &resp, &len);
   }
-  else
+  else if(conn->bits.user_passwd) {
+#if defined(USE_KERBEROS5)
+    if(enabledmechs & SASL_MECH_GSSAPI) {
+      sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
+      mech = SASL_MECH_STRING_GSSAPI;
+      state1 = SASL_GSSAPI;
+      state2 = SASL_GSSAPI_TOKEN;
+      sasl->authused = SASL_MECH_GSSAPI;
+
+      if(force_ir || data->set.sasl_ir)
+        result = Curl_sasl_create_gssapi_user_message(data, conn->user,
+                                                      conn->passwd,
+                                                      sasl->params->service,
+                                                      sasl->mutual_auth,
+                                                      NULL, &conn->krb5,
+                                                      &resp, &len);
+    }
+    else
 #endif
 #ifndef CURL_DISABLE_CRYPTO_AUTH
-  if(enabledmechs & SASL_MECH_DIGEST_MD5) {
-    mech = SASL_MECH_STRING_DIGEST_MD5;
-    state1 = SASL_DIGESTMD5;
-    sasl->authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if(enabledmechs & SASL_MECH_CRAM_MD5) {
-    mech = SASL_MECH_STRING_CRAM_MD5;
-    state1 = SASL_CRAMMD5;
-    sasl->authused = SASL_MECH_CRAM_MD5;
-  }
-  else
+    if(enabledmechs & SASL_MECH_DIGEST_MD5) {
+      mech = SASL_MECH_STRING_DIGEST_MD5;
+      state1 = SASL_DIGESTMD5;
+      sasl->authused = SASL_MECH_DIGEST_MD5;
+    }
+    else if(enabledmechs & SASL_MECH_CRAM_MD5) {
+      mech = SASL_MECH_STRING_CRAM_MD5;
+      state1 = SASL_CRAMMD5;
+      sasl->authused = SASL_MECH_CRAM_MD5;
+    }
+    else
 #endif
 #ifdef USE_NTLM
-  if(enabledmechs & SASL_MECH_NTLM) {
-    mech = SASL_MECH_STRING_NTLM;
-    state1 = SASL_NTLM;
-    state2 = SASL_NTLM_TYPE2MSG;
-    sasl->authused = SASL_MECH_NTLM;
-
-    if(force_ir || data->set.sasl_ir)
-      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                   &conn->ntlm, &resp, &len);
-    }
-  else
+    if(enabledmechs & SASL_MECH_NTLM) {
+      mech = SASL_MECH_STRING_NTLM;
+      state1 = SASL_NTLM;
+      state2 = SASL_NTLM_TYPE2MSG;
+      sasl->authused = SASL_MECH_NTLM;
+
+      if(force_ir || data->set.sasl_ir)
+        result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                     &conn->ntlm, &resp, &len);
+      }
+    else
 #endif
-  if((((enabledmechs & SASL_MECH_XOAUTH2) &&
-      sasl->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
-    mech = SASL_MECH_STRING_XOAUTH2;
-    state1 = SASL_XOAUTH2;
-    sasl->authused = SASL_MECH_XOAUTH2;
-
-    if(force_ir || data->set.sasl_ir)
-      result = sasl_create_xoauth2_message(data, conn->user,
-                                           conn->xoauth2_bearer, &resp, &len);
-  }
-  else if(enabledmechs & SASL_MECH_LOGIN) {
-    mech = SASL_MECH_STRING_LOGIN;
-    state1 = SASL_LOGIN;
-    state2 = SASL_LOGIN_PASSWD;
-    sasl->authused = SASL_MECH_LOGIN;
-
-    if(force_ir || data->set.sasl_ir)
-      result = sasl_create_login_message(data, conn->user, &resp, &len);
-  }
-  else if(enabledmechs & SASL_MECH_PLAIN) {
-    mech = SASL_MECH_STRING_PLAIN;
-    state1 = SASL_PLAIN;
-    sasl->authused = SASL_MECH_PLAIN;
-
-    if(force_ir || data->set.sasl_ir)
-      result = sasl_create_plain_message(data, conn->user, conn->passwd,
-                                         &resp, &len);
+    if((((enabledmechs & SASL_MECH_XOAUTH2) &&
+        sasl->prefmech != SASL_AUTH_DEFAULT)) || conn->xoauth2_bearer) {
+      mech = SASL_MECH_STRING_XOAUTH2;
+      state1 = SASL_XOAUTH2;
+      sasl->authused = SASL_MECH_XOAUTH2;
+
+      if(force_ir || data->set.sasl_ir)
+        result = sasl_create_xoauth2_message(data, conn->user,
+                                             conn->xoauth2_bearer,
+                                             &resp, &len);
+    }
+    else if(enabledmechs & SASL_MECH_LOGIN) {
+      mech = SASL_MECH_STRING_LOGIN;
+      state1 = SASL_LOGIN;
+      state2 = SASL_LOGIN_PASSWD;
+      sasl->authused = SASL_MECH_LOGIN;
+
+      if(force_ir || data->set.sasl_ir)
+        result = sasl_create_login_message(data, conn->user, &resp, &len);
+    }
+    else if(enabledmechs & SASL_MECH_PLAIN) {
+      mech = SASL_MECH_STRING_PLAIN;
+      state1 = SASL_PLAIN;
+      sasl->authused = SASL_MECH_PLAIN;
+
+      if(force_ir || data->set.sasl_ir)
+        result = sasl_create_plain_message(data, conn->user, conn->passwd,
+                                           &resp, &len);
+    }
   }
-  else
-    state2 = SASL_STOP;    /* No authentication started */
 
   if(!result) {
     if(resp && sasl->params->maxirlen &&
@@ -1490,6 +1541,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
   case SASL_LOGIN_PASSWD:
     result = sasl_create_login_message(data, conn->passwd, &resp, &len);
     break;
+  case SASL_EXTERNAL:
+    result = sasl_create_external_message(data, conn->user, &resp, &len);
+    break;
 
 #ifndef CURL_DISABLE_CRYPTO_AUTH
   case SASL_CRAMMD5:
index e4a594c5385d64d26644540119119aab027e7c5c..985ca416ee1b3238bed302b38b130d560f32a09f 100644 (file)
@@ -39,10 +39,6 @@ struct ntlmdata;
 struct kerberos5data;
 #endif
 
-/* Authentication mechanism values */
-#define SASL_AUTH_NONE          0
-#define SASL_AUTH_ANY           ~0U
-
 /* Authentication mechanism flags */
 #define SASL_MECH_LOGIN             (1 << 0)
 #define SASL_MECH_PLAIN             (1 << 1)
@@ -53,6 +49,11 @@ struct kerberos5data;
 #define SASL_MECH_NTLM              (1 << 6)
 #define SASL_MECH_XOAUTH2           (1 << 7)
 
+/* Authentication mechanism values */
+#define SASL_AUTH_NONE          0
+#define SASL_AUTH_ANY           ~0U
+#define SASL_AUTH_DEFAULT       (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL)
+
 /* Authentication mechanism strings */
 #define SASL_MECH_STRING_LOGIN      "LOGIN"
 #define SASL_MECH_STRING_PLAIN      "PLAIN"
@@ -74,6 +75,7 @@ typedef enum {
   SASL_PLAIN,
   SASL_LOGIN,
   SASL_LOGIN_PASSWD,
+  SASL_EXTERNAL,
   SASL_CRAMMD5,
   SASL_DIGESTMD5,
   SASL_DIGESTMD5_RESP,
@@ -228,6 +230,9 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
 /* Initializes an SASL structure */
 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params);
 
+/* Check if we have enough auth data and capabilities to authenticate */
+bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn);
+
 /* Calculate the required login details for SASL authentication  */
 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
                          bool force_ir, saslprogress *progress);
index c5c8e4aeafff7b7fd255484318f92e79365e1e06..2d037eeb3524d17a4234070336a231ea604af8a7 100644 (file)
@@ -612,9 +612,9 @@ static CURLcode imap_perform_authentication(struct connectdata *conn)
   struct imap_conn *imapc = &conn->proto.imapc;
   saslprogress progress;
 
-  /* Check we have a username and password to authenticate with and end the
+  /* Check we have enough data to authenticate with and end the
      connect phase if we don't */
-  if(!conn->bits.user_passwd) {
+  if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
     state(conn, IMAP_STOP);
     return result;
   }
@@ -1962,7 +1962,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
   case SASL_AUTH_NONE:
     imapc->preftype = IMAP_TYPE_NONE;
     break;
-  case SASL_AUTH_ANY:
+  case SASL_AUTH_DEFAULT:
     imapc->preftype = IMAP_TYPE_ANY;
     break;
   default:
index a7213a44edd343d1350e1811ef64a869be607ba6..d3b59f2490ee5ce4643b80a86b9d4cbbf0ae047d 100644 (file)
@@ -543,9 +543,9 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn)
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   saslprogress progress = SASL_IDLE;
 
-  /* Check we have a username and password to authenticate with and end the
+  /* Check we have enough data to authenticate with and end the
      connect phase if we don't */
-  if(!conn->bits.user_passwd) {
+  if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
     state(conn, POP3_STOP);
     return result;
   }
@@ -1425,7 +1425,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
     case SASL_AUTH_NONE:
       pop3c->preftype = POP3_TYPE_NONE;
       break;
-    case SASL_AUTH_ANY:
+    case SASL_AUTH_DEFAULT:
       pop3c->preftype = POP3_TYPE_ANY;
       break;
     default:
index ca1ab2cece66e28994a86608693badc8b1f1d65b..550725a93edd338dcc1061cadd5c649712444bd6 100644 (file)
@@ -486,9 +486,10 @@ static CURLcode smtp_perform_authentication(struct connectdata *conn)
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   saslprogress progress;
 
-  /* Check we have a username and password to authenticate with, and the
+  /* Check we have enough data to authenticate with, and the
      server supports authentiation, and end the connect phase if not */
-  if(!conn->bits.user_passwd || !smtpc->auth_supported) {
+  if(!smtpc->auth_supported ||
+      !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
     state(conn, SMTP_STOP);
     return result;
   }