auth.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675
  1. /*
  2. Authentication tests
  3. Copyright (C) 2001-2009, Joe Orton <[email protected]>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include "config.h"
  17. #include <sys/types.h>
  18. #ifdef HAVE_STDLIB_H
  19. #include <stdlib.h>
  20. #endif
  21. #ifdef HAVE_UNISTD_H
  22. #include <unistd.h>
  23. #endif
  24. #include "ne_md5.h"
  25. #include "ne_string.h"
  26. #include "ne_request.h"
  27. #include "ne_auth.h"
  28. #include "ne_basic.h"
  29. #include "tests.h"
  30. #include "child.h"
  31. #include "utils.h"
  32. static const char username[] = "Aladdin", password[] = "open sesame";
  33. static const char *alt_username, *alt_username_star;
  34. static int auth_failed;
  35. static int has_sha256 = 0, has_sha512_256 = 0;
  36. #define BASIC_WALLY "Basic realm=WallyWorld"
  37. #define CHAL_WALLY "WWW-Authenticate: " BASIC_WALLY
  38. #define EOL "\r\n"
  39. static int auth_cb(void *userdata, const char *realm, int tries,
  40. char *un, char *pw)
  41. {
  42. if (strcmp(realm, "WallyWorld")) {
  43. NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
  44. return -1;
  45. }
  46. strcpy(un, userdata ? userdata : username);
  47. strcpy(pw, password);
  48. return tries;
  49. }
  50. static int auth_provide_cb(void *userdata, int attempt,
  51. unsigned protocol, const char *realm,
  52. char *un, char *pw, size_t buflen)
  53. {
  54. if (strcmp(realm, "WallyWorld")) {
  55. NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm);
  56. return -1;
  57. }
  58. strcpy(un, alt_username);
  59. strcpy(pw, password);
  60. return attempt;
  61. }
  62. static void auth_hdr(char *value)
  63. {
  64. #define B "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
  65. auth_failed = strcmp(value, B);
  66. NE_DEBUG(NE_DBG_HTTP, "Got auth header: [%s]\nWanted header: [%s]\n"
  67. "Result: %d\n", value, B, auth_failed);
  68. #undef B
  69. }
  70. /* Sends a response with given response-code. If hdr is not NULL,
  71. * sends that header string too (appending an EOL). If eoc is
  72. * non-zero, request must be last sent down a connection; otherwise,
  73. * clength 0 is sent to maintain a persistent connection. */
  74. static int send_response(ne_socket *sock, const char *hdr, int code, int eoc)
  75. {
  76. char buffer[BUFSIZ];
  77. sprintf(buffer, "HTTP/1.1 %d Blah Blah" EOL, code);
  78. if (hdr) {
  79. strcat(buffer, hdr);
  80. strcat(buffer, EOL);
  81. }
  82. if (eoc) {
  83. strcat(buffer, "Connection: close" EOL EOL);
  84. } else {
  85. strcat(buffer, "Content-Length: 0" EOL EOL);
  86. }
  87. return SEND_STRING(sock, buffer);
  88. }
  89. /* Server function which sends two responses: first requires auth,
  90. * second doesn't. */
  91. static int auth_serve(ne_socket *sock, void *userdata)
  92. {
  93. char *hdr = userdata;
  94. auth_failed = 1;
  95. /* Register globals for discard_request. */
  96. got_header = auth_hdr;
  97. want_header = "Authorization";
  98. discard_request(sock);
  99. send_response(sock, hdr, 401, 0);
  100. discard_request(sock);
  101. send_response(sock, NULL, auth_failed?500:200, 1);
  102. return 0;
  103. }
  104. static int init(void)
  105. {
  106. char *p;
  107. p = ne_strhash(NE_HASH_SHA256, "", NULL);
  108. has_sha256 = p != NULL;
  109. if (p) ne_free(p);
  110. p = ne_strhash(NE_HASH_SHA512_256, "", NULL);
  111. has_sha512_256 = p != NULL;
  112. if (p) ne_free(p);
  113. return lookup_localhost();
  114. }
  115. /* Test that various Basic auth challenges are correctly handled. */
  116. static int basic(void)
  117. {
  118. const char *hdrs[] = {
  119. /* simplest case */
  120. CHAL_WALLY,
  121. /* several challenges, one header */
  122. "WWW-Authenticate: BarFooScheme, " BASIC_WALLY,
  123. /* several challenges, one header */
  124. CHAL_WALLY ", BarFooScheme realm=\"PenguinWorld\"",
  125. /* whitespace tests. */
  126. "WWW-Authenticate: Basic realm=WallyWorld ",
  127. /* nego test. */
  128. "WWW-Authenticate: Negotiate fish, Basic realm=WallyWorld",
  129. /* nego test. */
  130. "WWW-Authenticate: Negotiate fish, bar=boo, Basic realm=WallyWorld",
  131. /* nego test. */
  132. "WWW-Authenticate: Negotiate, Basic realm=WallyWorld",
  133. /* multi-header case 1 */
  134. "WWW-Authenticate: BarFooScheme\r\n"
  135. CHAL_WALLY,
  136. /* multi-header cases 1 */
  137. CHAL_WALLY "\r\n"
  138. "WWW-Authenticate: BarFooScheme bar=\"foo\"",
  139. /* multi-header case 3 */
  140. "WWW-Authenticate: FooBarChall foo=\"bar\"\r\n"
  141. CHAL_WALLY "\r\n"
  142. "WWW-Authenticate: BarFooScheme bar=\"foo\"",
  143. /* quoting test; fails to handle scheme properly with <= 0.28.2. */
  144. "WWW-Authenticate: Basic realm=\"WallyWorld\" , BarFooScheme"
  145. };
  146. size_t n;
  147. for (n = 0; n < sizeof(hdrs)/sizeof(hdrs[0]); n++) {
  148. ne_session *sess;
  149. CALL(make_session(&sess, auth_serve, (void *)hdrs[n]));
  150. ne_set_server_auth(sess, auth_cb, NULL);
  151. CALL(any_2xx_request(sess, "/norman"));
  152. ne_session_destroy(sess);
  153. CALL(await_server());
  154. }
  155. return OK;
  156. }
  157. static int retry_serve(ne_socket *sock, void *ud)
  158. {
  159. discard_request(sock);
  160. send_response(sock, CHAL_WALLY, 401, 0);
  161. discard_request(sock);
  162. send_response(sock, CHAL_WALLY, 401, 0);
  163. discard_request(sock);
  164. send_response(sock, NULL, 200, 0);
  165. discard_request(sock);
  166. send_response(sock, CHAL_WALLY, 401, 0);
  167. discard_request(sock);
  168. send_response(sock, NULL, 200, 0);
  169. discard_request(sock);
  170. send_response(sock, NULL, 200, 0);
  171. discard_request(sock);
  172. send_response(sock, NULL, 200, 0);
  173. discard_request(sock);
  174. send_response(sock, CHAL_WALLY, 401, 0);
  175. discard_request(sock);
  176. send_response(sock, NULL, 200, 0);
  177. discard_request(sock);
  178. send_response(sock, CHAL_WALLY, 401, 0);
  179. discard_request(sock);
  180. send_response(sock, CHAL_WALLY, 401, 0);
  181. discard_request(sock);
  182. send_response(sock, CHAL_WALLY, 401, 0);
  183. discard_request(sock);
  184. send_response(sock, NULL, 200, 0);
  185. return OK;
  186. }
  187. static int retry_cb(void *userdata, const char *realm, int tries,
  188. char *un, char *pw)
  189. {
  190. int *count = userdata;
  191. /* dummy creds; server ignores them anyway. */
  192. strcpy(un, "a");
  193. strcpy(pw, "b");
  194. switch (*count) {
  195. case 0:
  196. case 1:
  197. if (tries == *count) {
  198. *count += 1;
  199. return 0;
  200. } else {
  201. t_context("On request #%d, got attempt #%d", *count, tries);
  202. *count = -1;
  203. return 1;
  204. }
  205. break;
  206. case 2:
  207. case 3:
  208. /* server fails a subsequent request, check that tries has
  209. * reset to zero. */
  210. if (tries == 0) {
  211. *count += 1;
  212. return 0;
  213. } else {
  214. t_context("On retry after failure #%d, tries was %d",
  215. *count, tries);
  216. *count = -1;
  217. return 1;
  218. }
  219. break;
  220. case 4:
  221. case 5:
  222. if (tries > 1) {
  223. t_context("Attempt counter reached #%d", tries);
  224. *count = -1;
  225. return 1;
  226. }
  227. return tries;
  228. default:
  229. t_context("Count reached %d!?", *count);
  230. *count = -1;
  231. }
  232. return 1;
  233. }
  234. /* Test that auth retries are working correctly. */
  235. static int retries(void)
  236. {
  237. ne_session *sess;
  238. int count = 0;
  239. CALL(make_session(&sess, retry_serve, NULL));
  240. ne_set_server_auth(sess, retry_cb, &count);
  241. /* This request will be 401'ed twice, then succeed. */
  242. ONREQ(any_request(sess, "/foo"));
  243. /* auth_cb will have set up context. */
  244. CALL(count != 2);
  245. /* this request will be 401'ed once, then succeed. */
  246. ONREQ(any_request(sess, "/foo"));
  247. /* auth_cb will have set up context. */
  248. CALL(count != 3);
  249. /* some 20x requests. */
  250. ONREQ(any_request(sess, "/foo"));
  251. ONREQ(any_request(sess, "/foo"));
  252. /* this request will be 401'ed once, then succeed. */
  253. ONREQ(any_request(sess, "/foo"));
  254. /* auth_cb will have set up context. */
  255. CALL(count != 4);
  256. /* First request is 401'ed by the server at both attempts. */
  257. ONV(any_request(sess, "/foo") != NE_AUTH,
  258. ("auth succeeded, should have failed: %s", ne_get_error(sess)));
  259. count++;
  260. /* Second request is 401'ed first time, then will succeed if
  261. * retried. 0.18.0 didn't reset the attempt counter though so
  262. * this didn't work. */
  263. ONV(any_request(sess, "/foo") == NE_AUTH,
  264. ("auth failed on second try, should have succeeded: %s", ne_get_error(sess)));
  265. return destroy_and_wait(sess);
  266. }
  267. /* crashes with neon <0.22 */
  268. static int forget_regress(void)
  269. {
  270. ne_session *sess = ne_session_create("http", "localhost", 1234);
  271. ne_forget_auth(sess);
  272. ne_session_destroy(sess);
  273. return OK;
  274. }
  275. static int fail_auth_cb(void *ud, const char *realm, int attempt,
  276. char *un, char *pw)
  277. {
  278. return 1;
  279. }
  280. /* this may trigger a segfault in neon 0.21.x and earlier. */
  281. static int tunnel_regress(void)
  282. {
  283. ne_session *sess;
  284. CALL(proxied_session_server(&sess, "http", "localhost", 443,
  285. single_serve_string,
  286. "HTTP/1.1 401 Auth failed.\r\n"
  287. "WWW-Authenticate: Basic realm=asda\r\n"
  288. "Content-Length: 0\r\n\r\n"));
  289. ne_set_server_auth(sess, fail_auth_cb, NULL);
  290. any_request(sess, "/foo");
  291. return destroy_and_wait(sess);
  292. }
  293. /* regression test for parsing a Negotiate challenge with on parameter
  294. * token. */
  295. static int negotiate_regress(void)
  296. {
  297. ne_session *sess;
  298. CALL(session_server(&sess, single_serve_string,
  299. "HTTP/1.1 401 Auth failed.\r\n"
  300. "WWW-Authenticate: Negotiate\r\n"
  301. "Content-Length: 0\r\n\r\n"));
  302. ne_set_server_auth(sess, fail_auth_cb, NULL);
  303. any_request(sess, "/foo");
  304. return destroy_and_wait(sess);
  305. }
  306. static char *digest_hdr = NULL;
  307. static void dup_header(char *header)
  308. {
  309. if (digest_hdr) ne_free(digest_hdr);
  310. digest_hdr = ne_strdup(header);
  311. }
  312. #define PARM_PROXY (0x0001)
  313. #define PARM_NEXTNONCE (0x0002)
  314. #define PARM_RFC2617 (0x0004)
  315. #define PARM_AINFO (0x0008)
  316. #define PARM_USERHASH (0x0010) /* userhash=true */
  317. #define PARM_UHFALSE (0x0020) /* userhash=false */
  318. #define PARM_ALTUSER (0x0040)
  319. #define PARM_LEGACY (0x0080)
  320. #define PARM_LEGACY_ONLY (0x0100)
  321. struct digest_parms {
  322. const char *realm, *nonce, *opaque, *domain;
  323. enum { ALG_MD5 = 0, ALG_MD5_SESS, ALG_SHA256, ALG_SHA256_SESS, ALG_SHA512_256, ALG_SHA512_256_SESS } alg;
  324. unsigned int flags;
  325. int num_requests;
  326. int stale;
  327. enum digest_failure {
  328. fail_not,
  329. fail_bogus_alg,
  330. fail_req0_stale,
  331. fail_req0_2069_stale,
  332. fail_omit_qop,
  333. fail_omit_realm,
  334. fail_omit_nonce,
  335. fail_ai_bad_nc,
  336. fail_ai_bad_nc_syntax,
  337. fail_ai_bad_digest,
  338. fail_ai_bad_cnonce,
  339. fail_ai_omit_cnonce,
  340. fail_ai_omit_digest,
  341. fail_ai_omit_nc,
  342. fail_outside_domain,
  343. fail_2069_weak
  344. } failure;
  345. };
  346. struct digest_state {
  347. const char *realm, *nonce, *uri, *username, *username_star, *password, *algorithm, *qop,
  348. *method, *opaque;
  349. char userhash[64];
  350. char *cnonce, *digest, *ncval;
  351. long nc;
  352. int count;
  353. int uhash_bool;
  354. };
  355. static char *hash(struct digest_parms *p, ...)
  356. ne_attribute_sentinel;
  357. static char *hash(struct digest_parms *p, ...)
  358. {
  359. va_list ap;
  360. unsigned int flags;
  361. char *h;
  362. switch (p->alg) {
  363. case ALG_SHA512_256_SESS:
  364. case ALG_SHA512_256:
  365. flags = NE_HASH_SHA512_256;
  366. break;
  367. case ALG_SHA256_SESS:
  368. case ALG_SHA256:
  369. flags = NE_HASH_SHA256;
  370. break;
  371. default:
  372. flags = NE_HASH_MD5;
  373. break;
  374. }
  375. va_start(ap, p);
  376. h = ne_vstrhash(flags, ap);
  377. va_end(ap);
  378. if (h == NULL) abort();
  379. return h;
  380. }
  381. /* Write the request-digest into 'digest' (or response-digest if
  382. * auth_info is non-zero) for given digest auth state and
  383. * parameters. */
  384. static char *make_digest(struct digest_state *state, struct digest_parms *parms,
  385. int auth_info)
  386. {
  387. char *h_a1, *h_a2, *rv;
  388. h_a1 = hash(parms, state->username, ":", state->realm, ":",
  389. state->password, NULL);
  390. if (parms->alg == ALG_MD5_SESS || parms->alg == ALG_SHA256_SESS || parms->alg == ALG_SHA512_256_SESS) {
  391. char *sess_h_a1;
  392. sess_h_a1 = hash(parms, h_a1, ":", state->nonce, ":", state->cnonce, NULL);
  393. ne_free(h_a1);
  394. h_a1 = sess_h_a1;
  395. }
  396. h_a2 = hash(parms, !auth_info ? state->method : "", ":", state->uri, NULL);
  397. if (parms->flags & PARM_RFC2617) {
  398. rv = hash(parms,
  399. h_a1, ":", state->nonce, ":",
  400. state->ncval, ":", state->cnonce, ":", state->qop, ":",
  401. h_a2, NULL);
  402. }
  403. else {
  404. /* RFC2069-style */
  405. rv = hash(parms, h_a1, ":", state->nonce, ":", h_a2, NULL);
  406. }
  407. ne_free(h_a2);
  408. ne_free(h_a1);
  409. return rv;
  410. }
  411. /* Verify that the response-digest matches expected state. */
  412. static int check_digest(struct digest_state *state, struct digest_parms *parms)
  413. {
  414. char *digest;
  415. digest = make_digest(state, parms, 0);
  416. ONV(digest == NULL,
  417. ("failed to create digest for %s", state->algorithm));
  418. ONV(strcmp(digest, state->digest),
  419. ("bad digest; expected %s got %s", state->digest, digest));
  420. ne_free(digest);
  421. return OK;
  422. }
  423. #define DIGCMP(field) \
  424. do { \
  425. ONCMP(state->field, newstate.field, \
  426. "Digest response header", #field); \
  427. } while (0)
  428. #define NPARAM(field, param) \
  429. do { \
  430. if (ne_strcasecmp(name, param) == 0) { \
  431. ONV(newstate.field != NULL, \
  432. ("received multiple %s params: %s, %s", param, \
  433. newstate.field, val)); \
  434. newstate.field = val; \
  435. } \
  436. } while (0)
  437. #define PARAM(field) NPARAM(field, #field)
  438. /* Verify that Digest auth request header, 'header', meets expected
  439. * state and parameters. */
  440. static int verify_digest_header(struct digest_state *state,
  441. struct digest_parms *parms,
  442. char *header)
  443. {
  444. char *ptr;
  445. struct digest_state newstate = {0};
  446. ptr = ne_token(&header, ' ');
  447. ONCMP("Digest", ptr, "Digest response", "scheme name");
  448. while (header) {
  449. char *name, *val;
  450. ptr = ne_qtoken(&header, ',', "\"\'");
  451. ONN("quoting broken", ptr == NULL);
  452. name = ne_shave(ptr, " ");
  453. val = strchr(name, '=');
  454. ONV(val == NULL, ("bad name/value pair: %s", name));
  455. *val++ = '\0';
  456. val = ne_shave(val, "\"\' ");
  457. NE_DEBUG(NE_DBG_HTTP, "got field: [%s] = [%s]\n", name, val);
  458. PARAM(uri);
  459. PARAM(realm);
  460. PARAM(username);
  461. PARAM(nonce);
  462. PARAM(algorithm);
  463. PARAM(qop);
  464. PARAM(opaque);
  465. PARAM(cnonce);
  466. NPARAM(username_star, "username*");
  467. if (ne_strcasecmp(name, "nc") == 0) {
  468. long nc = strtol(val, NULL, 16);
  469. ONV(nc != state->nc,
  470. ("got bad nonce count: %ld (%s) not %ld",
  471. nc, val, state->nc));
  472. state->ncval = ne_strdup(val);
  473. }
  474. else if (ne_strcasecmp(name, "response") == 0) {
  475. state->digest = ne_strdup(val);
  476. }
  477. else if (ne_strcasecmp(name, "userhash") == 0 ) {
  478. newstate.uhash_bool = strcmp(val, "true") == 0;
  479. }
  480. }
  481. ONN("cnonce param missing or short for 2617-style auth",
  482. (parms->flags & PARM_RFC2617)
  483. && (newstate.cnonce == NULL
  484. || strlen(newstate.cnonce) < 32));
  485. if (alt_username_star) {
  486. ONN("unexpected userhash=true sent", newstate.uhash_bool);
  487. ONN("username* missing", newstate.username_star == NULL);
  488. ONCMP(alt_username_star, newstate.username_star, "Digest field", "username*");
  489. }
  490. else if (parms->flags & PARM_USERHASH) {
  491. ONN("userhash missing", !newstate.uhash_bool);
  492. ONCMP(state->userhash, newstate.username,
  493. "Digest username (userhash) field", "userhash");
  494. }
  495. else {
  496. ONN("unexpected userhash=true sent", newstate.uhash_bool);
  497. DIGCMP(username);
  498. }
  499. DIGCMP(realm);
  500. if (!parms->domain)
  501. DIGCMP(uri);
  502. DIGCMP(nonce);
  503. DIGCMP(opaque);
  504. DIGCMP(algorithm);
  505. if (parms->flags & PARM_RFC2617) {
  506. DIGCMP(qop);
  507. }
  508. if (newstate.cnonce) {
  509. state->cnonce = ne_strdup(newstate.cnonce);
  510. }
  511. if (parms->domain) {
  512. state->uri = ne_strdup(newstate.uri);
  513. }
  514. ONN("no digest param given", !state->digest);
  515. CALL(check_digest(state, parms));
  516. state->nc++;
  517. return OK;
  518. }
  519. static char *make_authinfo_header(struct digest_state *state,
  520. struct digest_parms *parms)
  521. {
  522. ne_buffer *buf = ne_buffer_create();
  523. char *digest, *ncval, *cnonce;
  524. if (parms->failure == fail_ai_bad_digest) {
  525. digest = ne_strdup("fish");
  526. } else {
  527. digest = make_digest(state, parms, 1);
  528. }
  529. if (parms->failure == fail_ai_bad_nc_syntax) {
  530. ncval = "zztop";
  531. } else if (parms->failure == fail_ai_bad_nc) {
  532. ncval = "999";
  533. } else {
  534. ncval = state->ncval;
  535. }
  536. if (parms->failure == fail_ai_bad_cnonce) {
  537. cnonce = "another-fish";
  538. } else {
  539. cnonce = state->cnonce;
  540. }
  541. if ((parms->flags & PARM_PROXY)) {
  542. ne_buffer_czappend(buf, "Proxy-");
  543. }
  544. ne_buffer_czappend(buf, "Authentication-Info: ");
  545. if ((parms->flags & PARM_RFC2617) == 0) {
  546. ne_buffer_concat(buf, "rspauth=\"", digest, "\"", NULL);
  547. } else {
  548. if (parms->failure != fail_ai_omit_nc) {
  549. ne_buffer_concat(buf, "nc=", ncval, ", ", NULL);
  550. }
  551. if (parms->failure != fail_ai_omit_cnonce) {
  552. ne_buffer_concat(buf, "cnonce=\"", cnonce, "\", ", NULL);
  553. }
  554. if (parms->failure != fail_ai_omit_digest) {
  555. ne_buffer_concat(buf, "rspauth=\"", digest, "\", ", NULL);
  556. }
  557. if (parms->flags & PARM_NEXTNONCE) {
  558. state->nonce = ne_concat("next-", state->nonce, NULL);
  559. ne_buffer_concat(buf, "nextnonce=\"", state->nonce, "\", ", NULL);
  560. state->nc = 1;
  561. }
  562. ne_buffer_czappend(buf, "qop=\"auth\"");
  563. }
  564. ne_free(digest);
  565. return ne_buffer_finish(buf);
  566. }
  567. static char *make_digest_header(struct digest_state *state,
  568. struct digest_parms *parms)
  569. {
  570. ne_buffer *buf = ne_buffer_create();
  571. const char *algorithm;
  572. algorithm = parms->failure == fail_bogus_alg ? "fish"
  573. : state->algorithm;
  574. ne_buffer_concat(buf,
  575. (parms->flags & PARM_PROXY) ? "Proxy-Authenticate"
  576. : "WWW-Authenticate",
  577. ": Digest "
  578. "realm=\"", parms->realm, "\", ", NULL);
  579. if (parms->flags & PARM_RFC2617) {
  580. ne_buffer_concat(buf, "algorithm=\"", algorithm, "\", ",
  581. "qop=\"", state->qop, "\", ", NULL);
  582. }
  583. if (parms->opaque) {
  584. ne_buffer_concat(buf, "opaque=\"", parms->opaque, "\", ", NULL);
  585. }
  586. if (parms->domain) {
  587. ne_buffer_concat(buf, "domain=\"", parms->domain, "\", ", NULL);
  588. }
  589. if (parms->flags & PARM_USERHASH) {
  590. ne_buffer_czappend(buf, "userhash=true, ");
  591. }
  592. else if (parms->flags & PARM_UHFALSE) {
  593. ne_buffer_czappend(buf, "userhash=false, ");
  594. }
  595. if (parms->failure == fail_req0_stale
  596. || parms->failure == fail_req0_2069_stale
  597. || parms->stale == parms->num_requests) {
  598. ne_buffer_concat(buf, "stale='true', ", NULL);
  599. }
  600. ne_buffer_concat(buf, "nonce=\"", state->nonce, "\"", NULL);
  601. return ne_buffer_finish(buf);
  602. }
  603. /* Server process for Digest auth handling. */
  604. static int serve_digest(ne_socket *sock, void *userdata)
  605. {
  606. struct digest_parms *parms = userdata;
  607. struct digest_state state;
  608. char resp[NE_BUFSIZ], *rspdigest;
  609. if ((parms->flags & PARM_PROXY))
  610. state.uri = "http://www.example.com/fish";
  611. else if (parms->domain)
  612. state.uri = "/fish/0";
  613. else
  614. state.uri = "/fish";
  615. state.method = "GET";
  616. state.realm = parms->realm;
  617. state.nonce = parms->nonce;
  618. state.opaque = parms->opaque;
  619. if (parms->flags & PARM_ALTUSER)
  620. state.username = alt_username;
  621. else
  622. state.username = username;
  623. state.password = password;
  624. state.nc = 1;
  625. switch (parms->alg) {
  626. case ALG_SHA512_256: state.algorithm = "SHA-512-256"; break;
  627. case ALG_SHA512_256_SESS: state.algorithm = "SHA-512-256-sess"; break;
  628. case ALG_SHA256: state.algorithm = "SHA-256"; break;
  629. case ALG_SHA256_SESS: state.algorithm = "SHA-256-sess"; break;
  630. case ALG_MD5_SESS: state.algorithm = "MD5-sess"; break;
  631. default:
  632. case ALG_MD5: state.algorithm = "MD5"; break;
  633. }
  634. state.qop = "auth";
  635. if (parms->flags & PARM_USERHASH) {
  636. char *uh = hash(parms, username, ":", parms->realm, NULL);
  637. ONN("userhash too long", strlen(uh) >= sizeof state.userhash);
  638. ne_strnzcpy(state.userhash, uh, sizeof state.userhash);
  639. ne_free(uh);
  640. }
  641. state.cnonce = state.digest = state.ncval = NULL;
  642. parms->num_requests += parms->stale ? 1 : 0;
  643. NE_DEBUG(NE_DBG_HTTP, ">>>> Response sequence begins, %d requests.\n",
  644. parms->num_requests);
  645. want_header = (parms->flags & PARM_PROXY) ? "Proxy-Authorization" : "Authorization";
  646. digest_hdr = NULL;
  647. got_header = dup_header;
  648. CALL(discard_request(sock));
  649. ONV(digest_hdr != NULL,
  650. ("got unwarranted WWW-Auth header: %s", digest_hdr));
  651. rspdigest = make_digest_header(&state, parms);
  652. ne_snprintf(resp, sizeof resp,
  653. "HTTP/1.1 %d Auth Denied\r\n"
  654. "%s\r\n"
  655. "Content-Length: 0\r\n" "\r\n",
  656. (parms->flags & PARM_PROXY) ? 407 : 401,
  657. rspdigest);
  658. ne_free(rspdigest);
  659. SEND_STRING(sock, resp);
  660. /* Give up now if we've sent a challenge which should force the
  661. * client to fail immediately: */
  662. if (parms->failure == fail_bogus_alg
  663. || parms->failure == fail_req0_stale
  664. || parms->failure == fail_req0_2069_stale) {
  665. return OK;
  666. }
  667. do {
  668. digest_hdr = NULL;
  669. CALL(discard_request(sock));
  670. if (digest_hdr && parms->domain && (parms->num_requests & 1) != 0) {
  671. SEND_STRING(sock, "HTTP/1.1 400 Used Auth Outside Domain\r\n\r\n");
  672. return OK;
  673. }
  674. else if (digest_hdr == NULL && parms->domain && (parms->num_requests & 1) != 0) {
  675. /* Do nothing. */
  676. NE_DEBUG(NE_DBG_HTTP, "No Authorization header sent, good.\n");
  677. }
  678. else {
  679. ONN("no Authorization header sent", digest_hdr == NULL);
  680. ONERR(sock, verify_digest_header(&state, parms, digest_hdr));
  681. }
  682. if (parms->num_requests == parms->stale) {
  683. char *dig;
  684. state.nonce = ne_concat("stale-", state.nonce, NULL);
  685. state.nc = 1;
  686. dig = make_digest_header(&state, parms);
  687. ne_snprintf(resp, sizeof resp,
  688. "HTTP/1.1 %d Auth Denied\r\n"
  689. "%s\r\n"
  690. "Content-Length: 0\r\n" "\r\n",
  691. (parms->flags & PARM_PROXY) ? 407 : 401,
  692. dig);
  693. ne_free(dig);
  694. }
  695. else if (parms->flags & PARM_AINFO) {
  696. char *ai = make_authinfo_header(&state, parms);
  697. ne_snprintf(resp, sizeof resp,
  698. "HTTP/1.1 200 Well, if you insist\r\n"
  699. "Content-Length: 0\r\n"
  700. "%s\r\n"
  701. "\r\n", ai);
  702. ne_free(ai);
  703. } else {
  704. ne_snprintf(resp, sizeof resp,
  705. "HTTP/1.1 200 You did good\r\n"
  706. "Content-Length: 0\r\n" "\r\n");
  707. }
  708. SEND_STRING(sock, resp);
  709. NE_DEBUG(NE_DBG_HTTP, "Handled request; %d requests remain.\n",
  710. parms->num_requests - 1);
  711. } while (--parms->num_requests);
  712. return OK;
  713. }
  714. static int test_digest(struct digest_parms *parms)
  715. {
  716. ne_session *sess;
  717. unsigned proto = NE_AUTH_DIGEST;
  718. if ((parms->flags & PARM_LEGACY))
  719. proto |= NE_AUTH_LEGACY_DIGEST;
  720. else if ((parms->flags & PARM_LEGACY_ONLY))
  721. proto = NE_AUTH_LEGACY_DIGEST;
  722. NE_DEBUG(NE_DBG_HTTP, ">>>> Request sequence begins "
  723. "(reqs=%d, nonce=%s, rfc=%s, stale=%d, proxy=%d).\n",
  724. parms->num_requests,
  725. parms->nonce, (parms->flags & PARM_RFC2617) ? "2617" : "2069",
  726. parms->stale, !!(parms->flags & PARM_PROXY));
  727. if ((parms->flags & PARM_PROXY)) {
  728. CALL(proxied_session_server(&sess, "http", "www.example.com", 80,
  729. serve_digest, parms));
  730. ne_set_proxy_auth(sess, auth_cb, NULL);
  731. }
  732. else {
  733. CALL(session_server(&sess, serve_digest, parms));
  734. if ((parms->flags & PARM_ALTUSER))
  735. ne_add_auth(sess, proto, auth_provide_cb, NULL);
  736. else
  737. ne_add_server_auth(sess, proto, auth_cb, NULL);
  738. }
  739. do {
  740. CALL(any_2xx_request(sess, "/fish"));
  741. } while (--parms->num_requests);
  742. return destroy_and_wait(sess);
  743. }
  744. /* Test for RFC2617-style Digest auth. */
  745. static int digest(void)
  746. {
  747. struct digest_parms parms[] = {
  748. /* RFC 2617-style */
  749. { "WallyWorld", "this-is-a-nonce", NULL, NULL, ALG_MD5, PARM_RFC2617, 1, 0, fail_not },
  750. { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617, 1, 0, fail_not },
  751. /* ... with A-I */
  752. { "WallyWorld", "nonce-nonce-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO, 1, 0, fail_not },
  753. /* ... with md5-sess. */
  754. { "WallyWorld", "nonce-nonce-nonce", "opaque-string", NULL, ALG_MD5_SESS, PARM_RFC2617 | PARM_AINFO, 1, 0, fail_not },
  755. /* many requests, with changing nonces; tests for next-nonce handling bug. */
  756. { "WallyWorld", "this-is-a-nonce", "opaque-thingy", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO | PARM_NEXTNONCE, 20, 0, fail_not },
  757. /* staleness. */
  758. { "WallyWorld", "this-is-a-nonce", "opaque-thingy", NULL, ALG_MD5, PARM_RFC2617 | PARM_AINFO, 3, 2, fail_not },
  759. /* 2069 + stale */
  760. { "WallyWorld", "this-is-a-nonce", NULL, NULL, ALG_MD5, PARM_LEGACY|PARM_AINFO, 3, 2, fail_not },
  761. /* RFC 7616-style */
  762. { "WallyWorld", "new-day-new-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_USERHASH, 1, 0, fail_not },
  763. /* ... userhash=false */
  764. { "WallyWorld", "just-another-nonce", "new-opaque", NULL, ALG_MD5, PARM_RFC2617 | PARM_UHFALSE, 1, 0, fail_not },
  765. /* RFC 2069-style */
  766. { "WallyWorld", "lah-di-da-di-dah", NULL, NULL, ALG_MD5, PARM_LEGACY, 1, 0, fail_not },
  767. { "WallyWorld", "lah-lah-lah-lah", NULL, NULL, ALG_MD5, PARM_LEGACY_ONLY, 1, 0, fail_not },
  768. { "WallyWorld", "fee-fi-fo-fum", "opaque-string", NULL, ALG_MD5, PARM_LEGACY, 1, 0, fail_not },
  769. { "WallyWorld", "fee-fi-fo-fum", "opaque-string", NULL, ALG_MD5, PARM_AINFO|PARM_LEGACY, 1, 0, fail_not },
  770. /* Proxy auth */
  771. { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617|PARM_PROXY, 1, 0, fail_not },
  772. /* Proxy + nextnonce */
  773. { "WallyWorld", "this-is-also-a-nonce", "opaque-string", NULL, ALG_MD5, PARM_RFC2617|PARM_AINFO|PARM_PROXY, 1, 0, fail_not },
  774. { NULL }
  775. };
  776. size_t n;
  777. for (n = 0; parms[n].realm; n++) {
  778. CALL(test_digest(&parms[n]));
  779. }
  780. return OK;
  781. }
  782. static int digest_sha256(void)
  783. {
  784. struct digest_parms parms[] = {
  785. { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617, 1, 0, fail_not },
  786. { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
  787. { "WallyWorld", "nonce-sha-session", "opaque-string", NULL, ALG_SHA256_SESS, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
  788. { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA256, PARM_RFC2617|PARM_AINFO, 8, 0, fail_not },
  789. { NULL },
  790. };
  791. size_t n;
  792. if (!has_sha256) {
  793. t_context("SHA-256 not supported");
  794. return SKIP;
  795. }
  796. for (n = 0; parms[n].realm; n++) {
  797. CALL(test_digest(&parms[n]));
  798. }
  799. return OK;
  800. }
  801. static int digest_sha512_256(void)
  802. {
  803. struct digest_parms parms[] = {
  804. { "WallyWorld", "nonce-sha5-nonce", "opaque-string", NULL, ALG_SHA512_256, PARM_RFC2617, 1, 0, fail_not },
  805. { "WallyWorld", "nonce-sha5-nonce", "opaque-string", NULL, ALG_SHA512_256, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
  806. { "WallyWorld", "nonce-sha5-session", "opaque-string", NULL, ALG_SHA512_256_SESS, PARM_RFC2617|PARM_AINFO, 1, 0, fail_not },
  807. { "WallyWorld", "nonce-sha-nonce", "opaque-string", NULL, ALG_SHA512_256_SESS, PARM_RFC2617|PARM_AINFO, 20, 0, fail_not },
  808. { NULL },
  809. };
  810. size_t n;
  811. if (!has_sha512_256) {
  812. t_context("SHA-512/256 not supported");
  813. return SKIP;
  814. }
  815. for (n = 0; parms[n].realm; n++) {
  816. CALL(test_digest(&parms[n]));
  817. }
  818. return OK;
  819. }
  820. static int digest_username_star(void)
  821. {
  822. static const struct {
  823. const char *username_raw, *username_star;
  824. } ts[] = {
  825. { "Aladdin", NULL },
  826. { "[email protected]", NULL },
  827. { "Ałâddín", "UTF-8''A%c5%82%c3%a2dd%c3%adn" },
  828. { "Jäsøn Doe", "UTF-8''J%c3%a4s%c3%b8n%20Doe" },
  829. { "foo bar", "UTF-8''foo%20bar"},
  830. { "foo\"bar", "UTF-8''foo%22bar" },
  831. { NULL, NULL }
  832. };
  833. unsigned n;
  834. int ret = OK;
  835. for (n = 0; ret == OK && ts[n].username_raw; n++) {
  836. struct digest_parms parms = {
  837. "WallyWorld", "nonce-sha5-nonce", "opaque-string",
  838. NULL, ALG_MD5, PARM_RFC2617|PARM_UHFALSE|PARM_ALTUSER, 1, 0, fail_not };
  839. alt_username = ts[n].username_raw;
  840. alt_username_star = ts[n].username_star;
  841. ret = test_digest(&parms);
  842. }
  843. alt_username = NULL;
  844. alt_username_star = NULL;
  845. return ret;
  846. }
  847. static int digest_failures(void)
  848. {
  849. struct digest_parms parms;
  850. static const struct {
  851. enum digest_failure mode;
  852. const char *message;
  853. } fails[] = {
  854. { fail_ai_bad_nc, "nonce count mismatch" },
  855. { fail_ai_bad_nc_syntax, "could not parse nonce count" },
  856. { fail_ai_bad_digest, "digest mismatch" },
  857. { fail_ai_bad_cnonce, "client nonce mismatch" },
  858. { fail_ai_omit_nc, "missing parameters" },
  859. { fail_ai_omit_digest, "missing parameters" },
  860. { fail_ai_omit_cnonce, "missing parameters" },
  861. { fail_bogus_alg, "unknown algorithm" },
  862. { fail_req0_stale, "initial Digest challenge was stale" },
  863. { fail_req0_2069_stale, "initial Digest challenge was stale" },
  864. { fail_2069_weak, "legacy Digest challenge not supported" },
  865. { fail_not, NULL }
  866. };
  867. unsigned n;
  868. memset(&parms, 0, sizeof parms);
  869. parms.realm = "WallyWorld";
  870. parms.nonce = "random-invented-string";
  871. parms.opaque = NULL;
  872. parms.flags = PARM_AINFO;
  873. parms.num_requests = 1;
  874. for (n = 0; fails[n].message; n++) {
  875. ne_session *sess;
  876. int ret;
  877. parms.failure = fails[n].mode;
  878. if (parms.failure == fail_req0_2069_stale || parms.failure == fail_2069_weak)
  879. parms.flags &= ~PARM_RFC2617;
  880. else
  881. parms.flags |= PARM_RFC2617;
  882. NE_DEBUG(NE_DBG_HTTP, ">>> New Digest failure test, "
  883. "expecting failure '%s'\n", fails[n].message);
  884. CALL(session_server(&sess, serve_digest, &parms));
  885. ne_set_server_auth(sess, auth_cb, NULL);
  886. ret = any_2xx_request(sess, "/fish");
  887. ONV(ret == NE_OK,
  888. ("request success (iter %u); expecting error '%s'",
  889. n, fails[n].message));
  890. ONV(strstr(ne_get_error(sess), fails[n].message) == NULL,
  891. ("request fails with error '%s'; expecting '%s'",
  892. ne_get_error(sess), fails[n].message));
  893. ne_session_destroy(sess);
  894. if (fails[n].mode == fail_bogus_alg
  895. || fails[n].mode == fail_req0_stale
  896. || fails[n].mode == fail_2069_weak) {
  897. reap_server();
  898. } else {
  899. CALL(await_server());
  900. }
  901. }
  902. return OK;
  903. }
  904. static int fail_cb(void *userdata, const char *realm, int tries,
  905. char *un, char *pw)
  906. {
  907. ne_buffer *buf = userdata;
  908. char str[64];
  909. if (strcmp(realm, "colonic") == 0 && ne_buffer_size(buf) == 0) {
  910. ne_strnzcpy(un, "user:name", NE_ABUFSIZ);
  911. ne_strnzcpy(pw, "passwerd", NE_ABUFSIZ);
  912. return 0;
  913. }
  914. ne_snprintf(str, sizeof str, "<%s, %d>", realm, tries);
  915. ne_buffer_zappend(buf, str);
  916. return -1;
  917. }
  918. static int fail_challenge(void)
  919. {
  920. static const struct {
  921. const char *resp, *error, *challs;
  922. } ts[] = {
  923. /* only possible Basic parse failure. */
  924. { "Basic", "missing realm in Basic challenge" },
  925. { "Basic realm=\"colonic\"", "username containing colon" },
  926. /* Digest parameter invalid/omitted failure cases: */
  927. { "Digest algorithm=MD5, qop=auth, nonce=\"foo\"",
  928. "missing parameter in Digest challenge" },
  929. { "Digest algorithm=MD5, qop=auth, realm=\"foo\"",
  930. "missing parameter in Digest challenge" },
  931. { "Digest algorithm=ZEBEDEE-GOES-BOING, qop=auth, realm=\"foo\"",
  932. "unknown algorithm in Digest challenge" },
  933. { "Digest algorithm=MD5-sess, realm=\"foo\"",
  934. "incompatible algorithm in Digest challenge" },
  935. { "Digest algorithm=MD5, qop=auth, nonce=\"foo\", realm=\"foo\", "
  936. "domain=\"http://[::1/\"", "could not parse domain" },
  937. /* Multiple challenge failure cases: */
  938. { "Basic, Digest",
  939. "missing parameter in Digest challenge, missing realm in Basic challenge" },
  940. { "Digest realm=\"foo\", algorithm=MD5, qop=auth, nonce=\"foo\","
  941. " Basic realm=\"foo\"",
  942. "rejected Digest challenge, rejected Basic challenge" },
  943. { "WhizzBangAuth realm=\"foo\", "
  944. "Basic realm='foo'",
  945. "ignored WhizzBangAuth challenge, rejected Basic challenge" },
  946. { "", "could not parse challenge" },
  947. /* neon 0.26.x regression in "attempt" handling. */
  948. { "Basic realm=\"foo\", "
  949. "Digest realm=\"bar\", algorithm=MD5, qop=auth, nonce=\"foo\"",
  950. "rejected Digest challenge, rejected Basic challenge"
  951. , "<bar, 0><foo, 1>" /* Digest challenge first, Basic second. */
  952. }
  953. };
  954. unsigned n;
  955. for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
  956. char resp[512];
  957. ne_session *sess;
  958. int ret;
  959. ne_buffer *buf = ne_buffer_create();
  960. ne_snprintf(resp, sizeof resp,
  961. "HTTP/1.1 401 Auth Denied\r\n"
  962. "WWW-Authenticate: %s\r\n"
  963. "Content-Length: 0\r\n" "\r\n",
  964. ts[n].resp);
  965. CALL(multi_session_server(&sess, "http", "localhost",
  966. 2, single_serve_string, resp));
  967. ne_set_server_auth(sess, fail_cb, buf);
  968. ret = any_2xx_request(sess, "/fish");
  969. ONV(ret == NE_OK,
  970. ("request success (iter %u); expecting error '%s'",
  971. n, ts[n].error));
  972. ONV(strstr(ne_get_error(sess), ts[n].error) == NULL,
  973. ("request fails with error '%s'; expecting '%s'",
  974. ne_get_error(sess), ts[n].error));
  975. if (ts[n].challs) {
  976. ONCMP(ts[n].challs, buf->data, "challenge callback",
  977. "invocation order");
  978. }
  979. ne_session_destroy(sess);
  980. ne_buffer_destroy(buf);
  981. reap_server();
  982. }
  983. return OK;
  984. }
  985. struct multi_context {
  986. int id;
  987. ne_buffer *buf;
  988. };
  989. static int multi_cb(void *userdata, const char *realm, int tries,
  990. char *un, char *pw)
  991. {
  992. struct multi_context *ctx = userdata;
  993. ne_buffer_snprintf(ctx->buf, 128, "[id=%d, realm=%s, tries=%d]",
  994. ctx->id, realm, tries);
  995. return -1;
  996. }
  997. static int multi_handler(void)
  998. {
  999. ne_session *sess;
  1000. struct multi_context c[2];
  1001. unsigned n;
  1002. ne_buffer *buf = ne_buffer_create();
  1003. CALL(make_session(&sess, single_serve_string,
  1004. "HTTP/1.1 401 Auth Denied\r\n"
  1005. "WWW-Authenticate: Basic realm='fish',"
  1006. " Digest realm='food', algorithm=MD5, qop=auth, nonce=gaga\r\n"
  1007. "Content-Length: 0\r\n" "\r\n"));
  1008. for (n = 0; n < 2; n++) {
  1009. c[n].buf = buf;
  1010. c[n].id = n + 1;
  1011. }
  1012. ne_add_server_auth(sess, NE_AUTH_BASIC, multi_cb, &c[0]);
  1013. ne_add_server_auth(sess, NE_AUTH_DIGEST, multi_cb, &c[1]);
  1014. any_request(sess, "/fish");
  1015. ONCMP("[id=2, realm=food, tries=0]"
  1016. "[id=1, realm=fish, tries=0]", buf->data,
  1017. "multiple callback", "invocation order");
  1018. ne_buffer_destroy(buf);
  1019. return destroy_and_wait(sess);
  1020. }
  1021. static int multi_rfc7616(void)
  1022. {
  1023. ne_session *sess;
  1024. struct multi_context c[2];
  1025. unsigned n;
  1026. ne_buffer *buf, *exp;
  1027. buf = ne_buffer_create();
  1028. CALL(make_session(&sess, single_serve_string,
  1029. "HTTP/1.1 401 Auth Denied\r\n"
  1030. "WWW-Authenticate: "
  1031. "Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1032. "Basic realm='basic-realm', "
  1033. "Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
  1034. "Digest realm='sha256-realm', algorithm=SHA-256, qop=auth, nonce=gaga\r\n"
  1035. "Content-Length: 0\r\n" "\r\n"));
  1036. for (n = 0; n < 2; n++) {
  1037. c[n].buf = buf;
  1038. c[n].id = n + 1;
  1039. }
  1040. ne_add_server_auth(sess, NE_AUTH_BASIC, multi_cb, &c[0]);
  1041. ne_add_server_auth(sess, NE_AUTH_DIGEST, multi_cb, &c[1]);
  1042. any_request(sess, "/fish");
  1043. exp = ne_buffer_create();
  1044. n = 0;
  1045. if (has_sha512_256)
  1046. ne_buffer_snprintf(exp, 100, "[id=2, realm=sha512-realm, tries=%u]", n++);
  1047. if (has_sha256)
  1048. ne_buffer_snprintf(exp, 100, "[id=2, realm=sha256-realm, tries=%u]", n++);
  1049. ne_buffer_snprintf(exp, 100,
  1050. "[id=2, realm=md5-realm, tries=%u]"
  1051. "[id=1, realm=basic-realm, tries=0]", n);
  1052. ONV(strcmp(exp->data, buf->data),
  1053. ("unexpected callback ordering.\n"
  1054. "expected: %s\n"
  1055. "actual: %s\n",
  1056. exp->data, buf->data));
  1057. ne_buffer_destroy(buf);
  1058. ne_buffer_destroy(exp);
  1059. return destroy_and_wait(sess);
  1060. }
  1061. static int multi_provider_cb(void *userdata, int attempt,
  1062. unsigned protocol, const char *realm,
  1063. char *un, char *pw, size_t buflen)
  1064. {
  1065. ne_buffer *buf = userdata;
  1066. const char *ctx;
  1067. if (buflen == NE_ABUFSIZ) {
  1068. NE_DEBUG(NE_DBG_HTTPAUTH, "auth: FAILED for short buffer length.\n");
  1069. return -1;
  1070. }
  1071. if ((protocol & NE_AUTH_PROXY) == NE_AUTH_PROXY) {
  1072. ctx = "proxy";
  1073. protocol ^= NE_AUTH_PROXY;
  1074. }
  1075. else {
  1076. ctx = "server";
  1077. }
  1078. ne_buffer_snprintf(buf, 128, "[%s: proto=%u, realm=%s, attempt=%d]",
  1079. ctx, protocol, realm, attempt);
  1080. ne_strnzcpy(un, "foo", buflen);
  1081. ne_strnzcpy(pw, "bar", buflen);
  1082. return protocol == NE_AUTH_BASIC ? 0 : -1;
  1083. }
  1084. static int serve_provider(ne_socket *s, void *userdata)
  1085. {
  1086. CALL(serve_response(s,
  1087. "HTTP/1.1 407 Proxy Auth Plz\r\n"
  1088. "Proxy-Authenticate: Basic realm='proxy-realm'\r\n"
  1089. "Content-Length: 0\r\n" "\r\n"));
  1090. CALL(serve_response(s,
  1091. "HTTP/1.1 401 Auth Denied\r\n"
  1092. "WWW-Authenticate: "
  1093. " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1094. " Basic realm='basic-realm', "
  1095. " Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
  1096. " Digest realm='sha256-realm', algorithm=SHA-256, qop=auth, nonce=gaga\r\n"
  1097. "Content-Length: 0\r\n" "\r\n"));
  1098. CALL(serve_response(s,
  1099. "HTTP/1.1 401 Auth Denied\r\n"
  1100. "WWW-Authenticate: "
  1101. " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1102. " Basic realm='basic-realm'\r\n"
  1103. "Content-Length: 0\r\n" "\r\n"));
  1104. return serve_response(s,
  1105. "HTTP/1.1 200 OK\r\n"
  1106. "Content-Length: 0\r\n" "\r\n");
  1107. }
  1108. static int multi_provider(void)
  1109. {
  1110. ne_session *sess;
  1111. ne_buffer *buf = ne_buffer_create(), *exp;
  1112. CALL(make_session(&sess, serve_provider, NULL));
  1113. ne_add_auth(sess, NE_AUTH_DIGEST|NE_AUTH_BASIC, multi_provider_cb, buf);
  1114. ONREQ(any_request(sess, "/fish"));
  1115. exp = ne_buffer_create();
  1116. ne_buffer_snprintf(exp, 100,
  1117. "[proxy: proto=%u, realm=proxy-realm, attempt=0]",
  1118. NE_AUTH_BASIC);
  1119. if (has_sha512_256)
  1120. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=0]",
  1121. NE_AUTH_DIGEST);
  1122. if (has_sha256)
  1123. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha256-realm, attempt=0]",
  1124. NE_AUTH_DIGEST);
  1125. ne_buffer_snprintf(exp, 100,
  1126. "[server: proto=%u, realm=md5-realm, attempt=0]"
  1127. "[server: proto=%u, realm=basic-realm, attempt=0]",
  1128. NE_AUTH_DIGEST, NE_AUTH_BASIC);
  1129. if (has_sha512_256)
  1130. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=1]",
  1131. NE_AUTH_DIGEST);
  1132. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=basic-realm, attempt=1]",
  1133. NE_AUTH_BASIC);
  1134. ONV(strcmp(exp->data, buf->data),
  1135. ("unexpected callback ordering.\n"
  1136. "expected: %s\n"
  1137. "actual: %s\n",
  1138. exp->data, buf->data));
  1139. ne_buffer_destroy(buf);
  1140. ne_buffer_destroy(exp);
  1141. return destroy_and_wait(sess);
  1142. }
  1143. static int domains(void)
  1144. {
  1145. ne_session *sess;
  1146. struct digest_parms parms;
  1147. memset(&parms, 0, sizeof parms);
  1148. parms.realm = "WallyWorld";
  1149. parms.flags = PARM_RFC2617;
  1150. parms.nonce = "agoog";
  1151. parms.domain = "http://localhost:4242/fish/ https://example.com /agaor /other";
  1152. parms.num_requests = 6;
  1153. CALL(proxied_session_server(&sess, "http", "localhost", 4242,
  1154. serve_digest, &parms));
  1155. ne_set_server_auth(sess, auth_cb, NULL);
  1156. CALL(any_2xx_request(sess, "/fish/0"));
  1157. CALL(any_2xx_request(sess, "/outside"));
  1158. CALL(any_2xx_request(sess, "/others"));
  1159. CALL(any_2xx_request(sess, "/fish"));
  1160. CALL(any_2xx_request(sess, "/fish/2"));
  1161. CALL(any_2xx_request(sess, "*"));
  1162. return destroy_and_wait(sess);
  1163. }
  1164. /* This segfaulted with 0.28.0 through 0.28.2 inclusive. */
  1165. static int CVE_2008_3746(void)
  1166. {
  1167. ne_session *sess;
  1168. struct digest_parms parms;
  1169. memset(&parms, 0, sizeof parms);
  1170. parms.realm = "WallyWorld";
  1171. parms.flags = PARM_RFC2617;
  1172. parms.nonce = "agoog";
  1173. parms.domain = "foo";
  1174. parms.num_requests = 1;
  1175. CALL(proxied_session_server(&sess, "http", "www.example.com", 80,
  1176. serve_digest, &parms));
  1177. ne_set_server_auth(sess, auth_cb, NULL);
  1178. any_2xx_request(sess, "/fish/0");
  1179. return destroy_and_wait(sess);
  1180. }
  1181. static int defaults(void)
  1182. {
  1183. ne_session *sess;
  1184. CALL(make_session(&sess, auth_serve, CHAL_WALLY));
  1185. ne_add_server_auth(sess, NE_AUTH_DEFAULT, auth_cb, NULL);
  1186. CALL(any_2xx_request(sess, "/norman"));
  1187. ne_session_destroy(sess);
  1188. CALL(await_server());
  1189. CALL(make_session(&sess, auth_serve, CHAL_WALLY));
  1190. ne_add_server_auth(sess, NE_AUTH_ALL, auth_cb, NULL);
  1191. CALL(any_2xx_request(sess, "/norman"));
  1192. return destroy_and_wait(sess);
  1193. }
  1194. static void fail_hdr(char *value)
  1195. {
  1196. auth_failed = 1;
  1197. }
  1198. static int serve_forgotten(ne_socket *sock, void *userdata)
  1199. {
  1200. auth_failed = 0;
  1201. got_header = fail_hdr;
  1202. want_header = "Authorization";
  1203. CALL(discard_request(sock));
  1204. if (auth_failed) {
  1205. /* Should not get initial Auth header. Eek. */
  1206. send_response(sock, NULL, 403, 1);
  1207. return 0;
  1208. }
  1209. send_response(sock, CHAL_WALLY, 401, 0);
  1210. got_header = auth_hdr;
  1211. CALL(discard_request(sock));
  1212. if (auth_failed) {
  1213. send_response(sock, NULL, 403, 1);
  1214. return 0;
  1215. }
  1216. send_response(sock, NULL, 200, 0);
  1217. ne_sock_read_timeout(sock, 5);
  1218. /* Last time; should get no Auth header. */
  1219. got_header = fail_hdr;
  1220. CALL(discard_request(sock));
  1221. send_response(sock, NULL, auth_failed ? 500 : 200, 1);
  1222. return 0;
  1223. }
  1224. static int forget(void)
  1225. {
  1226. ne_session *sess;
  1227. CALL(make_session(&sess, serve_forgotten, NULL));
  1228. ne_set_server_auth(sess, auth_cb, NULL);
  1229. CALL(any_2xx_request(sess, "/norman"));
  1230. ne_forget_auth(sess);
  1231. CALL(any_2xx_request(sess, "/norman"));
  1232. ne_session_destroy(sess);
  1233. return await_server();
  1234. }
  1235. static int serve_basic_scope_checker(ne_socket *sock, void *userdata)
  1236. {
  1237. /* --- GET /fish/0.txt -- first request */
  1238. digest_hdr = NULL;
  1239. got_header = dup_header;
  1240. want_header = "Authorization";
  1241. CALL(discard_request(sock));
  1242. if (digest_hdr) {
  1243. t_context("Got WWW-Auth header on initial request");
  1244. return error_response(sock, FAIL);
  1245. }
  1246. send_response(sock, CHAL_WALLY, 401, 0);
  1247. /* Retry of GET /fish/0 - expect Basic creds */
  1248. auth_failed = 1;
  1249. got_header = auth_hdr;
  1250. CALL(discard_request(sock));
  1251. if (auth_failed) {
  1252. t_context("bad Basic Auth on first request");
  1253. return error_response(sock, FAIL);
  1254. }
  1255. send_response(sock, CHAL_WALLY, 200, 0);
  1256. /* --- GET /not/inside -- second request */
  1257. got_header = dup_header;
  1258. CALL(discard_request(sock));
  1259. if (digest_hdr) {
  1260. t_context("Basic auth sent outside of credentials scope");
  1261. return error_response(sock, FAIL);
  1262. }
  1263. send_response(sock, CHAL_WALLY, 200, 0);
  1264. /* --- GET /fish/1 -- third request */
  1265. got_header = auth_hdr;
  1266. CALL(discard_request(sock));
  1267. send_response(sock, NULL, auth_failed?500:200, 1);
  1268. return 0;
  1269. }
  1270. /* Check that Basic auth follows the RFC7617 rules around scope. */
  1271. static int basic_scope(void)
  1272. {
  1273. ne_session *sess;
  1274. CALL(make_session(&sess, serve_basic_scope_checker, NULL));
  1275. ne_set_server_auth(sess, auth_cb, NULL);
  1276. CALL(any_2xx_request(sess, "/fish/0.txt")); /* must use auth */
  1277. CALL(any_2xx_request(sess, "/not/inside")); /* must NOT use auth credentials */
  1278. CALL(any_2xx_request(sess, "/fish/1")); /* must use auth credentials */
  1279. return destroy_and_wait(sess);
  1280. }
  1281. /* Test for scope of "*" */
  1282. static int serve_star_scope_checker(ne_socket *sock, void *userdata)
  1283. {
  1284. /* --- OPTIONS * -- first request */
  1285. digest_hdr = NULL;
  1286. got_header = dup_header;
  1287. want_header = "Authorization";
  1288. CALL(discard_request(sock));
  1289. if (digest_hdr) {
  1290. t_context("Got WWW-Auth header on initial request");
  1291. return error_response(sock, FAIL);
  1292. }
  1293. send_response(sock, CHAL_WALLY, 401, 0);
  1294. /* Retry of OPTIONS * - expect Basic creds */
  1295. auth_failed = 1;
  1296. got_header = auth_hdr;
  1297. CALL(discard_request(sock));
  1298. if (auth_failed) {
  1299. t_context("No Basic Auth in OPTIONS request");
  1300. return error_response(sock, FAIL);
  1301. }
  1302. send_response(sock, CHAL_WALLY, 200, 0);
  1303. return 0;
  1304. }
  1305. /* Test for the scope of "*". */
  1306. static int star_scope(void)
  1307. {
  1308. ne_session *sess;
  1309. CALL(make_session(&sess, serve_star_scope_checker, NULL));
  1310. ne_set_server_auth(sess, auth_cb, NULL);
  1311. CALL(any_2xx_request_method(sess, "OPTIONS", "*")); /* must use auth */
  1312. return destroy_and_wait(sess);
  1313. }
  1314. /* proxy auth, proxy AND origin */
  1315. ne_test tests[] = {
  1316. T(init),
  1317. T(basic),
  1318. T(retries),
  1319. T(forget_regress),
  1320. T(tunnel_regress),
  1321. T(negotiate_regress),
  1322. T(digest),
  1323. T(digest_sha256),
  1324. T(digest_sha512_256),
  1325. T(digest_failures),
  1326. T(digest_username_star),
  1327. T(fail_challenge),
  1328. T(multi_handler),
  1329. T(multi_rfc7616),
  1330. T(multi_provider),
  1331. T(domains),
  1332. T(defaults),
  1333. T(CVE_2008_3746),
  1334. T(forget),
  1335. T(basic_scope),
  1336. T(star_scope),
  1337. T(NULL)
  1338. };