auth.c 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  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. { "Ałâddín", "UTF-8''A%c5%82%c3%a2dd%c3%adn" },
  827. { "Jäsøn Doe", "UTF-8''J%c3%a4s%c3%b8n%20Doe" },
  828. { "foo bar", "UTF-8''foo%20bar"},
  829. { "foo\"bar", "UTF-8''foo%22bar" },
  830. { NULL, NULL }
  831. };
  832. unsigned n;
  833. int ret = OK;
  834. for (n = 0; ret == OK && ts[n].username_raw; n++) {
  835. struct digest_parms parms = {
  836. "WallyWorld", "nonce-sha5-nonce", "opaque-string",
  837. NULL, ALG_MD5, PARM_RFC2617|PARM_UHFALSE|PARM_ALTUSER, 1, 0, fail_not };
  838. alt_username = ts[n].username_raw;
  839. alt_username_star = ts[n].username_star;
  840. ret = test_digest(&parms);
  841. }
  842. alt_username = NULL;
  843. alt_username_star = NULL;
  844. return ret;
  845. }
  846. static int digest_failures(void)
  847. {
  848. struct digest_parms parms;
  849. static const struct {
  850. enum digest_failure mode;
  851. const char *message;
  852. } fails[] = {
  853. { fail_ai_bad_nc, "nonce count mismatch" },
  854. { fail_ai_bad_nc_syntax, "could not parse nonce count" },
  855. { fail_ai_bad_digest, "digest mismatch" },
  856. { fail_ai_bad_cnonce, "client nonce mismatch" },
  857. { fail_ai_omit_nc, "missing parameters" },
  858. { fail_ai_omit_digest, "missing parameters" },
  859. { fail_ai_omit_cnonce, "missing parameters" },
  860. { fail_bogus_alg, "unknown algorithm" },
  861. { fail_req0_stale, "initial Digest challenge was stale" },
  862. { fail_req0_2069_stale, "initial Digest challenge was stale" },
  863. { fail_2069_weak, "legacy Digest challenge not supported" },
  864. { fail_not, NULL }
  865. };
  866. unsigned n;
  867. memset(&parms, 0, sizeof parms);
  868. parms.realm = "WallyWorld";
  869. parms.nonce = "random-invented-string";
  870. parms.opaque = NULL;
  871. parms.flags = PARM_AINFO;
  872. parms.num_requests = 1;
  873. for (n = 0; fails[n].message; n++) {
  874. ne_session *sess;
  875. int ret;
  876. parms.failure = fails[n].mode;
  877. if (parms.failure == fail_req0_2069_stale || parms.failure == fail_2069_weak)
  878. parms.flags &= ~PARM_RFC2617;
  879. else
  880. parms.flags |= PARM_RFC2617;
  881. NE_DEBUG(NE_DBG_HTTP, ">>> New Digest failure test, "
  882. "expecting failure '%s'\n", fails[n].message);
  883. CALL(session_server(&sess, serve_digest, &parms));
  884. ne_set_server_auth(sess, auth_cb, NULL);
  885. ret = any_2xx_request(sess, "/fish");
  886. ONV(ret == NE_OK,
  887. ("request success (iter %u); expecting error '%s'",
  888. n, fails[n].message));
  889. ONV(strstr(ne_get_error(sess), fails[n].message) == NULL,
  890. ("request fails with error '%s'; expecting '%s'",
  891. ne_get_error(sess), fails[n].message));
  892. ne_session_destroy(sess);
  893. if (fails[n].mode == fail_bogus_alg
  894. || fails[n].mode == fail_req0_stale
  895. || fails[n].mode == fail_2069_weak) {
  896. reap_server();
  897. } else {
  898. CALL(await_server());
  899. }
  900. }
  901. return OK;
  902. }
  903. static int fail_cb(void *userdata, const char *realm, int tries,
  904. char *un, char *pw)
  905. {
  906. ne_buffer *buf = userdata;
  907. char str[64];
  908. if (strcmp(realm, "colonic") == 0 && ne_buffer_size(buf) == 0) {
  909. ne_strnzcpy(un, "user:name", NE_ABUFSIZ);
  910. ne_strnzcpy(pw, "passwerd", NE_ABUFSIZ);
  911. return 0;
  912. }
  913. ne_snprintf(str, sizeof str, "<%s, %d>", realm, tries);
  914. ne_buffer_zappend(buf, str);
  915. return -1;
  916. }
  917. static int fail_challenge(void)
  918. {
  919. static const struct {
  920. const char *resp, *error, *challs;
  921. } ts[] = {
  922. /* only possible Basic parse failure. */
  923. { "Basic", "missing realm in Basic challenge" },
  924. { "Basic realm=\"colonic\"", "username containing colon" },
  925. /* Digest parameter invalid/omitted failure cases: */
  926. { "Digest algorithm=MD5, qop=auth, nonce=\"foo\"",
  927. "missing parameter in Digest challenge" },
  928. { "Digest algorithm=MD5, qop=auth, realm=\"foo\"",
  929. "missing parameter in Digest challenge" },
  930. { "Digest algorithm=ZEBEDEE-GOES-BOING, qop=auth, realm=\"foo\"",
  931. "unknown algorithm in Digest challenge" },
  932. { "Digest algorithm=MD5-sess, realm=\"foo\"",
  933. "incompatible algorithm in Digest challenge" },
  934. { "Digest algorithm=MD5, qop=auth, nonce=\"foo\", realm=\"foo\", "
  935. "domain=\"http://[::1/\"", "could not parse domain" },
  936. /* Multiple challenge failure cases: */
  937. { "Basic, Digest",
  938. "missing parameter in Digest challenge, missing realm in Basic challenge" },
  939. { "Digest realm=\"foo\", algorithm=MD5, qop=auth, nonce=\"foo\","
  940. " Basic realm=\"foo\"",
  941. "rejected Digest challenge, rejected Basic challenge" },
  942. { "WhizzBangAuth realm=\"foo\", "
  943. "Basic realm='foo'",
  944. "ignored WhizzBangAuth challenge, rejected Basic challenge" },
  945. { "", "could not parse challenge" },
  946. /* neon 0.26.x regression in "attempt" handling. */
  947. { "Basic realm=\"foo\", "
  948. "Digest realm=\"bar\", algorithm=MD5, qop=auth, nonce=\"foo\"",
  949. "rejected Digest challenge, rejected Basic challenge"
  950. , "<bar, 0><foo, 1>" /* Digest challenge first, Basic second. */
  951. }
  952. };
  953. unsigned n;
  954. for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
  955. char resp[512];
  956. ne_session *sess;
  957. int ret;
  958. ne_buffer *buf = ne_buffer_create();
  959. ne_snprintf(resp, sizeof resp,
  960. "HTTP/1.1 401 Auth Denied\r\n"
  961. "WWW-Authenticate: %s\r\n"
  962. "Content-Length: 0\r\n" "\r\n",
  963. ts[n].resp);
  964. CALL(multi_session_server(&sess, "http", "localhost",
  965. 2, single_serve_string, resp));
  966. ne_set_server_auth(sess, fail_cb, buf);
  967. ret = any_2xx_request(sess, "/fish");
  968. ONV(ret == NE_OK,
  969. ("request success (iter %u); expecting error '%s'",
  970. n, ts[n].error));
  971. ONV(strstr(ne_get_error(sess), ts[n].error) == NULL,
  972. ("request fails with error '%s'; expecting '%s'",
  973. ne_get_error(sess), ts[n].error));
  974. if (ts[n].challs) {
  975. ONCMP(ts[n].challs, buf->data, "challenge callback",
  976. "invocation order");
  977. }
  978. ne_session_destroy(sess);
  979. ne_buffer_destroy(buf);
  980. reap_server();
  981. }
  982. return OK;
  983. }
  984. struct multi_context {
  985. int id;
  986. ne_buffer *buf;
  987. };
  988. static int multi_cb(void *userdata, const char *realm, int tries,
  989. char *un, char *pw)
  990. {
  991. struct multi_context *ctx = userdata;
  992. ne_buffer_snprintf(ctx->buf, 128, "[id=%d, realm=%s, tries=%d]",
  993. ctx->id, realm, tries);
  994. return -1;
  995. }
  996. static int multi_handler(void)
  997. {
  998. ne_session *sess;
  999. struct multi_context c[2];
  1000. unsigned n;
  1001. ne_buffer *buf = ne_buffer_create();
  1002. CALL(make_session(&sess, single_serve_string,
  1003. "HTTP/1.1 401 Auth Denied\r\n"
  1004. "WWW-Authenticate: Basic realm='fish',"
  1005. " Digest realm='food', algorithm=MD5, qop=auth, nonce=gaga\r\n"
  1006. "Content-Length: 0\r\n" "\r\n"));
  1007. for (n = 0; n < 2; n++) {
  1008. c[n].buf = buf;
  1009. c[n].id = n + 1;
  1010. }
  1011. ne_add_server_auth(sess, NE_AUTH_BASIC, multi_cb, &c[0]);
  1012. ne_add_server_auth(sess, NE_AUTH_DIGEST, multi_cb, &c[1]);
  1013. any_request(sess, "/fish");
  1014. ONCMP("[id=2, realm=food, tries=0]"
  1015. "[id=1, realm=fish, tries=0]", buf->data,
  1016. "multiple callback", "invocation order");
  1017. ne_buffer_destroy(buf);
  1018. return destroy_and_wait(sess);
  1019. }
  1020. static int multi_rfc7616(void)
  1021. {
  1022. ne_session *sess;
  1023. struct multi_context c[2];
  1024. unsigned n;
  1025. ne_buffer *buf, *exp;
  1026. buf = ne_buffer_create();
  1027. CALL(make_session(&sess, single_serve_string,
  1028. "HTTP/1.1 401 Auth Denied\r\n"
  1029. "WWW-Authenticate: "
  1030. "Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1031. "Basic realm='basic-realm', "
  1032. "Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
  1033. "Digest realm='sha256-realm', algorithm=SHA-256, 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. exp = ne_buffer_create();
  1043. n = 0;
  1044. if (has_sha512_256)
  1045. ne_buffer_snprintf(exp, 100, "[id=2, realm=sha512-realm, tries=%u]", n++);
  1046. if (has_sha256)
  1047. ne_buffer_snprintf(exp, 100, "[id=2, realm=sha256-realm, tries=%u]", n++);
  1048. ne_buffer_snprintf(exp, 100,
  1049. "[id=2, realm=md5-realm, tries=%u]"
  1050. "[id=1, realm=basic-realm, tries=0]", n);
  1051. ONV(strcmp(exp->data, buf->data),
  1052. ("unexpected callback ordering.\n"
  1053. "expected: %s\n"
  1054. "actual: %s\n",
  1055. exp->data, buf->data));
  1056. ne_buffer_destroy(buf);
  1057. ne_buffer_destroy(exp);
  1058. return destroy_and_wait(sess);
  1059. }
  1060. static int multi_provider_cb(void *userdata, int attempt,
  1061. unsigned protocol, const char *realm,
  1062. char *un, char *pw, size_t buflen)
  1063. {
  1064. ne_buffer *buf = userdata;
  1065. const char *ctx;
  1066. if (buflen == NE_ABUFSIZ) {
  1067. NE_DEBUG(NE_DBG_HTTPAUTH, "auth: FAILED for short buffer length.\n");
  1068. return -1;
  1069. }
  1070. if ((protocol & NE_AUTH_PROXY) == NE_AUTH_PROXY) {
  1071. ctx = "proxy";
  1072. protocol ^= NE_AUTH_PROXY;
  1073. }
  1074. else {
  1075. ctx = "server";
  1076. }
  1077. ne_buffer_snprintf(buf, 128, "[%s: proto=%u, realm=%s, attempt=%d]",
  1078. ctx, protocol, realm, attempt);
  1079. ne_strnzcpy(un, "foo", buflen);
  1080. ne_strnzcpy(pw, "bar", buflen);
  1081. return protocol == NE_AUTH_BASIC ? 0 : -1;
  1082. }
  1083. static int serve_provider(ne_socket *s, void *userdata)
  1084. {
  1085. CALL(serve_response(s,
  1086. "HTTP/1.1 407 Proxy Auth Plz\r\n"
  1087. "Proxy-Authenticate: Basic realm='proxy-realm'\r\n"
  1088. "Content-Length: 0\r\n" "\r\n"));
  1089. CALL(serve_response(s,
  1090. "HTTP/1.1 401 Auth Denied\r\n"
  1091. "WWW-Authenticate: "
  1092. " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1093. " Basic realm='basic-realm', "
  1094. " Digest realm='md5-realm', algorithm=MD5, qop=auth, nonce=gaga, "
  1095. " Digest realm='sha256-realm', algorithm=SHA-256, qop=auth, nonce=gaga\r\n"
  1096. "Content-Length: 0\r\n" "\r\n"));
  1097. CALL(serve_response(s,
  1098. "HTTP/1.1 401 Auth Denied\r\n"
  1099. "WWW-Authenticate: "
  1100. " Digest realm='sha512-realm', algorithm=SHA-512-256, qop=auth, nonce=gaga, "
  1101. " Basic realm='basic-realm'\r\n"
  1102. "Content-Length: 0\r\n" "\r\n"));
  1103. return serve_response(s,
  1104. "HTTP/1.1 200 OK\r\n"
  1105. "Content-Length: 0\r\n" "\r\n");
  1106. }
  1107. static int multi_provider(void)
  1108. {
  1109. ne_session *sess;
  1110. ne_buffer *buf = ne_buffer_create(), *exp;
  1111. CALL(make_session(&sess, serve_provider, NULL));
  1112. ne_add_auth(sess, NE_AUTH_DIGEST|NE_AUTH_BASIC, multi_provider_cb, buf);
  1113. ONREQ(any_request(sess, "/fish"));
  1114. exp = ne_buffer_create();
  1115. ne_buffer_snprintf(exp, 100,
  1116. "[proxy: proto=%u, realm=proxy-realm, attempt=0]",
  1117. NE_AUTH_BASIC);
  1118. if (has_sha512_256)
  1119. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=0]",
  1120. NE_AUTH_DIGEST);
  1121. if (has_sha256)
  1122. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha256-realm, attempt=0]",
  1123. NE_AUTH_DIGEST);
  1124. ne_buffer_snprintf(exp, 100,
  1125. "[server: proto=%u, realm=md5-realm, attempt=0]"
  1126. "[server: proto=%u, realm=basic-realm, attempt=0]",
  1127. NE_AUTH_DIGEST, NE_AUTH_BASIC);
  1128. if (has_sha512_256)
  1129. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=sha512-realm, attempt=1]",
  1130. NE_AUTH_DIGEST);
  1131. ne_buffer_snprintf(exp, 100, "[server: proto=%u, realm=basic-realm, attempt=1]",
  1132. NE_AUTH_BASIC);
  1133. ONV(strcmp(exp->data, buf->data),
  1134. ("unexpected callback ordering.\n"
  1135. "expected: %s\n"
  1136. "actual: %s\n",
  1137. exp->data, buf->data));
  1138. ne_buffer_destroy(buf);
  1139. ne_buffer_destroy(exp);
  1140. return destroy_and_wait(sess);
  1141. }
  1142. static int domains(void)
  1143. {
  1144. ne_session *sess;
  1145. struct digest_parms parms;
  1146. memset(&parms, 0, sizeof parms);
  1147. parms.realm = "WallyWorld";
  1148. parms.flags = PARM_RFC2617;
  1149. parms.nonce = "agoog";
  1150. parms.domain = "http://localhost:4242/fish/ https://example.com /agaor /other";
  1151. parms.num_requests = 6;
  1152. CALL(proxied_session_server(&sess, "http", "localhost", 4242,
  1153. serve_digest, &parms));
  1154. ne_set_server_auth(sess, auth_cb, NULL);
  1155. CALL(any_2xx_request(sess, "/fish/0"));
  1156. CALL(any_2xx_request(sess, "/outside"));
  1157. CALL(any_2xx_request(sess, "/others"));
  1158. CALL(any_2xx_request(sess, "/fish"));
  1159. CALL(any_2xx_request(sess, "/fish/2"));
  1160. CALL(any_2xx_request(sess, "*"));
  1161. return destroy_and_wait(sess);
  1162. }
  1163. /* This segfaulted with 0.28.0 through 0.28.2 inclusive. */
  1164. static int CVE_2008_3746(void)
  1165. {
  1166. ne_session *sess;
  1167. struct digest_parms parms;
  1168. memset(&parms, 0, sizeof parms);
  1169. parms.realm = "WallyWorld";
  1170. parms.flags = PARM_RFC2617;
  1171. parms.nonce = "agoog";
  1172. parms.domain = "foo";
  1173. parms.num_requests = 1;
  1174. CALL(proxied_session_server(&sess, "http", "www.example.com", 80,
  1175. serve_digest, &parms));
  1176. ne_set_server_auth(sess, auth_cb, NULL);
  1177. any_2xx_request(sess, "/fish/0");
  1178. return destroy_and_wait(sess);
  1179. }
  1180. static int defaults(void)
  1181. {
  1182. ne_session *sess;
  1183. CALL(make_session(&sess, auth_serve, CHAL_WALLY));
  1184. ne_add_server_auth(sess, NE_AUTH_DEFAULT, auth_cb, NULL);
  1185. CALL(any_2xx_request(sess, "/norman"));
  1186. ne_session_destroy(sess);
  1187. CALL(await_server());
  1188. CALL(make_session(&sess, auth_serve, CHAL_WALLY));
  1189. ne_add_server_auth(sess, NE_AUTH_ALL, auth_cb, NULL);
  1190. CALL(any_2xx_request(sess, "/norman"));
  1191. return destroy_and_wait(sess);
  1192. }
  1193. static void fail_hdr(char *value)
  1194. {
  1195. auth_failed = 1;
  1196. }
  1197. static int serve_forgotten(ne_socket *sock, void *userdata)
  1198. {
  1199. auth_failed = 0;
  1200. got_header = fail_hdr;
  1201. want_header = "Authorization";
  1202. CALL(discard_request(sock));
  1203. if (auth_failed) {
  1204. /* Should not get initial Auth header. Eek. */
  1205. send_response(sock, NULL, 403, 1);
  1206. return 0;
  1207. }
  1208. send_response(sock, CHAL_WALLY, 401, 0);
  1209. got_header = auth_hdr;
  1210. CALL(discard_request(sock));
  1211. if (auth_failed) {
  1212. send_response(sock, NULL, 403, 1);
  1213. return 0;
  1214. }
  1215. send_response(sock, NULL, 200, 0);
  1216. ne_sock_read_timeout(sock, 5);
  1217. /* Last time; should get no Auth header. */
  1218. got_header = fail_hdr;
  1219. CALL(discard_request(sock));
  1220. send_response(sock, NULL, auth_failed ? 500 : 200, 1);
  1221. return 0;
  1222. }
  1223. static int forget(void)
  1224. {
  1225. ne_session *sess;
  1226. CALL(make_session(&sess, serve_forgotten, NULL));
  1227. ne_set_server_auth(sess, auth_cb, NULL);
  1228. CALL(any_2xx_request(sess, "/norman"));
  1229. ne_forget_auth(sess);
  1230. CALL(any_2xx_request(sess, "/norman"));
  1231. ne_session_destroy(sess);
  1232. return await_server();
  1233. }
  1234. static int serve_basic_scope_checker(ne_socket *sock, void *userdata)
  1235. {
  1236. /* --- GET /fish/0.txt -- first request */
  1237. digest_hdr = NULL;
  1238. got_header = dup_header;
  1239. want_header = "Authorization";
  1240. CALL(discard_request(sock));
  1241. if (digest_hdr) {
  1242. t_context("Got WWW-Auth header on initial request");
  1243. return error_response(sock, FAIL);
  1244. }
  1245. send_response(sock, CHAL_WALLY, 401, 0);
  1246. /* Retry of GET /fish/0 - expect Basic creds */
  1247. auth_failed = 1;
  1248. got_header = auth_hdr;
  1249. CALL(discard_request(sock));
  1250. if (auth_failed) {
  1251. t_context("bad Basic Auth on first request");
  1252. return error_response(sock, FAIL);
  1253. }
  1254. send_response(sock, CHAL_WALLY, 200, 0);
  1255. /* --- GET /not/inside -- second request */
  1256. got_header = dup_header;
  1257. CALL(discard_request(sock));
  1258. if (digest_hdr) {
  1259. t_context("Basic auth sent outside of credentials scope");
  1260. return error_response(sock, FAIL);
  1261. }
  1262. send_response(sock, CHAL_WALLY, 200, 0);
  1263. /* --- GET /fish/1 -- third request */
  1264. got_header = auth_hdr;
  1265. CALL(discard_request(sock));
  1266. send_response(sock, NULL, auth_failed?500:200, 1);
  1267. return 0;
  1268. }
  1269. /* Check that Basic auth follows the RFC7617 rules around scope. */
  1270. static int basic_scope(void)
  1271. {
  1272. ne_session *sess;
  1273. CALL(make_session(&sess, serve_basic_scope_checker, NULL));
  1274. ne_set_server_auth(sess, auth_cb, NULL);
  1275. CALL(any_2xx_request(sess, "/fish/0.txt")); /* must use auth */
  1276. CALL(any_2xx_request(sess, "/not/inside")); /* must NOT use auth credentials */
  1277. CALL(any_2xx_request(sess, "/fish/1")); /* must use auth credentials */
  1278. return destroy_and_wait(sess);
  1279. }
  1280. /* Test for scope of "*" */
  1281. static int serve_star_scope_checker(ne_socket *sock, void *userdata)
  1282. {
  1283. /* --- OPTIONS * -- first request */
  1284. digest_hdr = NULL;
  1285. got_header = dup_header;
  1286. want_header = "Authorization";
  1287. CALL(discard_request(sock));
  1288. if (digest_hdr) {
  1289. t_context("Got WWW-Auth header on initial request");
  1290. return error_response(sock, FAIL);
  1291. }
  1292. send_response(sock, CHAL_WALLY, 401, 0);
  1293. /* Retry of OPTIONS * - expect Basic creds */
  1294. auth_failed = 1;
  1295. got_header = auth_hdr;
  1296. CALL(discard_request(sock));
  1297. if (auth_failed) {
  1298. t_context("No Basic Auth in OPTIONS request");
  1299. return error_response(sock, FAIL);
  1300. }
  1301. send_response(sock, CHAL_WALLY, 200, 0);
  1302. return 0;
  1303. }
  1304. /* Test for the scope of "*". */
  1305. static int star_scope(void)
  1306. {
  1307. ne_session *sess;
  1308. CALL(make_session(&sess, serve_star_scope_checker, NULL));
  1309. ne_set_server_auth(sess, auth_cb, NULL);
  1310. CALL(any_2xx_request_method(sess, "OPTIONS", "*")); /* must use auth */
  1311. return destroy_and_wait(sess);
  1312. }
  1313. /* proxy auth, proxy AND origin */
  1314. ne_test tests[] = {
  1315. T(init),
  1316. T(basic),
  1317. T(retries),
  1318. T(forget_regress),
  1319. T(tunnel_regress),
  1320. T(negotiate_regress),
  1321. T(digest),
  1322. T(digest_sha256),
  1323. T(digest_sha512_256),
  1324. T(digest_failures),
  1325. T(digest_username_star),
  1326. T(fail_challenge),
  1327. T(multi_handler),
  1328. T(multi_rfc7616),
  1329. T(multi_provider),
  1330. T(domains),
  1331. T(defaults),
  1332. T(CVE_2008_3746),
  1333. T(forget),
  1334. T(basic_scope),
  1335. T(star_scope),
  1336. T(NULL)
  1337. };