]> granicus.if.org Git - curl/commitdiff
http_digest: Moved challenge decoding into SASL module
authorSteve Holme <steve_holme@hotmail.com>
Wed, 5 Nov 2014 14:33:26 +0000 (14:33 +0000)
committerSteve Holme <steve_holme@hotmail.com>
Wed, 5 Nov 2014 14:39:13 +0000 (14:39 +0000)
lib/curl_sasl.c
lib/curl_sasl.h
lib/http_digest.c

index ebb8d815304afeb5925fd79b000c8d5486743f3e..3169730b5f69e766546b4e1e367680deb771ab01 100644 (file)
@@ -19,6 +19,7 @@
  * KIND, either express or implied.
  *
  * RFC2195 CRAM-MD5 authentication
+ * RFC2617 Basic and Digest Access Authentication
  * RFC2831 DIGEST-MD5 authentication
  * RFC4422 Simple Authentication and Security Layer (SASL)
  * RFC4616 PLAIN authentication
 extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5);
 #endif
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI)
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#if !defined(USE_WINDOWS_SSPI)
 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
+#endif /* !USE_WINDOWS_SSPI */
 
 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
 
+#define DIGEST_MAX_VALUE_LENGTH           256
+#define DIGEST_MAX_CONTENT_LENGTH         1024
+
+/*
+ * Return 0 on success and then the buffers are filled in fine.
+ *
+ * Non-zero means failure to parse.
+ */
+static int sasl_digest_get_pair(const char *str, char *value, char *content,
+                                const char **endptr)
+{
+  int c;
+  bool starts_with_quote = FALSE;
+  bool escape = FALSE;
+
+  for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--); )
+    *value++ = *str++;
+  *value=0;
+
+  if('=' != *str++)
+    /* eek, no match */
+    return 1;
+
+  if('\"' == *str) {
+    /* this starts with a quote so it must end with one as well! */
+    str++;
+    starts_with_quote = TRUE;
+  }
+
+  for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
+    switch(*str) {
+    case '\\':
+      if(!escape) {
+        /* possibly the start of an escaped quote */
+        escape = TRUE;
+        *content++ = '\\'; /* even though this is an escape character, we still
+                              store it as-is in the target buffer */
+        continue;
+      }
+      break;
+    case ',':
+      if(!starts_with_quote) {
+        /* this signals the end of the content if we didn't get a starting
+           quote and then we do "sloppy" parsing */
+        c=0; /* the end */
+        continue;
+      }
+      break;
+    case '\r':
+    case '\n':
+      /* end of string */
+      c=0;
+      continue;
+    case '\"':
+      if(!escape && starts_with_quote) {
+        /* end of string */
+        c=0;
+        continue;
+      }
+      break;
+    }
+    escape = FALSE;
+    *content++ = *str;
+  }
+  *content=0;
+
+  *endptr = str;
+
+  return 0; /* all is fine! */
+}
+
+#if !defined(USE_WINDOWS_SSPI)
 /* Retrieves the value for a corresponding key from the challenge string
  * returns TRUE if the key could be found, FALSE if it does not exists
  */
@@ -122,7 +198,9 @@ static CURLcode sasl_digest_get_qop_values(const char *options, int *value)
 
   return CURLE_OK;
 }
-#endif
+#endif /* !USE_WINDOWS_SSPI */
+
+#endif /* !CURL_DISABLE_CRYPTO_AUTH */
 
 #if !defined(USE_WINDOWS_SSPI)
 /*
@@ -582,6 +660,136 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
 }
 #endif  /* !USE_WINDOWS_SSPI */
 
+/*
+ * Curl_sasl_decode_digest_http_message()
+ *
+ * This is used to decode a HTTP DIGEST challenge message into the seperate
+ * attributes.
+ *
+ * Parameters:
+ *
+ * chlg    [in]     - Pointer to the challenge message.
+ * digest  [in/out] - The digest data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
+                                              struct digestdata *digest)
+{
+  bool before = FALSE; /* got a nonce before */
+  bool foundAuth = FALSE;
+  bool foundAuthInt = FALSE;
+  char *token = NULL;
+  char *tmp = NULL;
+
+  /* If we already have received a nonce, keep that in mind */
+  if(digest->nonce)
+    before = TRUE;
+
+  /* Clean up any former leftovers and initialise to defaults */
+  Curl_sasl_digest_cleanup(digest);
+
+  for(;;) {
+    char value[DIGEST_MAX_VALUE_LENGTH];
+    char content[DIGEST_MAX_CONTENT_LENGTH];
+
+    /* Extract a value=content pair */
+    if(!sasl_digest_get_pair(chlg, value, content, &chlg)) {
+      if(Curl_raw_equal(value, "nonce")) {
+        digest->nonce = strdup(content);
+        if(!digest->nonce)
+          return CURLE_OUT_OF_MEMORY;
+      }
+      else if(Curl_raw_equal(value, "stale")) {
+        if(Curl_raw_equal(content, "true")) {
+          digest->stale = TRUE;
+          digest->nc = 1; /* we make a new nonce now */
+        }
+      }
+      else if(Curl_raw_equal(value, "realm")) {
+        digest->realm = strdup(content);
+        if(!digest->realm)
+          return CURLE_OUT_OF_MEMORY;
+      }
+      else if(Curl_raw_equal(value, "opaque")) {
+        digest->opaque = strdup(content);
+        if(!digest->opaque)
+          return CURLE_OUT_OF_MEMORY;
+      }
+      else if(Curl_raw_equal(value, "qop")) {
+        char *tok_buf;
+        /* Tokenize the list and choose auth if possible, use a temporary
+            clone of the buffer since strtok_r() ruins it */
+        tmp = strdup(content);
+        if(!tmp)
+          return CURLE_OUT_OF_MEMORY;
+
+        token = strtok_r(tmp, ",", &tok_buf);
+        while(token != NULL) {
+          if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
+            foundAuth = TRUE;
+          }
+          else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
+            foundAuthInt = TRUE;
+          }
+          token = strtok_r(NULL, ",", &tok_buf);
+        }
+
+        free(tmp);
+
+        /* Select only auth o auth-int. Otherwise, ignore */
+        if(foundAuth) {
+          digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
+          if(!digest->qop)
+            return CURLE_OUT_OF_MEMORY;
+        }
+        else if(foundAuthInt) {
+          digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
+          if(!digest->qop)
+            return CURLE_OUT_OF_MEMORY;
+        }
+      }
+      else if(Curl_raw_equal(value, "algorithm")) {
+        digest->algorithm = strdup(content);
+        if(!digest->algorithm)
+          return CURLE_OUT_OF_MEMORY;
+
+        if(Curl_raw_equal(content, "MD5-sess"))
+          digest->algo = CURLDIGESTALGO_MD5SESS;
+        else if(Curl_raw_equal(content, "MD5"))
+          digest->algo = CURLDIGESTALGO_MD5;
+        else
+          return CURLE_BAD_CONTENT_ENCODING;
+      }
+      else {
+        /* unknown specifier, ignore it! */
+      }
+    }
+    else
+      break; /* we're done here */
+
+    /* Pass all additional spaces here */
+    while(*chlg && ISSPACE(*chlg))
+      chlg++;
+
+    /* Allow the list to be comma-separated */
+    if(',' == *chlg)
+      chlg++;
+  }
+
+  /* We had a nonce since before, and we got another one now without
+     'stale=true'. This means we provided bad credentials in the previous
+     request */
+  if(before && !digest->stale)
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  /* We got this header without a nonce, that's a bad Digest line! */
+  if(!digest->nonce)
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  return CURLE_OK;
+}
+
 /*
  * Curl_sasl_digest_cleanup()
  *
index 8fcce320f981799ec8e5d74d7cd7efb42c89b102..da6511c3de11f449c0188ef0d2c1ab83f16e9f95 100644 (file)
@@ -105,6 +105,10 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data,
                                              const char *service,
                                              char **outptr, size_t *outlen);
 
+/* This is used to decode a HTTP DIGEST challenge message */
+CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,
+                                              struct digestdata *digest);
+
 /* This is used to clean up the digest specific data */
 void Curl_sasl_digest_cleanup(struct digestdata *digest);
 #endif
index d2dbfe59bd1c150ea2a1c456e5b268cd14350f3f..19e7c8392f7a4531d2087c576f6f61033959ffed 100644 (file)
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#define MAX_VALUE_LENGTH 256
-#define MAX_CONTENT_LENGTH 1024
-
-/*
- * Return 0 on success and then the buffers are filled in fine.
- *
- * Non-zero means failure to parse.
- */
-static int get_pair(const char *str, char *value, char *content,
-                    const char **endptr)
-{
-  int c;
-  bool starts_with_quote = FALSE;
-  bool escape = FALSE;
-
-  for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
-    *value++ = *str++;
-  *value=0;
-
-  if('=' != *str++)
-    /* eek, no match */
-    return 1;
-
-  if('\"' == *str) {
-    /* this starts with a quote so it must end with one as well! */
-    str++;
-    starts_with_quote = TRUE;
-  }
-
-  for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
-    switch(*str) {
-    case '\\':
-      if(!escape) {
-        /* possibly the start of an escaped quote */
-        escape = TRUE;
-        *content++ = '\\'; /* even though this is an escape character, we still
-                              store it as-is in the target buffer */
-        continue;
-      }
-      break;
-    case ',':
-      if(!starts_with_quote) {
-        /* this signals the end of the content if we didn't get a starting
-           quote and then we do "sloppy" parsing */
-        c=0; /* the end */
-        continue;
-      }
-      break;
-    case '\r':
-    case '\n':
-      /* end of string */
-      c=0;
-      continue;
-    case '\"':
-      if(!escape && starts_with_quote) {
-        /* end of string */
-        c=0;
-        continue;
-      }
-      break;
-    }
-    escape = FALSE;
-    *content++ = *str;
-  }
-  *content=0;
-
-  *endptr = str;
-
-  return 0; /* all is fine! */
-}
-
 /* Test example headers:
 
 WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
@@ -125,12 +54,7 @@ CURLcode Curl_input_digest(struct connectdata *conn,
                            const char *header) /* rest of the *-authenticate:
                                                   header */
 {
-  char *token = NULL;
-  char *tmp = NULL;
-  bool foundAuth = FALSE;
-  bool foundAuthInt = FALSE;
   struct SessionHandle *data=conn->data;
-  bool before = FALSE; /* got a nonce before */
   struct digestdata *d;
 
   if(proxy) {
@@ -140,118 +64,14 @@ CURLcode Curl_input_digest(struct connectdata *conn,
     d = &data->state.digest;
   }
 
-  if(checkprefix("Digest", header)) {
-    header += strlen("Digest");
-
-    /* If we already have received a nonce, keep that in mind */
-    if(d->nonce)
-      before = TRUE;
-
-    /* clear off any former leftovers and init to defaults */
-    Curl_sasl_digest_cleanup(d);
-
-    for(;;) {
-      char value[MAX_VALUE_LENGTH];
-      char content[MAX_CONTENT_LENGTH];
-
-      while(*header && ISSPACE(*header))
-        header++;
-
-      /* extract a value=content pair */
-      if(!get_pair(header, value, content, &header)) {
-        if(Curl_raw_equal(value, "nonce")) {
-          d->nonce = strdup(content);
-          if(!d->nonce)
-            return CURLE_OUT_OF_MEMORY;
-        }
-        else if(Curl_raw_equal(value, "stale")) {
-          if(Curl_raw_equal(content, "true")) {
-            d->stale = TRUE;
-            d->nc = 1; /* we make a new nonce now */
-          }
-        }
-        else if(Curl_raw_equal(value, "realm")) {
-          d->realm = strdup(content);
-          if(!d->realm)
-            return CURLE_OUT_OF_MEMORY;
-        }
-        else if(Curl_raw_equal(value, "opaque")) {
-          d->opaque = strdup(content);
-          if(!d->opaque)
-            return CURLE_OUT_OF_MEMORY;
-        }
-        else if(Curl_raw_equal(value, "qop")) {
-          char *tok_buf;
-          /* tokenize the list and choose auth if possible, use a temporary
-             clone of the buffer since strtok_r() ruins it */
-          tmp = strdup(content);
-          if(!tmp)
-            return CURLE_OUT_OF_MEMORY;
-
-          token = strtok_r(tmp, ",", &tok_buf);
-          while(token != NULL) {
-            if(Curl_raw_equal(token, "auth")) {
-              foundAuth = TRUE;
-            }
-            else if(Curl_raw_equal(token, "auth-int")) {
-              foundAuthInt = TRUE;
-            }
-            token = strtok_r(NULL, ",", &tok_buf);
-          }
-          free(tmp);
-          /*select only auth o auth-int. Otherwise, ignore*/
-          if(foundAuth) {
-            d->qop = strdup("auth");
-            if(!d->qop)
-              return CURLE_OUT_OF_MEMORY;
-          }
-          else if(foundAuthInt) {
-            d->qop = strdup("auth-int");
-            if(!d->qop)
-              return CURLE_OUT_OF_MEMORY;
-          }
-        }
-        else if(Curl_raw_equal(value, "algorithm")) {
-          d->algorithm = strdup(content);
-          if(!d->algorithm)
-            return CURLE_OUT_OF_MEMORY;
-
-          if(Curl_raw_equal(content, "MD5-sess"))
-            d->algo = CURLDIGESTALGO_MD5SESS;
-          else if(Curl_raw_equal(content, "MD5"))
-            d->algo = CURLDIGESTALGO_MD5;
-          else
-            return CURLE_BAD_CONTENT_ENCODING;
-        }
-        else {
-          /* unknown specifier, ignore it! */
-        }
-      }
-      else
-        break; /* we're done here */
-
-      /* pass all additional spaces here */
-      while(*header && ISSPACE(*header))
-        header++;
-      if(',' == *header)
-        /* allow the list to be comma-separated */
-        header++;
-    }
-    /* We had a nonce since before, and we got another one now without
-       'stale=true'. This means we provided bad credentials in the previous
-       request */
-    if(before && !d->stale)
-      return CURLE_BAD_CONTENT_ENCODING;
-
-    /* We got this header without a nonce, that's a bad Digest line! */
-    if(!d->nonce)
-      return CURLE_BAD_CONTENT_ENCODING;
-  }
-  else
-    /* else not a digest, get out */
+  if(!checkprefix("Digest", header))
     return CURLE_BAD_CONTENT_ENCODING;
 
-  return CURLE_OK;
+  header += strlen("Digest");
+  while(*header && ISSPACE(*header))
+    header++;
+
+  return Curl_sasl_decode_digest_http_message(header, d);
 }
 
 /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/