auth.c 51 KB

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