| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675 |
- /*
- Authentication tests
- Copyright (C) 2001-2009, Joe Orton <[email protected]>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include "config.h"
- #include <sys/types.h>
- #ifdef HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include "ne_md5.h"
- #include "ne_string.h"
- #include "ne_request.h"
- #include "ne_auth.h"
- #include "ne_basic.h"
- #include "tests.h"
- #include "child.h"
- #include "utils.h"
- static const char username[] = "Aladdin", password[] = "open sesame";
- static const char *alt_username, *alt_username_star;
- static int auth_failed;
- static int has_sha256 = 0, has_sha512_256 = 0;
- #define BASIC_WALLY "Basic realm=WallyWorld"
- #define CHAL_WALLY "WWW-Authenticate: " BASIC_WALLY
- #define EOL "\r\n"
- static int auth_cb(void *userdata, const char *realm, int tries,
- char *un, char *pw)
- {
- if (strcmp(realm, "WallyWorld")) {
- NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
- return -1;
- }
- strcpy(un, userdata ? userdata : username);
- strcpy(pw, password);
- return tries;
- }
- static int auth_provide_cb(void *userdata, int attempt,
- unsigned protocol, const char *realm,
- char *un, char *pw, size_t buflen)
- {
- if (strcmp(realm, "WallyWorld")) {
- NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
- return -1;
- }
- strcpy(un, alt_username);
- strcpy(pw, password);
- return attempt;
- }
- static void auth_hdr(char *value)
- {
- #define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
- auth_failed = strcmp(value, B);
- NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\nWanted header: [%s]\n"
- "Result: %d\n", value, B, auth_failed);
- #undef B
- }
- /* Sends a response with given response-code. If hdr is not NULL,
- * sends that header string too (appending an EOL). If eoc is
- * non-zero, request must be last sent down a connection; otherwise,
- * clength 0 is sent to maintain a persistent connection. */
- static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
- {
- char buffer[BUFSIZ];
-
- sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
-
- if (hdr) {
- strcat(buffer, hdr);
- strcat(buffer, EOL);
- }
- if (eoc) {
- strcat(buffer, "Connection: close" EOL EOL);
- } else {
- strcat(buffer, "Content-Length: 0" EOL EOL);
- }
-
- return SEND_STRING(sock, buffer);
- }
- /* Server function which sends two responses: first requires auth,
- * second doesn't. */
- static int auth_serve(ne_socket *sock, void *userdata)
- {
- char *hdr = userdata;
- auth_failed = 1;
- /* Register globals for discard_request. */
- got_header = auth_hdr;
- want_header = "Authorization";
- discard_request(sock);
- send_response(sock, hdr, 401, 0);
- discard_request(sock);
- send_response(sock, NULL, auth_failed?500:200, 1);
- return 0;
- }
- static int init(void)
- {
- char *p;
- p = ne_strhash(NE_HASH_SHA256, "", NULL);
- has_sha256 = p != NULL;
- if (p) ne_free(p);
- p = ne_strhash(NE_HASH_SHA512_256, "", NULL);
- has_sha512_256 = p != NULL;
- if (p) ne_free(p);
- return lookup_localhost();
- }
- /* Test that various Basic auth challenges are correctly handled. */
- static int basic(void)
- {
- const char *hdrs[] = {
- /* simplest case */
- CHAL_WALLY,
- /* several challenges, one header */
- "WWW-Authenticate: BarFooScheme, " BASIC_WALLY,
- /* several challenges, one header */
- CHAL_WALLY ", BarFooScheme realm=\"PenguinWorld\"",
- /* whitespace tests. */
- "WWW-Authenticate: Basic realm=WallyWorld ",
- /* nego test. */
- "WWW-Authenticate: Negotiate fish, Basic realm=WallyWorld",
- /* nego test. */
- "WWW-Authenticate: Negotiate fish, bar=boo, Basic realm=WallyWorld",
- /* nego test. */
- "WWW-Authenticate: Negotiate, Basic realm=WallyWorld",
- /* multi-header case 1 */
- "WWW-Authenticate: BarFooScheme\r\n"
- CHAL_WALLY,
-
- /* multi-header cases 1 */
- CHAL_WALLY "\r\n"
- "WWW-Authenticate: BarFooScheme bar=\"foo\"",
- /* multi-header case 3 */
- "WWW-Authenticate: FooBarChall foo=\"bar\"\r\n"
- CHAL_WALLY "\r\n"
- "WWW-Authenticate: BarFooScheme bar=\"foo\"",
- /* quoting test; fails to handle scheme properly with <= 0.28.2. */
- "WWW-Authenticate: Basic realm=\"WallyWorld\" , BarFooScheme"
- };
- size_t n;
-
- for (n = 0; n < sizeof(hdrs)/sizeof(hdrs[0]); n++) {
- ne_session *sess;
-
- CALL(make_session(&sess, auth_serve, (void *)hdrs[n]));
- ne_set_server_auth(sess, auth_cb, NULL);
-
- CALL(any_2xx_request(sess, "/norman"));
-
- ne_session_destroy(sess);
- CALL(await_server());
- }
- return OK;
- }
- static int retry_serve(ne_socket *sock, void *ud)
- {
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
-
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, CHAL_WALLY, 401, 0);
- discard_request(sock);
- send_response(sock, NULL, 200, 0);
-
- return OK;
- }
- static int retry_cb(void *userdata, const char *realm, int tries,
- char *un, char *pw)
- {
- int *count = userdata;
- /* dummy creds; server ignores them anyway. */
- strcpy(un, "a");
- strcpy(pw, "b");
- switch (*count) {
- case 0:
- case 1:
- if (tries == *count) {
- *count += 1;
- return 0;
- } else {
- t_context("On request #%d, got attempt #%d", *count, tries);
- *count = -1;
- return 1;
- }
- break;
- case 2:
- case 3:
- /* server fails a subsequent request, check that tries has
- * reset to zero. */
- if (tries == 0) {
- *count += 1;
- return 0;
- } else {
- t_context("On retry after failure #%d, tries was %d",
- *count, tries);
- *count = -1;
- return 1;
- }
- break;
- case 4:
- case 5:
- if (tries > 1) {
- t_context("Attempt counter reached #%d", tries);
- *count = -1;
- return 1;
- }
- return tries;
- default:
- t_context("Count reached %d!?", *count);
- *count = -1;
- }
- return 1;
- }
- /* Test that auth retries are working correctly. */
- static int retries(void)
- {
- ne_session *sess;
- int count = 0;
-
- CALL(make_session(&sess, retry_serve, NULL));
- ne_set_server_auth(sess, retry_cb, &count);
- /* This request will be 401'ed twice, then succeed. */
- ONREQ(any_request(sess, "/foo"));
- /* auth_cb will have set up context. */
- CALL(count != 2);
- /* this request will be 401'ed once, then succeed. */
- ONREQ(any_request(sess, "/foo"));
- /* auth_cb will have set up context. */
- CALL(count != 3);
- /* some 20x requests. */
- ONREQ(any_request(sess, "/foo"));
- ONREQ(any_request(sess, "/foo"));
- /* this request will be 401'ed once, then succeed. */
- ONREQ(any_request(sess, "/foo"));
- /* auth_cb will have set up context. */
- CALL(count != 4);
- /* First request is 401'ed by the server at both attempts. */
- ONV(any_request(sess, "/foo") != NE_AUTH,
- ("auth succeeded, should have failed: %s", ne_get_error(sess)));
- count++;
- /* Second request is 401'ed first time, then will succeed if
- * retried. 0.18.0 didn't reset the attempt counter though so
- * this didn't work. */
- ONV(any_request(sess, "/foo") == NE_AUTH,
- ("auth failed on second try, should have succeeded: %s", ne_get_error(sess)));
- return destroy_and_wait(sess);
- }
- /* crashes with neon <0.22 */
- static int forget_regress(void)
- {
- ne_session *sess = ne_session_create("http", "localhost", 1234);
- ne_forget_auth(sess);
- ne_session_destroy(sess);
- return OK;
- }
- static int fail_auth_cb(void *ud, const char *realm, int attempt,
- char *un, char *pw)
- {
- return 1;
- }
- /* this may trigger a segfault in neon 0.21.x and earlier. */
- static int tunnel_regress(void)
- {
- ne_session *sess;
-
- CALL(proxied_session_server(&sess, "http", "localhost", 443,
- single_serve_string,
- "HTTP/1.1 401 Auth failed.\r\n"
- "WWW-Authenticate: Basic realm=asda\r\n"
- "Content-Length: 0\r\n\r\n"));
- ne_set_server_auth(sess, fail_auth_cb, NULL);
- any_request(sess, "/foo");
- return destroy_and_wait(sess);
- }
- /* regression test for parsing a Negotiate challenge with on parameter
- * token. */
- static int negotiate_regress(void)
- {
- ne_session *sess;
-
- CALL(session_server(&sess, single_serve_string,
- "HTTP/1.1 401 Auth failed.\r\n"
- "WWW-Authenticate: Negotiate\r\n"
- "Content-Length: 0\r\n\r\n"));
- ne_set_server_auth(sess, fail_auth_cb, NULL);
- any_request(sess, "/foo");
- return destroy_and_wait(sess);
- }
- static char *digest_hdr = NULL;
- static void dup_header(char *header)
- {
- if (digest_hdr) ne_free(digest_hdr);
- digest_hdr = ne_strdup(header);
- }
- #define PARM_PROXY (0x0001)
- #define PARM_NEXTNONCE (0x0002)
- #define PARM_RFC2617 (0x0004)
- #define PARM_AINFO (0x0008)
- #define PARM_USERHASH (0x0010) /* userhash=true */
- #define PARM_UHFALSE (0x0020) /* userhash=false */
- #define PARM_ALTUSER (0x0040)
- #define PARM_LEGACY (0x0080)
- #define PARM_LEGACY_ONLY (0x0100)
- struct digest_parms {
- const char *realm, *nonce, *opaque, *domain;
- enum { ALG_MD5 = 0, ALG_MD5_SESS, ALG_SHA256, ALG_SHA256_SESS, ALG_SHA512_256, ALG_SHA512_256_SESS } alg;
- unsigned int flags;
- int num_requests;
- int stale;
- enum digest_failure {
- fail_not,
- fail_bogus_alg,
- fail_req0_stale,
- fail_req0_2069_stale,
- fail_omit_qop,
- fail_omit_realm,
- fail_omit_nonce,
- fail_ai_bad_nc,
- fail_ai_bad_nc_syntax,
- fail_ai_bad_digest,
- fail_ai_bad_cnonce,
- fail_ai_omit_cnonce,
- fail_ai_omit_digest,
- fail_ai_omit_nc,
- fail_outside_domain,
- fail_2069_weak
- } failure;
- };
- struct digest_state {
- const char *realm, *nonce, *uri, *username, *username_star, *password, *algorithm, *qop,
- *method, *opaque;
- char userhash[64];
- char *cnonce, *digest, *ncval;
- long nc;
- int count;
- int uhash_bool;
- };
- static char *hash(struct digest_parms *p, ...)
- ne_attribute_sentinel;
- static char *hash(struct digest_parms *p, ...)
- {
- va_list ap;
- unsigned int flags;
- char *h;
- switch (p->alg) {
- case ALG_SHA512_256_SESS:
- case ALG_SHA512_256:
- flags = NE_HASH_SHA512_256;
- break;
- case ALG_SHA256_SESS:
- case ALG_SHA256:
- flags = NE_HASH_SHA256;
- break;
- default:
- flags = NE_HASH_MD5;
- break;
- }
- va_start(ap, p);
- h = ne_vstrhash(flags, ap);
- va_end(ap);
- if (h == NULL) abort();
- return h;
- }
- /* Write the request-digest into 'digest' (or response-digest if
- * auth_info is non-zero) for given digest auth state and
- * parameters. */
- static char *make_digest(struct digest_state *state, struct digest_parms *parms,
- int auth_info)
- {
- char *h_a1, *h_a2, *rv;
- h_a1 = hash(parms, state->username, ":", state->realm, ":",
- state->password, NULL);
- if (parms->alg == ALG_MD5_SESS || parms->alg == ALG_SHA256_SESS || parms->alg == ALG_SHA512_256_SESS) {
- char *sess_h_a1;
- sess_h_a1 = hash(parms, h_a1, ":", state->nonce, ":", state->cnonce, NULL);
- ne_free(h_a1);
- h_a1 = sess_h_a1;
- }
- h_a2 = hash(parms, !auth_info ? state->method : "", ":", state->uri, NULL);
- if (parms->flags & PARM_RFC2617) {
- rv = hash(parms,
- h_a1, ":", state->nonce, ":",
- state->ncval, ":", state->cnonce, ":", state->qop, ":",
- h_a2, NULL);
- }
- else {
- /* RFC2069-style */
- rv = hash(parms, h_a1, ":", state->nonce, ":", h_a2, NULL);
- }
- ne_free(h_a2);
- ne_free(h_a1);
- return rv;
- }
- /* Verify that the response-digest matches expected state. */
- static int check_digest(struct digest_state *state, struct digest_parms *parms)
- {
- char *digest;
- digest = make_digest(state, parms, 0);
- ONV(digest == NULL,
- ("failed to create digest for %s", state->algorithm));
- ONV(strcmp(digest, state->digest),
- ("bad digest; expected %s got %s", state->digest, digest));
- ne_free(digest);
- return OK;
- }
- #define DIGCMP(field) \
- do { \
- ONCMP(state->field, newstate.field, \
- "Digest response header", #field); \
- } while (0)
- #define NPARAM(field, param) \
- do { \
- if (ne_strcasecmp(name, param) == 0) { \
- ONV(newstate.field != NULL, \
- ("received multiple %s params: %s, %s", param, \
- newstate.field, val)); \
- newstate.field = val; \
- } \
- } while (0)
- #define PARAM(field) NPARAM(field, #field)
- /* Verify that Digest auth request header, 'header', meets expected
- * state and parameters. */
- static int verify_digest_header(struct digest_state *state,
- struct digest_parms *parms,
- char *header)
- {
- char *ptr;
- struct digest_state newstate = {0};
- ptr = ne_token(&header, ' ');
- ONCMP("Digest", ptr, "Digest response", "scheme name");
- while (header) {
- char *name, *val;
- ptr = ne_qtoken(&header, ',', "\"\'");
- ONN("quoting broken", ptr == NULL);
- name = ne_shave(ptr, " ");
- val = strchr(name, '=');
- ONV(val == NULL, ("bad name/value pair: %s", name));
-
- *val++ = '\0';
- val = ne_shave(val, "\"\' ");
- NE_DEBUG(NE_DBG_HTTP, "got field: [%s] = [%s]\n", name, val);
- PARAM(uri);
- PARAM(realm);
- PARAM(username);
- PARAM(nonce);
- PARAM(algorithm);
- PARAM(qop);
- PARAM(opaque);
- PARAM(cnonce);
- NPARAM(username_star, "username*");
- if (ne_strcasecmp(name, "nc") == 0) {
- long nc = strtol(val, NULL, 16);
-
- ONV(nc != state->nc,
- ("got bad nonce count: %ld (%s) not %ld",
- nc, val, state->nc));
- state->ncval = ne_strdup(val);
- }
- else if (ne_strcasecmp(name, "response") == 0) {
- state->digest = ne_strdup(val);
- }
- else if (ne_strcasecmp(name, "userhash") == 0 ) {
- newstate.uhash_bool = strcmp(val, "true") == 0;
- }
- }
- ONN("cnonce param missing or short for 2617-style auth",
- (parms->flags & PARM_RFC2617)
- && (newstate.cnonce == NULL
- || strlen(newstate.cnonce) < 32));
- if (alt_username_star) {
- ONN("unexpected userhash=true sent", newstate.uhash_bool);
- ONN("username* missing", newstate.username_star == NULL);
- ONCMP(alt_username_star, newstate.username_star, "Digest field", "username*");
- }
- else if (parms->flags & PARM_USERHASH) {
- ONN("userhash missing", !newstate.uhash_bool);
- ONCMP(state->userhash, newstate.username,
- "Digest username (userhash) field", "userhash");
- }
- else {
- ONN("unexpected userhash=true sent", newstate.uhash_bool);
- DIGCMP(username);
- }
- DIGCMP(realm);
- if (!parms->domain)
- DIGCMP(uri);
- DIGCMP(nonce);
- DIGCMP(opaque);
- DIGCMP(algorithm);
- if (parms->flags & PARM_RFC2617) {
- DIGCMP(qop);
- }
-
- if (newstate.cnonce) {
- state->cnonce = ne_strdup(newstate.cnonce);
- }
- if (parms->domain) {
- state->uri = ne_strdup(newstate.uri);
- }
- ONN("no digest param given", !state->digest);
- CALL(check_digest(state, parms));
- state->nc++;
-
- return OK;
- }
- static char *make_authinfo_header(struct digest_state *state,
- struct digest_parms *parms)
- {
- ne_buffer *buf = ne_buffer_create();
- char *digest, *ncval, *cnonce;
- if (parms->failure == fail_ai_bad_digest) {
- digest = ne_strdup("fish");
- } else {
- digest = make_digest(state, parms, 1);
- }
- if (parms->failure == fail_ai_bad_nc_syntax) {
- ncval = "zztop";
- } else if (parms->failure == fail_ai_bad_nc) {
- ncval = "999";
- } else {
- ncval = state->ncval;
- }
- if (parms->failure == fail_ai_bad_cnonce) {
- cnonce = "another-fish";
- } else {
- cnonce = state->cnonce;
- }
- if ((parms->flags & PARM_PROXY)) {
- ne_buffer_czappend(buf, "Proxy-");
- }
- ne_buffer_czappend(buf, "Authentication-Info: ");
- if ((parms->flags & PARM_RFC2617) == 0) {
- ne_buffer_concat(buf, "rspauth=\"", digest, "\"", NULL);
- } else {
- if (parms->failure != fail_ai_omit_nc) {
- ne_buffer_concat(buf, "nc=", ncval, ", ", NULL);
- }
- if (parms->failure != fail_ai_omit_cnonce) {
- ne_buffer_concat(buf, "cnonce=\"", cnonce, "\", ", NULL);
- }
- if (parms->failure != fail_ai_omit_digest) {
- ne_buffer_concat(buf, "rspauth=\"", digest, "\", ", NULL);
- }
- if (parms->flags & PARM_NEXTNONCE) {
- state->nonce = ne_concat("next-", state->nonce, NULL);
- ne_buffer_concat(buf, "nextnonce=\"", state->nonce, "\", ", NULL);
- state->nc = 1;
- }
- ne_buffer_czappend(buf, "qop=\"auth\"");
- }
- ne_free(digest);
-
- return ne_buffer_finish(buf);
- }
- static char *make_digest_header(struct digest_state *state,
- struct digest_parms *parms)
- {
- ne_buffer *buf = ne_buffer_create();
- const char *algorithm;
- algorithm = parms->failure == fail_bogus_alg ? "fish"
- : state->algorithm;
- ne_buffer_concat(buf,
- (parms->flags & PARM_PROXY) ? "Proxy-Authenticate"
- : "WWW-Authenticate",
- ": Digest "
- "realm=\"", parms->realm, "\", ", NULL);
-
- if (parms->flags & PARM_RFC2617) {
- ne_buffer_concat(buf, "algorithm=\"", algorithm, "\", ",
- "qop=\"", state->qop, "\", ", NULL);
- }
- if (parms->opaque) {
- ne_buffer_concat(buf, "opaque=\"", parms->opaque, "\", ", NULL);
- }
- if (parms->domain) {
- ne_buffer_concat(buf, "domain=\"", parms->domain, "\", ", NULL);
- }
- if (parms->flags & PARM_USERHASH) {
- ne_buffer_czappend(buf, "userhash=true, ");
- }
- else if (parms->flags & PARM_UHFALSE) {
- ne_buffer_czappend(buf, "userhash=false, ");
- }
- if (parms->failure == fail_req0_stale
- || parms->failure == fail_req0_2069_stale
- || parms->stale == parms->num_requests) {
- ne_buffer_concat(buf, "stale='true', ", NULL);
- }
- ne_buffer_concat(buf, "nonce=\"", state->nonce, "\"", NULL);
- return ne_buffer_finish(buf);
- }
- /* Server process for Digest auth handling. */
- static int serve_digest(ne_socket *sock, void *userdata)
- {
- struct digest_parms *parms = userdata;
- struct digest_state state;
- char resp[NE_BUFSIZ], *rspdigest;
-
- if ((parms->flags & PARM_PROXY))
- state.uri = "http://www.example.com/fish";
- else if (parms->domain)
- state.uri = "/fish/0";
- else
- state.uri = "/fish";
- state.method = "GET";
- state.realm = parms->realm;
- state.nonce = parms->nonce;
- state.opaque = parms->opaque;
- if (parms->flags & PARM_ALTUSER)
- state.username = alt_username;
- else
- state.username = username;
- state.password = password;
- state.nc = 1;
- switch (parms->alg) {
- case ALG_SHA512_256: state.algorithm = "SHA-512-256"; break;
- case ALG_SHA512_256_SESS: state.algorithm = "SHA-512-256-sess"; break;
- case ALG_SHA256: state.algorithm = "SHA-256"; break;
- case ALG_SHA256_SESS: state.algorithm = "SHA-256-sess"; break;
- case ALG_MD5_SESS: state.algorithm = "MD5-sess"; break;
- default:
- case ALG_MD5: state.algorithm = "MD5"; break;
- }
- state.qop = "auth";
- if (parms->flags & PARM_USERHASH) {
- char *uh = hash(parms, username, ":", parms->realm, NULL);
- ONN("userhash too long", strlen(uh) >= sizeof state.userhash);
- ne_strnzcpy(state.userhash, uh, sizeof state.userhash);
- ne_free(uh);
- }
- state.cnonce = state.digest = state.ncval = NULL;
- parms->num_requests += parms->stale ? 1 : 0;
- NE_DEBUG(NE_DBG_HTTP, ">>>> Response sequence begins, %d requests.\n",
- parms->num_requests);
- want_header = (parms->flags & PARM_PROXY) ? "Proxy-Authorization" : "Authorization";
- digest_hdr = NULL;
- got_header = dup_header;
- CALL(discard_request(sock));
- ONV(digest_hdr != NULL,
- ("got unwarranted WWW-Auth header: %s", digest_hdr));
- rspdigest = make_digest_header(&state, parms);
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 %d Auth Denied\r\n"
- "%s\r\n"
- "Content-Length: 0\r\n" "\r\n",
- (parms->flags & PARM_PROXY) ? 407 : 401,
- rspdigest);
- ne_free(rspdigest);
- SEND_STRING(sock, resp);
- /* Give up now if we've sent a challenge which should force the
- * client to fail immediately: */
- if (parms->failure == fail_bogus_alg
- || parms->failure == fail_req0_stale
- || parms->failure == fail_req0_2069_stale) {
- return OK;
- }
- do {
- digest_hdr = NULL;
- CALL(discard_request(sock));
- if (digest_hdr && parms->domain && (parms->num_requests & 1) != 0) {
- SEND_STRING(sock, "HTTP/1.1 400 Used Auth Outside Domain\r\n\r\n");
- return OK;
- }
- else if (digest_hdr == NULL && parms->domain && (parms->num_requests & 1) != 0) {
- /* Do nothing. */
- NE_DEBUG(NE_DBG_HTTP, "No Authorization header sent, good.\n");
- }
- else {
- ONN("no Authorization header sent", digest_hdr == NULL);
- ONERR(sock, verify_digest_header(&state, parms, digest_hdr));
- }
- if (parms->num_requests == parms->stale) {
- char *dig;
- state.nonce = ne_concat("stale-", state.nonce, NULL);
- state.nc = 1;
- dig = make_digest_header(&state, parms);
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 %d Auth Denied\r\n"
- "%s\r\n"
- "Content-Length: 0\r\n" "\r\n",
- (parms->flags & PARM_PROXY) ? 407 : 401,
- dig);
- ne_free(dig);
- }
- else if (parms->flags & PARM_AINFO) {
- char *ai = make_authinfo_header(&state, parms);
-
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 200 Well, if you insist\r\n"
- "Content-Length: 0\r\n"
- "%s\r\n"
- "\r\n", ai);
-
- ne_free(ai);
- } else {
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 200 You did good\r\n"
- "Content-Length: 0\r\n" "\r\n");
- }
- SEND_STRING(sock, resp);
- NE_DEBUG(NE_DBG_HTTP, "Handled request; %d requests remain.\n",
- parms->num_requests - 1);
- } while (--parms->num_requests);
- return OK;
- }
- static int test_digest(struct digest_parms *parms)
- {
- ne_session *sess;
- unsigned proto = NE_AUTH_DIGEST;
- if ((parms->flags & PARM_LEGACY))
- proto |= NE_AUTH_LEGACY_DIGEST;
- else if ((parms->flags & PARM_LEGACY_ONLY))
- proto = NE_AUTH_LEGACY_DIGEST;
- NE_DEBUG(NE_DBG_HTTP, ">>>> Request sequence begins "
- "(reqs=%d, nonce=%s, rfc=%s, stale=%d, proxy=%d).\n",
- parms->num_requests,
- parms->nonce, (parms->flags & PARM_RFC2617) ? "2617" : "2069",
- parms->stale, !!(parms->flags & PARM_PROXY));
- if ((parms->flags & PARM_PROXY)) {
- CALL(proxied_session_server(&sess, "http", "www.example.com", 80,
- serve_digest, parms));
- ne_set_proxy_auth(sess, auth_cb, NULL);
- }
- else {
- CALL(session_server(&sess, serve_digest, parms));
- if ((parms->flags & PARM_ALTUSER))
- ne_add_auth(sess, proto, auth_provide_cb, NULL);
- else
- ne_add_server_auth(sess, proto, auth_cb, NULL);
- }
- do {
- CALL(any_2xx_request(sess, "/fish"));
- } while (--parms->num_requests);
-
- return destroy_and_wait(sess);
- }
- /* Test for RFC2617-style Digest auth. */
- static int digest(void)
- {
- struct digest_parms parms[] = {
- /* RFC 2617-style */
- { "WallyWorld", "this-is-a-nonce", NULL, NULL, ALG_MD5, PARM_RFC2617, 1, 0, fail_not },
- { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617, 1, 0, fail_not },
- /* ... with A-I */
- { "WallyWorld", "nonce-nonce-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO, 1, 0, fail_not },
- /* ... with md5-sess. */
- { "WallyWorld", "nonce-nonce-nonce", "opaque-string", NULL, ALG_MD5_SESS, PARM_RFC2617 | PARM_AINFO, 1, 0, fail_not },
- /* many requests, with changing nonces; tests for next-nonce handling bug. */
- { "WallyWorld", "this-is-a-nonce", "opaque-thingy", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO | PARM_NEXTNONCE, 20, 0, fail_not },
- /* staleness. */
- { "WallyWorld", "this-is-a-nonce", "opaque-thingy", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO, 3, 2, fail_not },
- /* 2069 + stale */
- { "WallyWorld", "this-is-a-nonce", NULL, NULL, ALG_MD5, PARM_LEGACY|PARM_AINFO, 3, 2, fail_not },
- /* RFC 7616-style */
- { "WallyWorld", "new-day-new-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_USERHASH, 1, 0, fail_not },
- /* ... userhash=false */
- { "WallyWorld", "just-another-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_UHFALSE, 1, 0, fail_not },
- /* RFC 2069-style */
- { "WallyWorld", "lah-di-da-di-dah", NULL, NULL, ALG_MD5, PARM_LEGACY, 1, 0, fail_not },
- { "WallyWorld", "lah-lah-lah-lah", NULL, NULL, ALG_MD5, PARM_LEGACY_ONLY, 1, 0, fail_not },
- { "WallyWorld", "fee-fi-fo-fum", "opaque-string", NULL, ALG_MD5, PARM_LEGACY, 1, 0, fail_not },
- { "WallyWorld", "fee-fi-fo-fum", "opaque-string", NULL, ALG_MD5, PARM_AINFO|PARM_LEGACY, 1, 0, fail_not },
- /* Proxy auth */
- { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617|PARM_PROXY, 1, 0, fail_not },
- /* Proxy + nextnonce */
- { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617|PARM_AINFO|PARM_PROXY, 1, 0, fail_not },
- { NULL }
- };
- size_t n;
- for (n = 0; parms[n].realm; n++) {
- CALL(test_digest(&parms[n]));
- }
- return OK;
- }
- static int digest_sha256(void)
- {
- struct digest_parms parms[] = {
- { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha-session", "opaque-string", NULL, ALG_SHA256_SESS, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617|PARM_AINFO, 8, 0, fail_not },
-
- { NULL },
- };
- size_t n;
- if (!has_sha256) {
- t_context("SHA-256 not supported");
- return SKIP;
- }
- for (n = 0; parms[n].realm; n++) {
- CALL(test_digest(&parms[n]));
- }
- return OK;
- }
- static int digest_sha512_256(void)
- {
- struct digest_parms parms[] = {
- { "WallyWorld", "nonce-sha5-nonce", "opaque-string", NULL, ALG_SHA512_256, PARM_RFC2617, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha5-nonce", "opaque-string", NULL, ALG_SHA512_256, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha5-session", "opaque-string", NULL, ALG_SHA512_256_SESS, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
- { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA512_256_SESS, PARM_RFC2617|PARM_AINFO, 20, 0, fail_not },
- { NULL },
- };
- size_t n;
- if (!has_sha512_256) {
- t_context("SHA-512/256 not supported");
- return SKIP;
- }
- for (n = 0; parms[n].realm; n++) {
- CALL(test_digest(&parms[n]));
- }
- return OK;
- }
- static int digest_username_star(void)
- {
- static const struct {
- const char *username_raw, *username_star;
- } ts[] = {
- { "Aladdin", NULL },
- { "[email protected]", NULL },
- { "Ałâddín", "UTF-8''A%c5%82%c3%a2dd%c3%adn" },
- { "Jäsøn Doe", "UTF-8''J%c3%a4s%c3%b8n%20Doe" },
- { "foo bar", "UTF-8''foo%20bar"},
- { "foo\"bar", "UTF-8''foo%22bar" },
- { NULL, NULL }
- };
- unsigned n;
- int ret = OK;
- for (n = 0; ret == OK && ts[n].username_raw; n++) {
- struct digest_parms parms = {
- "WallyWorld", "nonce-sha5-nonce", "opaque-string",
- NULL, ALG_MD5, PARM_RFC2617|PARM_UHFALSE|PARM_ALTUSER, 1, 0, fail_not };
- alt_username = ts[n].username_raw;
- alt_username_star = ts[n].username_star;
- ret = test_digest(&parms);
- }
- alt_username = NULL;
- alt_username_star = NULL;
- return ret;
- }
- static int digest_failures(void)
- {
- struct digest_parms parms;
- static const struct {
- enum digest_failure mode;
- const char *message;
- } fails[] = {
- { fail_ai_bad_nc, "nonce count mismatch" },
- { fail_ai_bad_nc_syntax, "could not parse nonce count" },
- { fail_ai_bad_digest, "digest mismatch" },
- { fail_ai_bad_cnonce, "client nonce mismatch" },
- { fail_ai_omit_nc, "missing parameters" },
- { fail_ai_omit_digest, "missing parameters" },
- { fail_ai_omit_cnonce, "missing parameters" },
- { fail_bogus_alg, "unknown algorithm" },
- { fail_req0_stale, "initial Digest challenge was stale" },
- { fail_req0_2069_stale, "initial Digest challenge was stale" },
- { fail_2069_weak, "legacy Digest challenge not supported" },
- { fail_not, NULL }
- };
- unsigned n;
- memset(&parms, 0, sizeof parms);
-
- parms.realm = "WallyWorld";
- parms.nonce = "random-invented-string";
- parms.opaque = NULL;
- parms.flags = PARM_AINFO;
- parms.num_requests = 1;
- for (n = 0; fails[n].message; n++) {
- ne_session *sess;
- int ret;
- parms.failure = fails[n].mode;
- if (parms.failure == fail_req0_2069_stale || parms.failure == fail_2069_weak)
- parms.flags &= ~PARM_RFC2617;
- else
- parms.flags |= PARM_RFC2617;
- NE_DEBUG(NE_DBG_HTTP, ">>> New Digest failure test, "
- "expecting failure '%s'\n", fails[n].message);
-
- CALL(session_server(&sess, serve_digest, &parms));
- ne_set_server_auth(sess, auth_cb, NULL);
-
- ret = any_2xx_request(sess, "/fish");
- ONV(ret == NE_OK,
- ("request success (iter %u); expecting error '%s'",
- n, fails[n].message));
- ONV(strstr(ne_get_error(sess), fails[n].message) == NULL,
- ("request fails with error '%s'; expecting '%s'",
- ne_get_error(sess), fails[n].message));
- ne_session_destroy(sess);
-
- if (fails[n].mode == fail_bogus_alg
- || fails[n].mode == fail_req0_stale
- || fails[n].mode == fail_2069_weak) {
- reap_server();
- } else {
- CALL(await_server());
- }
- }
- return OK;
- }
- static int fail_cb(void *userdata, const char *realm, int tries,
- char *un, char *pw)
- {
- ne_buffer *buf = userdata;
- char str[64];
- if (strcmp(realm, "colonic") == 0 && ne_buffer_size(buf) == 0) {
- ne_strnzcpy(un, "user:name", NE_ABUFSIZ);
- ne_strnzcpy(pw, "passwerd", NE_ABUFSIZ);
- return 0;
- }
- ne_snprintf(str, sizeof str, "<%s, %d>", realm, tries);
- ne_buffer_zappend(buf, str);
- return -1;
- }
- static int fail_challenge(void)
- {
- static const struct {
- const char *resp, *error, *challs;
- } ts[] = {
- /* only possible Basic parse failure. */
- { "Basic", "missing realm in Basic challenge" },
- { "Basic realm=\"colonic\"", "username containing colon" },
- /* Digest parameter invalid/omitted failure cases: */
- { "Digest algorithm=MD5, qop=auth, nonce=\"foo\"",
- "missing parameter in Digest challenge" },
- { "Digest algorithm=MD5, qop=auth, realm=\"foo\"",
- "missing parameter in Digest challenge" },
- { "Digest algorithm=ZEBEDEE-GOES-BOING, qop=auth, realm=\"foo\"",
- "unknown algorithm in Digest challenge" },
- { "Digest algorithm=MD5-sess, realm=\"foo\"",
- "incompatible algorithm in Digest challenge" },
- { "Digest algorithm=MD5, qop=auth, nonce=\"foo\", realm=\"foo\", "
- "domain=\"http://[::1/\"", "could not parse domain" },
- /* Multiple challenge failure cases: */
- { "Basic, Digest",
- "missing parameter in Digest challenge, missing realm in Basic challenge" },
-
- { "Digest realm=\"foo\", algorithm=MD5, qop=auth, nonce=\"foo\","
- " Basic realm=\"foo\"",
- "rejected Digest challenge, rejected Basic challenge" },
- { "WhizzBangAuth realm=\"foo\", "
- "Basic realm='foo'",
- "ignored WhizzBangAuth challenge, rejected Basic challenge" },
- { "", "could not parse challenge" },
- /* neon 0.26.x regression in "attempt" handling. */
- { "Basic realm=\"foo\", "
- "Digest realm=\"bar\", algorithm=MD5, qop=auth, nonce=\"foo\"",
- "rejected Digest challenge, rejected Basic challenge"
- , "<bar, 0><foo, 1>" /* Digest challenge first, Basic second. */
- }
- };
- unsigned n;
-
- for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
- char resp[512];
- ne_session *sess;
- int ret;
- ne_buffer *buf = ne_buffer_create();
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 401 Auth Denied\r\n"
- "WWW-Authenticate: %s\r\n"
- "Content-Length: 0\r\n" "\r\n",
- ts[n].resp);
-
- CALL(multi_session_server(&sess, "http", "localhost",
- 2, single_serve_string, resp));
- ne_set_server_auth(sess, fail_cb, buf);
-
- ret = any_2xx_request(sess, "/fish");
- ONV(ret == NE_OK,
- ("request success (iter %u); expecting error '%s'",
- n, ts[n].error));
- ONV(strstr(ne_get_error(sess), ts[n].error) == NULL,
- ("request fails with error '%s'; expecting '%s'",
- ne_get_error(sess), ts[n].error));
-
- if (ts[n].challs) {
- ONCMP(ts[n].challs, buf->data, "challenge callback",
- "invocation order");
- }
- ne_session_destroy(sess);
- ne_buffer_destroy(buf);
- reap_server();
- }
- return OK;
- }
- struct multi_context {
- int id;
- ne_buffer *buf;
- };
- static int multi_cb(void *userdata, const char *realm, int tries,
- char *un, char *pw)
- {
- struct multi_context *ctx = userdata;
- ne_buffer_snprintf(ctx->buf, 128, "[id=%d, realm=%s, tries=%d]",
- ctx->id, realm, tries);
- return -1;
- }
- static int multi_handler(void)
- {
- ne_session *sess;
- struct multi_context c[2];
- unsigned n;
- ne_buffer *buf = ne_buffer_create();
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 401 Auth Denied\r\n"
- "WWW-Authenticate: Basic realm='fish',"
- " Digest realm='food', algorithm=MD5, qop=auth, nonce=gaga\r\n"
- "Content-Length: 0\r\n" "\r\n"));
-
- for (n = 0; n < 2; n++) {
- c[n].buf = buf;
- c[n].id = n + 1;
- }
- ne_add_server_auth(sess, NE_AUTH_BASIC, multi_cb, &c[0]);
- ne_add_server_auth(sess, NE_AUTH_DIGEST, multi_cb, &c[1]);
-
- any_request(sess, "/fish");
-
- ONCMP("[id=2, realm=food, tries=0]"
- "[id=1, realm=fish, tries=0]", buf->data,
- "multiple callback", "invocation order");
-
- ne_buffer_destroy(buf);
- return destroy_and_wait(sess);
- }
- static int multi_rfc7616(void)
- {
- ne_session *sess;
- struct multi_context c[2];
- unsigned n;
- ne_buffer *buf, *exp;
- buf = ne_buffer_create();
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 401 Auth Denied\r\n"
- "WWW-Authenticate: "
- "Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
- "Basic realm='basic-realm', "
- "Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
- "Digest realm='sha256-realm', algorithm=SHA-256, qop=auth, nonce=gaga\r\n"
- "Content-Length: 0\r\n" "\r\n"));
- for (n = 0; n < 2; n++) {
- c[n].buf = buf;
- c[n].id = n + 1;
- }
- ne_add_server_auth(sess, NE_AUTH_BASIC, multi_cb, &c[0]);
- ne_add_server_auth(sess, NE_AUTH_DIGEST, multi_cb, &c[1]);
- any_request(sess, "/fish");
- exp = ne_buffer_create();
- n = 0;
- if (has_sha512_256)
- ne_buffer_snprintf(exp, 100, "[id=2, realm=sha512-realm, tries=%u]", n++);
- if (has_sha256)
- ne_buffer_snprintf(exp, 100, "[id=2, realm=sha256-realm, tries=%u]", n++);
- ne_buffer_snprintf(exp, 100,
- "[id=2, realm=md5-realm, tries=%u]"
- "[id=1, realm=basic-realm, tries=0]", n);
- ONV(strcmp(exp->data, buf->data),
- ("unexpected callback ordering.\n"
- "expected: %s\n"
- "actual: %s\n",
- exp->data, buf->data));
- ne_buffer_destroy(buf);
- ne_buffer_destroy(exp);
- return destroy_and_wait(sess);
- }
- static int multi_provider_cb(void *userdata, int attempt,
- unsigned protocol, const char *realm,
- char *un, char *pw, size_t buflen)
- {
- ne_buffer *buf = userdata;
- const char *ctx;
- if (buflen == NE_ABUFSIZ) {
- NE_DEBUG(NE_DBG_HTTPAUTH, "auth: FAILED for short buffer length.\n");
- return -1;
- }
- if ((protocol & NE_AUTH_PROXY) == NE_AUTH_PROXY) {
- ctx = "proxy";
- protocol ^= NE_AUTH_PROXY;
- }
- else {
- ctx = "server";
- }
- ne_buffer_snprintf(buf, 128, "[%s: proto=%u, realm=%s, attempt=%d]",
- ctx, protocol, realm, attempt);
- ne_strnzcpy(un, "foo", buflen);
- ne_strnzcpy(pw, "bar", buflen);
- return protocol == NE_AUTH_BASIC ? 0 : -1;
- }
- static int serve_provider(ne_socket *s, void *userdata)
- {
- CALL(serve_response(s,
- "HTTP/1.1 407 Proxy Auth Plz\r\n"
- "Proxy-Authenticate: Basic realm='proxy-realm'\r\n"
- "Content-Length: 0\r\n" "\r\n"));
- CALL(serve_response(s,
- "HTTP/1.1 401 Auth Denied\r\n"
- "WWW-Authenticate: "
- " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
- " Basic realm='basic-realm', "
- " Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
- " Digest realm='sha256-realm', algorithm=SHA-256, qop=auth, nonce=gaga\r\n"
- "Content-Length: 0\r\n" "\r\n"));
- CALL(serve_response(s,
- "HTTP/1.1 401 Auth Denied\r\n"
- "WWW-Authenticate: "
- " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
- " Basic realm='basic-realm'\r\n"
- "Content-Length: 0\r\n" "\r\n"));
- return serve_response(s,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 0\r\n" "\r\n");
- }
- static int multi_provider(void)
- {
- ne_session *sess;
- ne_buffer *buf = ne_buffer_create(), *exp;
- CALL(make_session(&sess, serve_provider, NULL));
- ne_add_auth(sess, NE_AUTH_DIGEST|NE_AUTH_BASIC, multi_provider_cb, buf);
- ONREQ(any_request(sess, "/fish"));
- exp = ne_buffer_create();
- ne_buffer_snprintf(exp, 100,
- "[proxy: proto=%u, realm=proxy-realm, attempt=0]",
- NE_AUTH_BASIC);
- if (has_sha512_256)
- ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=0]",
- NE_AUTH_DIGEST);
- if (has_sha256)
- ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha256-realm, attempt=0]",
- NE_AUTH_DIGEST);
- ne_buffer_snprintf(exp, 100,
- "[server: proto=%u, realm=md5-realm, attempt=0]"
- "[server: proto=%u, realm=basic-realm, attempt=0]",
- NE_AUTH_DIGEST, NE_AUTH_BASIC);
- if (has_sha512_256)
- ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=1]",
- NE_AUTH_DIGEST);
- ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=basic-realm, attempt=1]",
- NE_AUTH_BASIC);
- ONV(strcmp(exp->data, buf->data),
- ("unexpected callback ordering.\n"
- "expected: %s\n"
- "actual: %s\n",
- exp->data, buf->data));
- ne_buffer_destroy(buf);
- ne_buffer_destroy(exp);
- return destroy_and_wait(sess);
- }
- static int domains(void)
- {
- ne_session *sess;
- struct digest_parms parms;
- memset(&parms, 0, sizeof parms);
- parms.realm = "WallyWorld";
- parms.flags = PARM_RFC2617;
- parms.nonce = "agoog";
- parms.domain = "http://localhost:4242/fish/ https://example.com /agaor /other";
- parms.num_requests = 6;
- CALL(proxied_session_server(&sess, "http", "localhost", 4242,
- serve_digest, &parms));
- ne_set_server_auth(sess, auth_cb, NULL);
- CALL(any_2xx_request(sess, "/fish/0"));
- CALL(any_2xx_request(sess, "/outside"));
- CALL(any_2xx_request(sess, "/others"));
- CALL(any_2xx_request(sess, "/fish"));
- CALL(any_2xx_request(sess, "/fish/2"));
- CALL(any_2xx_request(sess, "*"));
-
- return destroy_and_wait(sess);
- }
- /* This segfaulted with 0.28.0 through 0.28.2 inclusive. */
- static int CVE_2008_3746(void)
- {
- ne_session *sess;
- struct digest_parms parms;
- memset(&parms, 0, sizeof parms);
- parms.realm = "WallyWorld";
- parms.flags = PARM_RFC2617;
- parms.nonce = "agoog";
- parms.domain = "foo";
- parms.num_requests = 1;
- CALL(proxied_session_server(&sess, "http", "www.example.com", 80,
- serve_digest, &parms));
- ne_set_server_auth(sess, auth_cb, NULL);
- any_2xx_request(sess, "/fish/0");
-
- return destroy_and_wait(sess);
- }
- static int defaults(void)
- {
- ne_session *sess;
-
- CALL(make_session(&sess, auth_serve, CHAL_WALLY));
- ne_add_server_auth(sess, NE_AUTH_DEFAULT, auth_cb, NULL);
- CALL(any_2xx_request(sess, "/norman"));
- ne_session_destroy(sess);
- CALL(await_server());
- CALL(make_session(&sess, auth_serve, CHAL_WALLY));
- ne_add_server_auth(sess, NE_AUTH_ALL, auth_cb, NULL);
- CALL(any_2xx_request(sess, "/norman"));
- return destroy_and_wait(sess);
- }
- static void fail_hdr(char *value)
- {
- auth_failed = 1;
- }
- static int serve_forgotten(ne_socket *sock, void *userdata)
- {
- auth_failed = 0;
- got_header = fail_hdr;
- want_header = "Authorization";
-
- CALL(discard_request(sock));
- if (auth_failed) {
- /* Should not get initial Auth header. Eek. */
- send_response(sock, NULL, 403, 1);
- return 0;
- }
- send_response(sock, CHAL_WALLY, 401, 0);
-
- got_header = auth_hdr;
- CALL(discard_request(sock));
- if (auth_failed) {
- send_response(sock, NULL, 403, 1);
- return 0;
- }
- send_response(sock, NULL, 200, 0);
-
- ne_sock_read_timeout(sock, 5);
- /* Last time; should get no Auth header. */
- got_header = fail_hdr;
- CALL(discard_request(sock));
- send_response(sock, NULL, auth_failed ? 500 : 200, 1);
-
- return 0;
- }
- static int forget(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, serve_forgotten, NULL));
- ne_set_server_auth(sess, auth_cb, NULL);
-
- CALL(any_2xx_request(sess, "/norman"));
-
- ne_forget_auth(sess);
- CALL(any_2xx_request(sess, "/norman"));
- ne_session_destroy(sess);
-
- return await_server();
- }
- static int serve_basic_scope_checker(ne_socket *sock, void *userdata)
- {
- /* --- GET /fish/0.txt -- first request */
- digest_hdr = NULL;
- got_header = dup_header;
- want_header = "Authorization";
- CALL(discard_request(sock));
- if (digest_hdr) {
- t_context("Got WWW-Auth header on initial request");
- return error_response(sock, FAIL);
- }
- send_response(sock, CHAL_WALLY, 401, 0);
- /* Retry of GET /fish/0 - expect Basic creds */
- auth_failed = 1;
- got_header = auth_hdr;
- CALL(discard_request(sock));
- if (auth_failed) {
- t_context("bad Basic Auth on first request");
- return error_response(sock, FAIL);
- }
- send_response(sock, CHAL_WALLY, 200, 0);
-
- /* --- GET /not/inside -- second request */
- got_header = dup_header;
- CALL(discard_request(sock));
- if (digest_hdr) {
- t_context("Basic auth sent outside of credentials scope");
- return error_response(sock, FAIL);
- }
- send_response(sock, CHAL_WALLY, 200, 0);
- /* --- GET /fish/1 -- third request */
- got_header = auth_hdr;
- CALL(discard_request(sock));
- send_response(sock, NULL, auth_failed?500:200, 1);
-
- return 0;
- }
- /* Check that Basic auth follows the RFC7617 rules around scope. */
- static int basic_scope(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, serve_basic_scope_checker, NULL));
- ne_set_server_auth(sess, auth_cb, NULL);
-
- CALL(any_2xx_request(sess, "/fish/0.txt")); /* must use auth */
- CALL(any_2xx_request(sess, "/not/inside")); /* must NOT use auth credentials */
- CALL(any_2xx_request(sess, "/fish/1")); /* must use auth credentials */
- return destroy_and_wait(sess);
- }
- /* Test for scope of "*" */
- static int serve_star_scope_checker(ne_socket *sock, void *userdata)
- {
- /* --- OPTIONS * -- first request */
- digest_hdr = NULL;
- got_header = dup_header;
- want_header = "Authorization";
- CALL(discard_request(sock));
- if (digest_hdr) {
- t_context("Got WWW-Auth header on initial request");
- return error_response(sock, FAIL);
- }
- send_response(sock, CHAL_WALLY, 401, 0);
- /* Retry of OPTIONS * - expect Basic creds */
- auth_failed = 1;
- got_header = auth_hdr;
- CALL(discard_request(sock));
- if (auth_failed) {
- t_context("No Basic Auth in OPTIONS request");
- return error_response(sock, FAIL);
- }
- send_response(sock, CHAL_WALLY, 200, 0);
- return 0;
- }
- /* Test for the scope of "*". */
- static int star_scope(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, serve_star_scope_checker, NULL));
- ne_set_server_auth(sess, auth_cb, NULL);
- CALL(any_2xx_request_method(sess, "OPTIONS", "*")); /* must use auth */
- return destroy_and_wait(sess);
- }
- /* proxy auth, proxy AND origin */
- ne_test tests[] = {
- T(init),
- T(basic),
- T(retries),
- T(forget_regress),
- T(tunnel_regress),
- T(negotiate_regress),
- T(digest),
- T(digest_sha256),
- T(digest_sha512_256),
- T(digest_failures),
- T(digest_username_star),
- T(fail_challenge),
- T(multi_handler),
- T(multi_rfc7616),
- T(multi_provider),
- T(domains),
- T(defaults),
- T(CVE_2008_3746),
- T(forget),
- T(basic_scope),
- T(star_scope),
- T(NULL)
- };
|