/* Passed the value of a "(Proxy,WWW)-Authenticate: " header field. * Returns 0 if valid challenge was accepted; non-zero if no valid * challenge was found. */ static int auth_challenge(auth_session *sess, const char *value) { char *pnt, *key, *val, *hdr, sep; struct auth_challenge *chall = NULL, *challenges = NULL; int success; pnt = hdr = ne_strdup(value); NE_DEBUG(NE_DBG_HTTPAUTH, "Got new auth challenge: %s\n", value); /* The header value may be made up of one or more challenges. We * split it down into attribute-value pairs, then search for * schemes in the pair keys. */ while (!tokenize(&pnt, &key, &val, &sep, 1)) { if (val == NULL) { auth_scheme scheme; if (strcasecmp(key, "basic") == 0) { scheme = auth_scheme_basic; } else if (strcasecmp(key, "digest") == 0) { scheme = auth_scheme_digest; } #ifdef HAVE_GSSAPI else if (strcasecmp(key, "negotiate") == 0) { scheme = auth_scheme_gssapi; } #else #ifdef HAVE_SSPI else if (strcasecmp(key, "negotiate") == 0) { scheme = auth_scheme_sspi_negotiate; } else if (strcasecmp(key, "ntlm") == 0) { scheme = auth_scheme_sspi_ntlm; } #endif #endif else { NE_DEBUG(NE_DBG_HTTPAUTH, "Ignoring challenge '%s'.\n", key); chall = NULL; continue; } NE_DEBUG(NE_DBG_HTTPAUTH, "New '%s' challenge.\n", key); chall = ne_calloc(sizeof *chall); chall->scheme = scheme; chall->next = challenges; challenges = chall; if (sep == ' ' && (scheme == auth_scheme_gssapi || scheme == auth_scheme_sspi_negotiate || scheme == auth_scheme_sspi_ntlm) ) { /* Cope with the fact that the unquoted base64 * paramater token doesn't match the 2617 auth-param * grammar: */ chall->opaque = ne_shave(ne_token(&pnt, ','), " \t"); NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Negotiate parameter '%s'\n", chall->opaque); if (!pnt) break; /* stop parsing at end-of-string. */ } continue; } else if (chall == NULL) { /* Ignore pairs for an unknown challenge. */ NE_DEBUG(NE_DBG_HTTPAUTH, "Ignored pair: %s = %s\n", key, val); continue; } /* Strip quotes off value. */ val = ne_shave(val, "\"'"); NE_DEBUG(NE_DBG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, val); if (strcasecmp(key, "realm") == 0) { chall->realm = val; } else if (strcasecmp(key, "nonce") == 0) { chall->nonce = val; } else if (strcasecmp(key, "opaque") == 0) { chall->opaque = val; } else if (strcasecmp(key, "stale") == 0) { /* Truth value */ chall->stale = (strcasecmp(val, "true") == 0); } else if (strcasecmp(key, "algorithm") == 0) { if (strcasecmp(val, "md5") == 0) { chall->alg = auth_alg_md5; } else if (strcasecmp(val, "md5-sess") == 0) { chall->alg = auth_alg_md5_sess; } else { chall->alg = auth_alg_unknown; } } else if (strcasecmp(key, "qop") == 0) { /* iterate over each token in the value */ do { const char *tok = ne_shave(ne_token(&val, ','), " \t"); if (strcasecmp(tok, "auth") == 0) { chall->qop_auth = 1; } } while (val); chall->got_qop = chall->qop_auth; } } NE_DEBUG(NE_DBG_HTTPAUTH, "Finished parsing parameters.\n"); /* Did we find any challenges */ if (challenges == NULL) { ne_free(hdr); return -1; } success = 0; #ifdef HAVE_GSSAPI /* Ignore Negotiate challenges from origin servers which don't * come over SSL. */ if (sess->spec == &ah_proxy_class || sess->context != AUTH_ANY) { NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n"); /* Try a GSSAPI challenge */ for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_gssapi) { if (!gssapi_challenge(sess, chall)) { success = 1; break; } } } } #endif #ifdef HAVE_SSPI if (!success) { NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for SSPI/Negotiate.\n"); for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_sspi_negotiate) { if (!sspi_challenge(sess, chall, 0)) { success = 1; break; } } } } if (!success) { NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for SSPI/NTLM.\n"); for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_sspi_ntlm) { if (!sspi_challenge(sess, chall, 1)) { success = 1; break; } } } } #endif /* Try a digest challenge */ if (!success) { NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n"); for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_digest) { if (!digest_challenge(sess, chall)) { success = 1; break; } } } } if (!success) { NE_DEBUG(NE_DBG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n"); for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_basic) { if (!basic_challenge(sess, chall)) { success = 1; break; } } } if (!success) { /* No good challenges - record this in the session state */ NE_DEBUG(NE_DBG_HTTPAUTH, "Did not understand any challenges.\n"); } } /* Remember whether we can now supply the auth details */ sess->can_handle = success; while (challenges != NULL) { chall = challenges->next; ne_free(challenges); challenges = chall; } ne_free(hdr); return !success; }
/* A new challenge presented by the server */ int http_auth_challenge(http_auth_session *sess, const char *value) { char **pairs, *pnt, *unquoted, *key; struct http_auth_chall *chall = NULL, *challenges = NULL; int n, success; DEBUG(DEBUG_HTTPAUTH, "Got new auth challenge: %s\n", value); /* The header value may be made up of one or more challenges. * We split it down into attribute-value pairs, then search for * schemes in the pair keys. */ pairs = pair_string(value, ',', '=', HTTP_QUOTES, HTTP_WHITESPACE); for (n = 0; pairs[n]!=NULL; n+=2) { /* Look for an auth-scheme in the key */ pnt = strchr(pairs[n], ' '); if (pnt != NULL) { /* We have a new challenge */ DEBUG(DEBUG_HTTPAUTH, "New challenge.\n"); chall = ne_calloc(sizeof *chall); chall->next = challenges; challenges = chall; /* Initialize the challenge parameters */ /* Which auth-scheme is it (case-insensitive matching) */ if (strncasecmp(pairs[n], "basic ", 6) == 0) { DEBUG(DEBUG_HTTPAUTH, "Basic scheme.\n"); chall->scheme = http_auth_scheme_basic; } else if (strncasecmp(pairs[n], "digest ", 7) == 0) { DEBUG(DEBUG_HTTPAUTH, "Digest scheme.\n"); chall->scheme = http_auth_scheme_digest; } else { DEBUG(DEBUG_HTTPAUTH, "Unknown scheme.\n"); free(chall); challenges = NULL; break; } /* Now, the real key for this pair starts after the * auth-scheme... skipping whitespace */ while (strchr(HTTP_WHITESPACE, *(++pnt)) != NULL) /* nullop */; key = pnt; } else if (chall == NULL) { /* If we haven't got an auth-scheme, and we're * haven't yet found a challenge, skip this pair. */ continue; } else { key = pairs[n]; } DEBUG(DEBUG_HTTPAUTH, "Got pair: [%s] = [%s]\n", key, pairs[n+1]); /* Most values are quoted, so unquote them here */ unquoted = shave_string(pairs[n+1], '"'); /* Now parse the attribute */ DEBUG(DEBUG_HTTPAUTH, "Unquoted pair is: [%s]\n", unquoted); if (strcasecmp(key, "realm") == 0) { chall->realm = pairs[n+1]; } else if (strcasecmp(key, "nonce") == 0) { chall->nonce = pairs[n+1]; } else if (strcasecmp(key, "opaque") == 0) { chall->opaque = pairs[n+1]; } else if (strcasecmp(key, "domain") == 0) { chall->domain = pairs[n+1]; } else if (strcasecmp(key, "stale") == 0) { /* Truth value */ chall->stale = (strcasecmp(unquoted, "true") == 0); } else if (strcasecmp(key, "algorithm") == 0) { if (strcasecmp(unquoted, "md5") == 0) { chall->alg = http_auth_alg_md5; } else if (strcasecmp(unquoted, "md5-sess") == 0) { chall->alg = http_auth_alg_md5_sess; } else { chall->alg = http_auth_alg_unknown; } } else if (strcasecmp(key, "qop") == 0) { char **qops; int qop; qops = split_string(unquoted, ',', NULL, HTTP_WHITESPACE); chall->got_qop = 1; for (qop = 0; qops[qop] != NULL; qop++) { if (strcasecmp(qops[qop], "auth") == 0) { chall->qop_auth = 1; } else if (strcasecmp(qops[qop], "auth-int") == 0) { chall->qop_auth_int = 1; } } split_string_free(qops); } free(unquoted); } DEBUG(DEBUG_HTTPAUTH, "Finished parsing parameters.\n"); /* Did we find any challenges */ if (challenges == NULL) { pair_string_free(pairs); return -1; } success = 0; DEBUG(DEBUG_HTTPAUTH, "Looking for Digest challenges.\n"); /* Try a digest challenge */ for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == http_auth_scheme_digest) { if (!digest_challenge(sess, chall)) { success = 1; break; } } } if (!success) { DEBUG(DEBUG_HTTPAUTH, "No good Digest challenges, looking for Basic.\n"); for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == http_auth_scheme_basic) { if (!basic_challenge(sess, chall)) { success = 1; break; } } } if (!success) { /* No good challenges - record this in the session state */ DEBUG(DEBUG_HTTPAUTH, "Did not understand any challenges.\n"); } } /* Remember whether we can now supply the auth details */ sess->can_handle = success; while (challenges != NULL) { chall = challenges->next; free(challenges); challenges = chall; } /* Free up the parsed header values */ pair_string_free(pairs); return !success; }