cookies.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ndb.h>
  5. #include <fcall.h>
  6. #include <thread.h>
  7. #include <9p.h>
  8. #include <ctype.h>
  9. #include "dat.h"
  10. #include "fns.h"
  11. int cookiedebug;
  12. typedef struct Cookie Cookie;
  13. typedef struct Jar Jar;
  14. struct Cookie
  15. {
  16. /* external info */
  17. char* name;
  18. char* value;
  19. char* dom; /* starts with . */
  20. char* path;
  21. char* version;
  22. char* comment; /* optional, may be nil */
  23. uint expire; /* time of expiration: ~0 means when webcookies dies */
  24. int secure;
  25. int explicitdom; /* dom was explicitly set */
  26. int explicitpath; /* path was explicitly set */
  27. int netscapestyle;
  28. /* internal info */
  29. int deleted;
  30. int mark;
  31. int ondisk;
  32. };
  33. struct Jar
  34. {
  35. Cookie *c;
  36. int nc;
  37. int mc;
  38. Qid qid;
  39. int dirty;
  40. char *file;
  41. char *lockfile;
  42. };
  43. struct {
  44. char *s;
  45. int offset;
  46. int ishttp;
  47. } stab[] = {
  48. "domain", offsetof(Cookie, dom), 1,
  49. "path", offsetof(Cookie, path), 1,
  50. "name", offsetof(Cookie, name), 0,
  51. "value", offsetof(Cookie, value), 0,
  52. "comment", offsetof(Cookie, comment), 1,
  53. "version", offsetof(Cookie, version), 1,
  54. };
  55. struct {
  56. char *s;
  57. int offset;
  58. } itab[] = {
  59. "expire", offsetof(Cookie, expire),
  60. "secure", offsetof(Cookie, secure),
  61. "explicitdomain", offsetof(Cookie, explicitdom),
  62. "explicitpath", offsetof(Cookie, explicitpath),
  63. "netscapestyle", offsetof(Cookie, netscapestyle),
  64. };
  65. #pragma varargck type "J" Jar*
  66. #pragma varargck type "K" Cookie*
  67. /* HTTP format */
  68. static int
  69. jarfmt(Fmt *fp)
  70. {
  71. int i;
  72. Jar *jar;
  73. jar = va_arg(fp->args, Jar*);
  74. if(jar == nil || jar->nc == 0)
  75. return 0;
  76. fmtstrcpy(fp, "Cookie: ");
  77. if(jar->c[0].version)
  78. fmtprint(fp, "$Version=%s; ", jar->c[0].version);
  79. for(i=0; i<jar->nc; i++)
  80. fmtprint(fp, "%s%s=%s", i ? "; ": "", jar->c[i].name, jar->c[i].value);
  81. fmtstrcpy(fp, "\r\n");
  82. return 0;
  83. }
  84. /* individual cookie */
  85. static int
  86. cookiefmt(Fmt *fp)
  87. {
  88. int j, k, first;
  89. char *t;
  90. Cookie *c;
  91. c = va_arg(fp->args, Cookie*);
  92. first = 1;
  93. for(j=0; j<nelem(stab); j++){
  94. t = *(char**)((ulong)c+stab[j].offset);
  95. if(t == nil)
  96. continue;
  97. if(first)
  98. first = 0;
  99. else
  100. fmtstrcpy(fp, " ");
  101. fmtprint(fp, "%s=%q", stab[j].s, t);
  102. }
  103. for(j=0; j<nelem(itab); j++){
  104. k = *(int*)((ulong)c+itab[j].offset);
  105. if(k == 0)
  106. continue;
  107. if(first)
  108. first = 0;
  109. else
  110. fmtstrcpy(fp, " ");
  111. fmtprint(fp, "%s=%ud", itab[j].s, k);
  112. }
  113. return 0;
  114. }
  115. /*
  116. * sort cookies:
  117. * - alpha by name
  118. * - alpha by domain
  119. * - longer paths first, then alpha by path (RFC2109 4.3.4)
  120. */
  121. static int
  122. cookiecmp(Cookie *a, Cookie *b)
  123. {
  124. int i;
  125. if((i = strcmp(a->name, b->name)) != 0)
  126. return i;
  127. if((i = cistrcmp(a->dom, b->dom)) != 0)
  128. return i;
  129. if((i = strlen(b->path) - strlen(a->path)) != 0)
  130. return i;
  131. if((i = strcmp(a->path, b->path)) != 0)
  132. return i;
  133. return 0;
  134. }
  135. static int
  136. exactcookiecmp(Cookie *a, Cookie *b)
  137. {
  138. int i;
  139. if((i = cookiecmp(a, b)) != 0)
  140. return i;
  141. if((i = strcmp(a->value, b->value)) != 0)
  142. return i;
  143. if(a->version || b->version){
  144. if(!a->version)
  145. return -1;
  146. if(!b->version)
  147. return 1;
  148. if((i = strcmp(a->version, b->version)) != 0)
  149. return i;
  150. }
  151. if(a->comment || b->comment){
  152. if(!a->comment)
  153. return -1;
  154. if(!b->comment)
  155. return 1;
  156. if((i = strcmp(a->comment, b->comment)) != 0)
  157. return i;
  158. }
  159. if((i = b->expire - a->expire) != 0)
  160. return i;
  161. if((i = b->secure - a->secure) != 0)
  162. return i;
  163. if((i = b->explicitdom - a->explicitdom) != 0)
  164. return i;
  165. if((i = b->explicitpath - a->explicitpath) != 0)
  166. return i;
  167. if((i = b->netscapestyle - a->netscapestyle) != 0)
  168. return i;
  169. return 0;
  170. }
  171. static void
  172. freecookie(Cookie *c)
  173. {
  174. int i;
  175. for(i=0; i<nelem(stab); i++)
  176. free(*(char**)((ulong)c+stab[i].offset));
  177. }
  178. static void
  179. copycookie(Cookie *c)
  180. {
  181. int i;
  182. char **ps;
  183. for(i=0; i<nelem(stab); i++){
  184. ps = (char**)((ulong)c+stab[i].offset);
  185. if(*ps)
  186. *ps = estrdup9p(*ps);
  187. }
  188. }
  189. static void
  190. delcookie(Jar *j, Cookie *c)
  191. {
  192. int i;
  193. j->dirty = 1;
  194. i = c - j->c;
  195. if(i < 0 || i >= j->nc)
  196. abort();
  197. c->deleted = 1;
  198. }
  199. static void
  200. addcookie(Jar *j, Cookie *c)
  201. {
  202. int i;
  203. if(!c->name || !c->value || !c->path || !c->dom){
  204. fprint(2, "not adding incomplete cookie\n");
  205. return;
  206. }
  207. if(cookiedebug)
  208. fprint(2, "add %K\n", c);
  209. for(i=0; i<j->nc; i++)
  210. if(cookiecmp(&j->c[i], c) == 0){
  211. if(cookiedebug)
  212. fprint(2, "cookie %K matches %K\n", &j->c[i], c);
  213. if(exactcookiecmp(&j->c[i], c) == 0){
  214. if(cookiedebug)
  215. fprint(2, "\texactly\n");
  216. j->c[i].mark = 0;
  217. return;
  218. }
  219. delcookie(j, &j->c[i]);
  220. }
  221. j->dirty = 1;
  222. if(j->nc == j->mc){
  223. j->mc += 16;
  224. j->c = erealloc9p(j->c, j->mc*sizeof(Cookie));
  225. }
  226. j->c[j->nc] = *c;
  227. copycookie(&j->c[j->nc]);
  228. j->nc++;
  229. }
  230. static void
  231. purgejar(Jar *j)
  232. {
  233. int i;
  234. for(i=j->nc-1; i>=0; i--){
  235. if(!j->c[i].deleted)
  236. continue;
  237. freecookie(&j->c[i]);
  238. --j->nc;
  239. j->c[i] = j->c[j->nc];
  240. }
  241. }
  242. static void
  243. addtojar(Jar *jar, char *line, int ondisk)
  244. {
  245. Cookie c;
  246. int i, j, nf, *pint;
  247. char *f[20], *attr, *val, **pstr;
  248. memset(&c, 0, sizeof c);
  249. c.expire = ~0;
  250. c.ondisk = ondisk;
  251. nf = tokenize(line, f, nelem(f));
  252. for(i=0; i<nf; i++){
  253. attr = f[i];
  254. if((val = strchr(attr, '=')) != nil)
  255. *val++ = '\0';
  256. else
  257. val = "";
  258. /* string attributes */
  259. for(j=0; j<nelem(stab); j++){
  260. if(strcmp(stab[j].s, attr) == 0){
  261. pstr = (char**)((ulong)&c+stab[j].offset);
  262. *pstr = val;
  263. }
  264. }
  265. /* integer attributes */
  266. for(j=0; j<nelem(itab); j++){
  267. if(strcmp(itab[j].s, attr) == 0){
  268. pint = (int*)((ulong)&c+itab[j].offset);
  269. if(val[0]=='\0')
  270. *pint = 1;
  271. else
  272. *pint = strtoul(val, 0, 0);
  273. }
  274. }
  275. }
  276. if(c.name==nil || c.value==nil || c.dom==nil || c.path==nil){
  277. if(cookiedebug)
  278. fprint(2, "ignoring fractional cookie %K\n", &c);
  279. return;
  280. }
  281. addcookie(jar, &c);
  282. }
  283. static Jar*
  284. newjar(void)
  285. {
  286. Jar *jar;
  287. jar = emalloc9p(sizeof(Jar));
  288. return jar;
  289. }
  290. static int
  291. expirejar(Jar *jar, int exiting)
  292. {
  293. int i, n;
  294. uint now;
  295. now = time(0);
  296. n = 0;
  297. for(i=0; i<jar->nc; i++){
  298. if(jar->c[i].expire < now || (exiting && jar->c[i].expire==~0)){
  299. delcookie(jar, &jar->c[i]);
  300. n++;
  301. }
  302. }
  303. return n;
  304. }
  305. static void
  306. dumpjar(Jar *jar, char *desc)
  307. {
  308. int i;
  309. Biobuf *b;
  310. char *s;
  311. print("%s\n", desc);
  312. print("\tin memory:\n");
  313. for(i=0; i<jar->nc; i++)
  314. print("\t%K%s%s%s\n", &jar->c[i],
  315. jar->c[i].ondisk ? " ondisk" : "",
  316. jar->c[i].deleted ? " deleted" : "",
  317. jar->c[i].mark ? " mark" : "");
  318. print("\n\ton disk:\n");
  319. if((b = Bopen(jar->file, OREAD)) == nil){
  320. print("\tno file\n");
  321. }else{
  322. while((s = Brdstr(b, '\n', 1)) != nil){
  323. print("\t%s\n", s);
  324. free(s);
  325. }
  326. Bterm(b);
  327. }
  328. print("\n");
  329. }
  330. static int
  331. syncjar(Jar *jar)
  332. {
  333. int i, fd;
  334. char *line;
  335. Dir *d;
  336. Biobuf *b;
  337. Qid q;
  338. if(jar->file==nil)
  339. return 0;
  340. memset(&q, 0, sizeof q);
  341. if((d = dirstat(jar->file)) != nil){
  342. q = d->qid;
  343. if(d->qid.path != jar->qid.path || d->qid.vers != jar->qid.vers)
  344. jar->dirty = 1;
  345. free(d);
  346. }
  347. if(jar->dirty == 0)
  348. return 0;
  349. fd = -1;
  350. for(i=0; i<50; i++){
  351. if((fd = create(jar->lockfile, OWRITE, DMEXCL|0666)) < 0){
  352. sleep(100);
  353. continue;
  354. }
  355. break;
  356. }
  357. if(fd < 0){
  358. if(cookiedebug)
  359. fprint(2, "open %s: %r", jar->lockfile);
  360. werrstr("cannot acquire jar lock: %r");
  361. return -1;
  362. }
  363. for(i=0; i<jar->nc; i++) /* mark is cleared by addcookie */
  364. jar->c[i].mark = jar->c[i].ondisk;
  365. if((b = Bopen(jar->file, OREAD)) == nil){
  366. if(cookiedebug)
  367. fprint(2, "Bopen %s: %r", jar->file);
  368. werrstr("cannot read cookie file %s: %r", jar->file);
  369. close(fd);
  370. return -1;
  371. }
  372. for(; (line = Brdstr(b, '\n', 1)) != nil; free(line)){
  373. if(*line == '#')
  374. continue;
  375. addtojar(jar, line, 1);
  376. }
  377. Bterm(b);
  378. for(i=0; i<jar->nc; i++)
  379. if(jar->c[i].mark && jar->c[i].expire != ~0)
  380. delcookie(jar, &jar->c[i]);
  381. purgejar(jar);
  382. b = Bopen(jar->file, OWRITE);
  383. if(b == nil){
  384. if(cookiedebug)
  385. fprint(2, "Bopen write %s: %r", jar->file);
  386. close(fd);
  387. return -1;
  388. }
  389. Bprint(b, "# webcookies cookie jar\n");
  390. Bprint(b, "# comments and non-standard fields will be lost\n");
  391. for(i=0; i<jar->nc; i++){
  392. if(jar->c[i].expire == ~0)
  393. continue;
  394. Bprint(b, "%K\n", &jar->c[i]);
  395. jar->c[i].ondisk = 1;
  396. }
  397. Bterm(b);
  398. jar->dirty = 0;
  399. close(fd);
  400. if((d = dirstat(jar->file)) != nil){
  401. jar->qid = d->qid;
  402. free(d);
  403. }
  404. return 0;
  405. }
  406. static Jar*
  407. readjar(char *file)
  408. {
  409. char *lock, *p;
  410. Jar *jar;
  411. jar = newjar();
  412. lock = emalloc9p(strlen(file)+10);
  413. strcpy(lock, file);
  414. if((p = strrchr(lock, '/')) != nil)
  415. p++;
  416. else
  417. p = lock;
  418. memmove(p+2, p, strlen(p)+1);
  419. p[0] = 'L';
  420. p[1] = '.';
  421. jar->lockfile = lock;
  422. jar->file = file;
  423. jar->dirty = 1;
  424. if(syncjar(jar) < 0){
  425. free(jar->file);
  426. free(jar->lockfile);
  427. free(jar);
  428. return nil;
  429. }
  430. return jar;
  431. }
  432. static void
  433. closejar(Jar *jar)
  434. {
  435. int i;
  436. if(jar == nil)
  437. return;
  438. expirejar(jar, 0);
  439. if(syncjar(jar) < 0)
  440. fprint(2, "warning: cannot rewrite cookie jar: %r\n");
  441. for(i=0; i<jar->nc; i++)
  442. freecookie(&jar->c[i]);
  443. free(jar->file);
  444. free(jar);
  445. }
  446. /*
  447. * Domain name matching is per RFC2109, section 2:
  448. *
  449. * Hosts names can be specified either as an IP address or a FQHN
  450. * string. Sometimes we compare one host name with another. Host A's
  451. * name domain-matches host B's if
  452. *
  453. * * both host names are IP addresses and their host name strings match
  454. * exactly; or
  455. *
  456. * * both host names are FQDN strings and their host name strings match
  457. * exactly; or
  458. *
  459. * * A is a FQDN string and has the form NB, where N is a non-empty name
  460. * string, B has the form .B', and B' is a FQDN string. (So, x.y.com
  461. * domain-matches .y.com but not y.com.)
  462. *
  463. * Note that domain-match is not a commutative operation: a.b.c.com
  464. * domain-matches .c.com, but not the reverse.
  465. *
  466. * (This does not verify that IP addresses and FQDN's are well-formed.)
  467. */
  468. static int
  469. isdomainmatch(char *name, char *pattern)
  470. {
  471. int lname, lpattern;
  472. if(cistrcmp(name, pattern)==0)
  473. return 1;
  474. if(strcmp(ipattr(name), "dom")==0 && pattern[0]=='.'){
  475. lname = strlen(name);
  476. lpattern = strlen(pattern);
  477. if(lname >= lpattern && cistrcmp(name+lname-lpattern, pattern)==0)
  478. return 1;
  479. }
  480. return 0;
  481. }
  482. /*
  483. * RFC2109 4.3.4:
  484. * - domain must match
  485. * - path in cookie must be a prefix of request path
  486. * - cookie must not have expired
  487. */
  488. static int
  489. iscookiematch(Cookie *c, char *dom, char *path, uint now)
  490. {
  491. return isdomainmatch(dom, c->dom)
  492. && strncmp(c->path, path, strlen(c->path))==0
  493. && c->expire >= now;
  494. }
  495. /*
  496. * Produce a subjar of matching cookies.
  497. * Secure cookies are only included if secure is set.
  498. */
  499. static Jar*
  500. cookiesearch(Jar *jar, char *dom, char *path, int issecure)
  501. {
  502. int i;
  503. Jar *j;
  504. uint now;
  505. if(cookiedebug)
  506. fprint(2, "cookiesearch %s %s %d\n", dom, path, issecure);
  507. now = time(0);
  508. j = newjar();
  509. for(i=0; i<jar->nc; i++){
  510. if(cookiedebug)
  511. fprint(2, "\ttry %s %s %d %s\n", jar->c[i].dom, jar->c[i].path, jar->c[i].secure, jar->c[i].name);
  512. if((issecure || !jar->c[i].secure) && iscookiematch(&jar->c[i], dom, path, now)){
  513. if(cookiedebug)
  514. fprint(2, "\tmatched\n");
  515. addcookie(j, &jar->c[i]);
  516. }
  517. }
  518. if(j->nc == 0){
  519. closejar(j);
  520. werrstr("no cookies found");
  521. return nil;
  522. }
  523. qsort(j->c, j->nc, sizeof(j->c[0]), (int(*)(const void*, const void*))cookiecmp);
  524. return j;
  525. }
  526. /*
  527. * RFC2109 4.3.2 security checks
  528. */
  529. static char*
  530. isbadcookie(Cookie *c, char *dom, char *path)
  531. {
  532. if(strncmp(c->path, path, strlen(c->path)) != 0)
  533. return "cookie path is not a prefix of the request path";
  534. if(c->explicitdom && c->dom[0] != '.')
  535. return "cookie domain doesn't start with dot";
  536. if(memchr(c->dom+1, '.', strlen(c->dom)-1-1) == nil)
  537. return "cookie domain doesn't have embedded dots";
  538. if(!isdomainmatch(dom, c->dom))
  539. return "request host does not match cookie domain";
  540. if(strcmp(ipattr(dom), "dom")==0
  541. && memchr(dom, '.', strlen(dom)-strlen(c->dom)) != nil)
  542. return "request host contains dots before cookie domain";
  543. return 0;
  544. }
  545. /*
  546. * Sunday, 25-Jan-2002 12:24:36 GMT
  547. * Sunday, 25 Jan 2002 12:24:36 GMT
  548. * Sun, 25 Jan 02 12:24:36 GMT
  549. */
  550. static int
  551. isleap(int year)
  552. {
  553. return year%4==0 && (year%100!=0 || year%400==0);
  554. }
  555. static uint
  556. strtotime(char *s)
  557. {
  558. char *os;
  559. int i;
  560. Tm tm;
  561. static int mday[2][12] = {
  562. 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  563. 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  564. };
  565. static char *wday[] = {
  566. "Sunday", "Monday", "Tuesday", "Wednesday",
  567. "Thursday", "Friday", "Saturday",
  568. };
  569. static char *mon[] = {
  570. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  571. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  572. };
  573. os = s;
  574. /* Sunday, */
  575. for(i=0; i<nelem(wday); i++){
  576. if(cistrncmp(s, wday[i], strlen(wday[i])) == 0){
  577. s += strlen(wday[i]);
  578. break;
  579. }
  580. if(cistrncmp(s, wday[i], 3) == 0){
  581. s += 3;
  582. break;
  583. }
  584. }
  585. if(i==nelem(wday)){
  586. if(cookiedebug)
  587. fprint(2, "bad wday (%s)\n", os);
  588. return -1;
  589. }
  590. if(*s++ != ',' || *s++ != ' '){
  591. if(cookiedebug)
  592. fprint(2, "bad wday separator (%s)\n", os);
  593. return -1;
  594. }
  595. /* 25- */
  596. if(!isdigit(s[0]) || !isdigit(s[1]) || (s[2]!='-' && s[2]!=' ')){
  597. if(cookiedebug)
  598. fprint(2, "bad day of month (%s)\n", os);
  599. return -1;
  600. }
  601. tm.mday = strtol(s, 0, 10);
  602. s += 3;
  603. /* Jan- */
  604. for(i=0; i<nelem(mon); i++)
  605. if(cistrncmp(s, mon[i], 3) == 0){
  606. tm.mon = i;
  607. s += 3;
  608. break;
  609. }
  610. if(i==nelem(mon)){
  611. if(cookiedebug)
  612. fprint(2, "bad month (%s)\n", os);
  613. return -1;
  614. }
  615. if(s[0] != '-' && s[0] != ' '){
  616. if(cookiedebug)
  617. fprint(2, "bad month separator (%s)\n", os);
  618. return -1;
  619. }
  620. s++;
  621. /* 2002 */
  622. if(!isdigit(s[0]) || !isdigit(s[1])){
  623. if(cookiedebug)
  624. fprint(2, "bad year (%s)\n", os);
  625. return -1;
  626. }
  627. tm.year = strtol(s, 0, 10);
  628. s += 2;
  629. if(isdigit(s[0]) && isdigit(s[1]))
  630. s += 2;
  631. else{
  632. if(tm.year <= 68)
  633. tm.year += 2000;
  634. else
  635. tm.year += 1900;
  636. }
  637. if(tm.mday==0 || tm.mday > mday[isleap(tm.year)][tm.mon]){
  638. if(cookiedebug)
  639. fprint(2, "invalid day of month (%s)\n", os);
  640. return -1;
  641. }
  642. tm.year -= 1900;
  643. if(*s++ != ' '){
  644. if(cookiedebug)
  645. fprint(2, "bad year separator (%s)\n", os);
  646. return -1;
  647. }
  648. if(!isdigit(s[0]) || !isdigit(s[1]) || s[2]!=':'
  649. || !isdigit(s[3]) || !isdigit(s[4]) || s[5]!=':'
  650. || !isdigit(s[6]) || !isdigit(s[7]) || s[8]!=' '){
  651. if(cookiedebug)
  652. fprint(2, "bad time (%s)\n", os);
  653. return -1;
  654. }
  655. tm.hour = atoi(s);
  656. tm.min = atoi(s+3);
  657. tm.sec = atoi(s+6);
  658. if(tm.hour >= 24 || tm.min >= 60 || tm.sec >= 60){
  659. if(cookiedebug)
  660. fprint(2, "invalid time (%s)\n", os);
  661. return -1;
  662. }
  663. s += 9;
  664. if(cistrcmp(s, "GMT") != 0){
  665. if(cookiedebug)
  666. fprint(2, "time zone not GMT (%s)\n", os);
  667. return -1;
  668. }
  669. strcpy(tm.zone, "GMT");
  670. tm.yday = 0;
  671. return tm2sec(&tm);
  672. }
  673. /*
  674. * skip linear whitespace. we're a bit more lenient than RFC2616 2.2.
  675. */
  676. static char*
  677. skipspace(char *s)
  678. {
  679. while(*s=='\r' || *s=='\n' || *s==' ' || *s=='\t')
  680. s++;
  681. return s;
  682. }
  683. /*
  684. * Try to identify old netscape headers.
  685. * The old headers:
  686. * - didn't allow spaces around the '='
  687. * - used an 'Expires' attribute
  688. * - had no 'Version' attribute
  689. * - had no quotes
  690. * - allowed whitespace in values
  691. * - apparently separated attr/value pairs with ';' exclusively
  692. */
  693. static int
  694. isnetscape(char *hdr)
  695. {
  696. char *s;
  697. for(s=hdr; (s=strchr(s, '=')) != nil; s++){
  698. if(isspace(s[1]) || (s > hdr && isspace(s[-1])))
  699. return 0;
  700. if(s[1]=='"')
  701. return 0;
  702. }
  703. if(cistrstr(hdr, "version="))
  704. return 0;
  705. return 1;
  706. }
  707. /*
  708. * Parse HTTP response headers, adding cookies to jar.
  709. * Overwrites the headers. May overwrite path.
  710. */
  711. static char* parsecookie(Cookie*, char*, char**, int, char*, char*);
  712. static int
  713. parsehttp(Jar *jar, char *hdr, char *dom, char *path)
  714. {
  715. static char setcookie[] = "Set-Cookie:";
  716. char *e, *p, *nextp;
  717. Cookie c;
  718. int isns, n;
  719. isns = isnetscape(hdr);
  720. n = 0;
  721. for(p=hdr; p; p=nextp){
  722. p = skipspace(p);
  723. if(*p == '\0')
  724. break;
  725. nextp = strchr(p, '\n');
  726. if(nextp != nil)
  727. *nextp++ = '\0';
  728. if(cistrncmp(p, setcookie, strlen(setcookie)) != 0)
  729. continue;
  730. if(cookiedebug)
  731. fprint(2, "%s\n", p);
  732. p = skipspace(p+strlen(setcookie));
  733. for(; *p; p=skipspace(p)){
  734. if((e = parsecookie(&c, p, &p, isns, dom, path)) != nil){
  735. if(cookiedebug)
  736. fprint(2, "parse cookie: %s\n", e);
  737. break;
  738. }
  739. if((e = isbadcookie(&c, dom, path)) != nil){
  740. if(cookiedebug)
  741. fprint(2, "reject cookie; %s\n", e);
  742. continue;
  743. }
  744. addcookie(jar, &c);
  745. n++;
  746. }
  747. }
  748. return n;
  749. }
  750. static char*
  751. skipquoted(char *s)
  752. {
  753. /*
  754. * Sec 2.2 of RFC2616 defines a "quoted-string" as:
  755. *
  756. * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
  757. * qdtext = <any TEXT except <">>
  758. * quoted-pair = "\" CHAR
  759. *
  760. * TEXT is any octet except CTLs, but including LWS;
  761. * LWS is [CR LF] 1*(SP | HT);
  762. * CHARs are ASCII octets 0-127; (NOTE: we reject 0's)
  763. * CTLs are octets 0-31 and 127;
  764. */
  765. if(*s != '"')
  766. return s;
  767. for(s++; 32 <= *s && *s < 127 && *s != '"'; s++)
  768. if(*s == '\\' && *(s+1) != '\0')
  769. s++;
  770. return s;
  771. }
  772. static char*
  773. skiptoken(char *s)
  774. {
  775. /*
  776. * Sec 2.2 of RFC2616 defines a "token" as
  777. * 1*<any CHAR except CTLs or separators>;
  778. * CHARs are ASCII octets 0-127;
  779. * CTLs are octets 0-31 and 127;
  780. * separators are "()<>@,;:\/[]?={}", double-quote, SP (32), and HT (9)
  781. */
  782. while(32 <= *s && *s < 127 && strchr("()<>@,;:[]?={}\" \t\\", *s)==nil)
  783. s++;
  784. return s;
  785. }
  786. static char*
  787. skipvalue(char *s, int isns)
  788. {
  789. char *t;
  790. /*
  791. * An RFC2109 value is an HTTP token or an HTTP quoted string.
  792. * Netscape servers ignore the spec and rely on semicolons, apparently.
  793. */
  794. if(isns){
  795. if((t = strchr(s, ';')) == nil)
  796. t = s+strlen(s);
  797. return t;
  798. }
  799. if(*s == '"')
  800. return skipquoted(s);
  801. return skiptoken(s);
  802. }
  803. /*
  804. * RMID=80b186bb64c03c65fab767f8; expires=Monday, 10-Feb-2003 04:44:39 GMT;
  805. * path=/; domain=.nytimes.com
  806. */
  807. static char*
  808. parsecookie(Cookie *c, char *p, char **e, int isns, char *dom, char *path)
  809. {
  810. int i, done;
  811. char *t, *u, *attr, *val;
  812. c->expire = ~0;
  813. memset(c, 0, sizeof *c);
  814. /* NAME=VALUE */
  815. t = skiptoken(p);
  816. c->name = p;
  817. p = skipspace(t);
  818. if(*p != '='){
  819. Badname:
  820. return "malformed cookie: no NAME=VALUE";
  821. }
  822. *t = '\0';
  823. p = skipspace(p+1);
  824. t = skipvalue(p, isns);
  825. if(*t)
  826. *t++ = '\0';
  827. c->value = p;
  828. p = skipspace(t);
  829. if(c->name[0]=='\0' || c->value[0]=='\0')
  830. goto Badname;
  831. done = 0;
  832. for(; *p && !done; p=skipspace(p)){
  833. attr = p;
  834. t = skiptoken(p);
  835. u = skipspace(t);
  836. switch(*u){
  837. case '\0':
  838. *t = '\0';
  839. val = p = u;
  840. break;
  841. case ';':
  842. *t = '\0';
  843. val = "";
  844. p = u+1;
  845. break;
  846. case '=':
  847. *t = '\0';
  848. val = skipspace(u+1);
  849. p = skipvalue(val, isns);
  850. if(*p==',')
  851. done = 1;
  852. if(*p)
  853. *p++ = '\0';
  854. break;
  855. case ',':
  856. if(!isns){
  857. val = "";
  858. p = u;
  859. *p++ = '\0';
  860. done = 1;
  861. break;
  862. }
  863. default:
  864. if(cookiedebug)
  865. fprint(2, "syntax: %s\n", p);
  866. return "syntax error";
  867. }
  868. for(i=0; i<nelem(stab); i++)
  869. if(stab[i].ishttp && cistrcmp(stab[i].s, attr)==0)
  870. *(char**)((ulong)c+stab[i].offset) = val;
  871. if(cistrcmp(attr, "expires") == 0){
  872. if(!isns)
  873. return "non-netscape cookie has Expires tag";
  874. if(!val[0])
  875. return "bad expires tag";
  876. c->expire = strtotime(val);
  877. if(c->expire == ~0)
  878. return "cannot parse netscape expires tag";
  879. }
  880. if(cistrcmp(attr, "max-age") == 0)
  881. c->expire = time(0)+atoi(val);
  882. if(cistrcmp(attr, "secure") == 0)
  883. c->secure = 1;
  884. }
  885. if(c->dom)
  886. c->explicitdom = 1;
  887. else
  888. c->dom = dom;
  889. if(c->path)
  890. c->explicitpath = 1;
  891. else{
  892. c->path = path;
  893. if((t = strchr(c->path, '?')) != 0)
  894. *t = '\0';
  895. if((t = strrchr(c->path, '/')) != 0)
  896. *t = '\0';
  897. }
  898. c->netscapestyle = isns;
  899. *e = p;
  900. return nil;
  901. }
  902. Jar *jar;
  903. typedef struct Aux Aux;
  904. struct Aux
  905. {
  906. char *dom;
  907. char *path;
  908. char *inhttp;
  909. char *outhttp;
  910. char *ctext;
  911. int rdoff;
  912. };
  913. enum
  914. {
  915. AuxBuf = 4096,
  916. MaxCtext = 16*1024*1024,
  917. };
  918. void
  919. cookieopen(Req *r)
  920. {
  921. char *s, *es;
  922. int i, sz;
  923. Aux *a;
  924. syncjar(jar);
  925. a = emalloc9p(sizeof(Aux));
  926. r->fid->aux = a;
  927. if(r->ifcall.mode&OTRUNC){
  928. a->ctext = emalloc9p(1);
  929. a->ctext[0] = '\0';
  930. }else{
  931. sz = 256*jar->nc+1024; /* BUG should do better */
  932. a->ctext = emalloc9p(sz);
  933. a->ctext[0] = '\0';
  934. s = a->ctext;
  935. es = s+sz;
  936. for(i=0; i<jar->nc; i++)
  937. s = seprint(s, es, "%K\n", &jar->c[i]);
  938. }
  939. respond(r, nil);
  940. }
  941. void
  942. cookieread(Req *r)
  943. {
  944. Aux *a;
  945. a = r->fid->aux;
  946. readstr(r, a->ctext);
  947. respond(r, nil);
  948. }
  949. void
  950. cookiewrite(Req *r)
  951. {
  952. Aux *a;
  953. int sz;
  954. a = r->fid->aux;
  955. sz = r->ifcall.count+r->ifcall.offset;
  956. if(sz > strlen(a->ctext)){
  957. if(sz >= MaxCtext){
  958. respond(r, "cookie file too large");
  959. return;
  960. }
  961. a->ctext = erealloc9p(a->ctext, sz+1);
  962. a->ctext[sz] = '\0';
  963. }
  964. memmove(a->ctext+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
  965. r->ofcall.count = r->ifcall.count;
  966. respond(r, nil);
  967. }
  968. void
  969. cookieclunk(Fid *fid)
  970. {
  971. char *p, *nextp;
  972. Aux *a;
  973. int i;
  974. a = fid->aux;
  975. if(a == nil)
  976. return;
  977. for(i=0; i<jar->nc; i++)
  978. jar->c[i].mark = 1;
  979. for(p=a->ctext; *p; p=nextp){
  980. if((nextp = strchr(p, '\n')) != nil)
  981. *nextp++ = '\0';
  982. else
  983. nextp = "";
  984. addtojar(jar, p, 0);
  985. }
  986. for(i=0; i<jar->nc; i++)
  987. if(jar->c[i].mark)
  988. delcookie(jar, &jar->c[i]);
  989. syncjar(jar);
  990. free(a->dom);
  991. free(a->path);
  992. free(a->inhttp);
  993. free(a->outhttp);
  994. free(a->ctext);
  995. free(a);
  996. }
  997. void
  998. closecookies(void)
  999. {
  1000. closejar(jar);
  1001. }
  1002. void
  1003. initcookies(char *file)
  1004. {
  1005. char *home;
  1006. fmtinstall('J', jarfmt);
  1007. fmtinstall('K', cookiefmt);
  1008. if(file == nil){
  1009. home = getenv("home");
  1010. if(home == nil)
  1011. sysfatal("no cookie file specified and no $home");
  1012. file = emalloc9p(strlen(home)+30);
  1013. strcpy(file, home);
  1014. strcat(file, "/lib/webcookies");
  1015. }
  1016. jar = readjar(file);
  1017. if(jar == nil)
  1018. sysfatal("readjar: %r");
  1019. }
  1020. void
  1021. httpsetcookie(char *hdr, char *dom, char *path)
  1022. {
  1023. if(path == nil)
  1024. path = "/";
  1025. parsehttp(jar, hdr, dom, path);
  1026. syncjar(jar);
  1027. }
  1028. char*
  1029. httpcookies(char *dom, char *path, int issecure)
  1030. {
  1031. char buf[1024];
  1032. Jar *j;
  1033. syncjar(jar);
  1034. j = cookiesearch(jar, dom, path, issecure);
  1035. snprint(buf, sizeof buf, "%J", j);
  1036. closejar(j);
  1037. return estrdup(buf);
  1038. }