cookies.c 21 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145
  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. jar->qid = q;
  401. return 0;
  402. }
  403. static Jar*
  404. readjar(char *file)
  405. {
  406. char *lock, *p;
  407. Jar *jar;
  408. jar = newjar();
  409. lock = emalloc9p(strlen(file)+10);
  410. strcpy(lock, file);
  411. if((p = strrchr(lock, '/')) != nil)
  412. p++;
  413. else
  414. p = lock;
  415. memmove(p+2, p, strlen(p)+1);
  416. p[0] = 'L';
  417. p[1] = '.';
  418. jar->lockfile = lock;
  419. jar->file = file;
  420. jar->dirty = 1;
  421. if(syncjar(jar) < 0){
  422. free(jar->file);
  423. free(jar->lockfile);
  424. free(jar);
  425. return nil;
  426. }
  427. return jar;
  428. }
  429. static void
  430. closejar(Jar *jar)
  431. {
  432. int i;
  433. if(jar == nil)
  434. return;
  435. expirejar(jar, 0);
  436. if(syncjar(jar) < 0)
  437. fprint(2, "warning: cannot rewrite cookie jar: %r\n");
  438. for(i=0; i<jar->nc; i++)
  439. freecookie(&jar->c[i]);
  440. free(jar->file);
  441. free(jar);
  442. }
  443. /*
  444. * Domain name matching is per RFC2109, section 2:
  445. *
  446. * Hosts names can be specified either as an IP address or a FQHN
  447. * string. Sometimes we compare one host name with another. Host A's
  448. * name domain-matches host B's if
  449. *
  450. * * both host names are IP addresses and their host name strings match
  451. * exactly; or
  452. *
  453. * * both host names are FQDN strings and their host name strings match
  454. * exactly; or
  455. *
  456. * * A is a FQDN string and has the form NB, where N is a non-empty name
  457. * string, B has the form .B', and B' is a FQDN string. (So, x.y.com
  458. * domain-matches .y.com but not y.com.)
  459. *
  460. * Note that domain-match is not a commutative operation: a.b.c.com
  461. * domain-matches .c.com, but not the reverse.
  462. *
  463. * (This does not verify that IP addresses and FQDN's are well-formed.)
  464. */
  465. static int
  466. isdomainmatch(char *name, char *pattern)
  467. {
  468. int lname, lpattern;
  469. if(cistrcmp(name, pattern)==0)
  470. return 1;
  471. if(strcmp(ipattr(name), "dom")==0 && pattern[0]=='.'){
  472. lname = strlen(name);
  473. lpattern = strlen(pattern);
  474. if(lname >= lpattern && cistrcmp(name+lname-lpattern, pattern)==0)
  475. return 1;
  476. }
  477. return 0;
  478. }
  479. /*
  480. * RFC2109 4.3.4:
  481. * - domain must match
  482. * - path in cookie must be a prefix of request path
  483. * - cookie must not have expired
  484. */
  485. static int
  486. iscookiematch(Cookie *c, char *dom, char *path, uint now)
  487. {
  488. return isdomainmatch(dom, c->dom)
  489. && strncmp(c->path, path, strlen(c->path))==0
  490. && c->expire >= now;
  491. }
  492. /*
  493. * Produce a subjar of matching cookies.
  494. * Secure cookies are only included if secure is set.
  495. */
  496. static Jar*
  497. cookiesearch(Jar *jar, char *dom, char *path, int issecure)
  498. {
  499. int i;
  500. Jar *j;
  501. uint now;
  502. if(cookiedebug)
  503. fprint(2, "cookiesearch %s %s %d\n", dom, path, issecure);
  504. now = time(0);
  505. j = newjar();
  506. for(i=0; i<jar->nc; i++){
  507. if(cookiedebug)
  508. fprint(2, "\ttry %s %s %d %s\n", jar->c[i].dom, jar->c[i].path, jar->c[i].secure, jar->c[i].name);
  509. if((issecure || !jar->c[i].secure) && iscookiematch(&jar->c[i], dom, path, now)){
  510. if(cookiedebug)
  511. fprint(2, "\tmatched\n");
  512. addcookie(j, &jar->c[i]);
  513. }
  514. }
  515. if(j->nc == 0){
  516. closejar(j);
  517. werrstr("no cookies found");
  518. return nil;
  519. }
  520. qsort(j->c, j->nc, sizeof(j->c[0]), (int(*)(const void*, const void*))cookiecmp);
  521. return j;
  522. }
  523. /*
  524. * RFC2109 4.3.2 security checks
  525. */
  526. static char*
  527. isbadcookie(Cookie *c, char *dom, char *path)
  528. {
  529. if(strncmp(c->path, path, strlen(c->path)) != 0)
  530. return "cookie path is not a prefix of the request path";
  531. if(c->dom[0] != '.')
  532. return "cookie domain doesn't start with dot";
  533. if(memchr(c->dom+1, '.', strlen(c->dom)-1-1) == nil)
  534. return "cookie domain doesn't have embedded dots";
  535. if(!isdomainmatch(dom, c->dom))
  536. return "request host does not match cookie domain";
  537. if(strcmp(ipattr(dom), "dom")==0
  538. && memchr(dom, '.', strlen(dom)-strlen(c->dom)) != nil)
  539. return "request host contains dots before cookie domain";
  540. return 0;
  541. }
  542. /*
  543. * Sunday, 25-Jan-2002 12:24:36 GMT
  544. * Sunday, 25 Jan 2002 12:24:36 GMT
  545. * Sun, 25 Jan 02 12:24:36 GMT
  546. */
  547. static int
  548. isleap(int year)
  549. {
  550. return year%4==0 && (year%100!=0 || year%400==0);
  551. }
  552. static uint
  553. strtotime(char *s)
  554. {
  555. char *os;
  556. int i;
  557. Tm tm;
  558. static int mday[2][12] = {
  559. 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  560. 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
  561. };
  562. static char *wday[] = {
  563. "Sunday", "Monday", "Tuesday", "Wednesday",
  564. "Thursday", "Friday", "Saturday",
  565. };
  566. static char *mon[] = {
  567. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  568. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  569. };
  570. os = s;
  571. /* Sunday, */
  572. for(i=0; i<nelem(wday); i++){
  573. if(cistrncmp(s, wday[i], strlen(wday[i])) == 0){
  574. s += strlen(wday[i]);
  575. break;
  576. }
  577. if(cistrncmp(s, wday[i], 3) == 0){
  578. s += 3;
  579. break;
  580. }
  581. }
  582. if(i==nelem(wday)){
  583. if(cookiedebug)
  584. fprint(2, "bad wday (%s)\n", os);
  585. return -1;
  586. }
  587. if(*s++ != ',' || *s++ != ' '){
  588. if(cookiedebug)
  589. fprint(2, "bad wday separator (%s)\n", os);
  590. return -1;
  591. }
  592. /* 25- */
  593. if(!isdigit(s[0]) || !isdigit(s[1]) || (s[2]!='-' && s[2]!=' ')){
  594. if(cookiedebug)
  595. fprint(2, "bad day of month (%s)\n", os);
  596. return -1;
  597. }
  598. tm.mday = strtol(s, 0, 10);
  599. s += 3;
  600. /* Jan- */
  601. for(i=0; i<nelem(mon); i++)
  602. if(cistrncmp(s, mon[i], 3) == 0){
  603. tm.mon = i;
  604. s += 3;
  605. break;
  606. }
  607. if(i==nelem(mon)){
  608. if(cookiedebug)
  609. fprint(2, "bad month (%s)\n", os);
  610. return -1;
  611. }
  612. if(s[0] != '-' && s[0] != ' '){
  613. if(cookiedebug)
  614. fprint(2, "bad month separator (%s)\n", os);
  615. return -1;
  616. }
  617. s++;
  618. /* 2002 */
  619. if(!isdigit(s[0]) || !isdigit(s[1])){
  620. if(cookiedebug)
  621. fprint(2, "bad year (%s)\n", os);
  622. return -1;
  623. }
  624. tm.year = strtol(s, 0, 10);
  625. s += 2;
  626. if(isdigit(s[0]) && isdigit(s[1]))
  627. s += 2;
  628. else{
  629. if(tm.year <= 68)
  630. tm.year += 2000;
  631. else
  632. tm.year += 1900;
  633. }
  634. if(tm.mday==0 || tm.mday > mday[isleap(tm.year)][tm.mon]){
  635. if(cookiedebug)
  636. fprint(2, "invalid day of month (%s)\n", os);
  637. return -1;
  638. }
  639. tm.year -= 1900;
  640. if(*s++ != ' '){
  641. if(cookiedebug)
  642. fprint(2, "bad year separator (%s)\n", os);
  643. return -1;
  644. }
  645. if(!isdigit(s[0]) || !isdigit(s[1]) || s[2]!=':'
  646. || !isdigit(s[3]) || !isdigit(s[4]) || s[5]!=':'
  647. || !isdigit(s[6]) || !isdigit(s[7]) || s[8]!=' '){
  648. if(cookiedebug)
  649. fprint(2, "bad time (%s)\n", os);
  650. return -1;
  651. }
  652. tm.hour = atoi(s);
  653. tm.min = atoi(s+3);
  654. tm.sec = atoi(s+6);
  655. if(tm.hour >= 24 || tm.min >= 60 || tm.sec >= 60){
  656. if(cookiedebug)
  657. fprint(2, "invalid time (%s)\n", os);
  658. return -1;
  659. }
  660. s += 9;
  661. if(cistrcmp(s, "GMT") != 0){
  662. if(cookiedebug)
  663. fprint(2, "time zone not GMT (%s)\n", os);
  664. return -1;
  665. }
  666. strcpy(tm.zone, "GMT");
  667. tm.yday = 0;
  668. return tm2sec(&tm);
  669. }
  670. /*
  671. * skip linear whitespace. we're a bit more lenient than RFC2616 2.2.
  672. */
  673. static char*
  674. skipspace(char *s)
  675. {
  676. while(*s=='\r' || *s=='\n' || *s==' ' || *s=='\t')
  677. s++;
  678. return s;
  679. }
  680. /*
  681. * Try to identify old netscape headers.
  682. * The old headers:
  683. * - didn't allow spaces around the '='
  684. * - used an 'Expires' attribute
  685. * - had no 'Version' attribute
  686. * - had no quotes
  687. * - allowed whitespace in values
  688. * - apparently separated attr/value pairs with ';' exclusively
  689. */
  690. static int
  691. isnetscape(char *hdr)
  692. {
  693. char *s;
  694. for(s=hdr; (s=strchr(s, '=')) != nil; s++){
  695. if(isspace(s[1]) || (s > hdr && isspace(s[-1])))
  696. return 0;
  697. if(s[1]=='"')
  698. return 0;
  699. }
  700. if(cistrstr(hdr, "version="))
  701. return 0;
  702. return 1;
  703. }
  704. /*
  705. * Parse HTTP response headers, adding cookies to jar.
  706. * Overwrites the headers.
  707. */
  708. static char* parsecookie(Cookie*, char*, char**, int, char*, char*);
  709. static int
  710. parsehttp(Jar *jar, char *hdr, char *dom, char *path)
  711. {
  712. static char setcookie[] = "Set-Cookie:";
  713. char *e, *p, *nextp;
  714. Cookie c;
  715. int isns, n;
  716. isns = isnetscape(hdr);
  717. n = 0;
  718. for(p=hdr; p; p=nextp){
  719. p = skipspace(p);
  720. if(*p == '\0')
  721. break;
  722. nextp = strchr(p, '\n');
  723. if(nextp != nil)
  724. *nextp++ = '\0';
  725. if(cistrncmp(p, setcookie, strlen(setcookie)) != 0)
  726. continue;
  727. if(cookiedebug)
  728. fprint(2, "%s\n", p);
  729. p = skipspace(p+strlen(setcookie));
  730. for(; *p; p=skipspace(p)){
  731. if((e = parsecookie(&c, p, &p, isns, dom, path)) != nil){
  732. if(cookiedebug)
  733. fprint(2, "parse cookie: %s\n", e);
  734. break;
  735. }
  736. if((e = isbadcookie(&c, dom, path)) != nil){
  737. if(cookiedebug)
  738. fprint(2, "reject cookie; %s\n", e);
  739. continue;
  740. }
  741. addcookie(jar, &c);
  742. n++;
  743. }
  744. }
  745. return n;
  746. }
  747. static char*
  748. skipquoted(char *s)
  749. {
  750. /*
  751. * Sec 2.2 of RFC2616 defines a "quoted-string" as:
  752. *
  753. * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
  754. * qdtext = <any TEXT except <">>
  755. * quoted-pair = "\" CHAR
  756. *
  757. * TEXT is any octet except CTLs, but including LWS;
  758. * LWS is [CR LF] 1*(SP | HT);
  759. * CHARs are ASCII octets 0-127; (NOTE: we reject 0's)
  760. * CTLs are octets 0-31 and 127;
  761. */
  762. if(*s != '"')
  763. return s;
  764. for(s++; 32 <= *s && *s < 127 && *s != '"'; s++)
  765. if(*s == '\\' && *(s+1) != '\0')
  766. s++;
  767. return s;
  768. }
  769. static char*
  770. skiptoken(char *s)
  771. {
  772. /*
  773. * Sec 2.2 of RFC2616 defines a "token" as
  774. * 1*<any CHAR except CTLs or separators>;
  775. * CHARs are ASCII octets 0-127;
  776. * CTLs are octets 0-31 and 127;
  777. * separators are "()<>@,;:\/[]?={}", double-quote, SP (32), and HT (9)
  778. */
  779. while(32 <= *s && *s < 127 && strchr("()<>@,;:[]?={}\" \t\\", *s)==nil)
  780. s++;
  781. return s;
  782. }
  783. static char*
  784. skipvalue(char *s, int isns)
  785. {
  786. char *t;
  787. /*
  788. * An RFC2109 value is an HTTP token or an HTTP quoted string.
  789. * Netscape servers ignore the spec and rely on semicolons, apparently.
  790. */
  791. if(isns){
  792. if((t = strchr(s, ';')) == nil)
  793. t = s+strlen(s);
  794. return t;
  795. }
  796. if(*s == '"')
  797. return skipquoted(s);
  798. return skiptoken(s);
  799. }
  800. /*
  801. * RMID=80b186bb64c03c65fab767f8; expires=Monday, 10-Feb-2003 04:44:39 GMT;
  802. * path=/; domain=.nytimes.com
  803. */
  804. static char*
  805. parsecookie(Cookie *c, char *p, char **e, int isns, char *dom, char *path)
  806. {
  807. int i, done;
  808. char *t, *u, *attr, *val;
  809. memset(c, 0, sizeof *c);
  810. /* NAME=VALUE */
  811. t = skiptoken(p);
  812. c->name = p;
  813. p = skipspace(t);
  814. if(*p != '='){
  815. Badname:
  816. return "malformed cookie: no NAME=VALUE";
  817. }
  818. *t = '\0';
  819. p = skipspace(p+1);
  820. t = skipvalue(p, isns);
  821. if(*t)
  822. *t++ = '\0';
  823. c->value = p;
  824. p = skipspace(t);
  825. if(c->name[0]=='\0' || c->value[0]=='\0')
  826. goto Badname;
  827. done = 0;
  828. for(; *p && !done; p=skipspace(p)){
  829. attr = p;
  830. t = skiptoken(p);
  831. u = skipspace(t);
  832. switch(*u){
  833. case ';':
  834. *t = '\0';
  835. val = "";
  836. p = u+1;
  837. break;
  838. case '=':
  839. *t = '\0';
  840. val = skipspace(u+1);
  841. p = skipvalue(val, isns);
  842. if(*p==',')
  843. done = 1;
  844. if(*p)
  845. *p++ = '\0';
  846. break;
  847. case ',':
  848. if(!isns){
  849. val = "";
  850. p = u;
  851. *p++ = '\0';
  852. done = 1;
  853. break;
  854. }
  855. default:
  856. if(cookiedebug)
  857. fprint(2, "syntax: %s\n", p);
  858. return "syntax error";
  859. }
  860. for(i=0; i<nelem(stab); i++)
  861. if(stab[i].ishttp && cistrcmp(stab[i].s, attr)==0)
  862. *(char**)((ulong)c+stab[i].offset) = val;
  863. if(cistrcmp(attr, "expires") == 0){
  864. if(!isns)
  865. return "non-netscape cookie has Expires tag";
  866. if(!val[0])
  867. return "bad expires tag";
  868. c->expire = strtotime(val);
  869. if(c->expire == ~0)
  870. return "cannot parse netscape expires tag";
  871. }
  872. if(cistrcmp(attr, "max-age") == 0)
  873. c->expire = time(0)+atoi(val);
  874. if(cistrcmp(attr, "secure") == 0)
  875. c->secure = 1;
  876. }
  877. if(c->dom)
  878. c->explicitdom = 1;
  879. else
  880. c->dom = dom;
  881. if(c->path)
  882. c->explicitpath = 1;
  883. else
  884. c->path = path;
  885. c->netscapestyle = isns;
  886. *e = p;
  887. return nil;
  888. }
  889. Jar *jar;
  890. typedef struct Aux Aux;
  891. struct Aux
  892. {
  893. char *dom;
  894. char *path;
  895. char *inhttp;
  896. char *outhttp;
  897. char *ctext;
  898. int rdoff;
  899. };
  900. enum
  901. {
  902. AuxBuf = 4096,
  903. MaxCtext = 16*1024*1024,
  904. };
  905. void
  906. cookieopen(Req *r)
  907. {
  908. char *s, *es;
  909. int i, sz;
  910. Aux *a;
  911. syncjar(jar);
  912. a = emalloc9p(sizeof(Aux));
  913. r->fid->aux = a;
  914. if(r->ifcall.mode&OTRUNC){
  915. a->ctext = emalloc9p(1);
  916. a->ctext[0] = '\0';
  917. }else{
  918. sz = 256*jar->nc+1024; /* BUG should do better */
  919. a->ctext = emalloc9p(sz);
  920. a->ctext[0] = '\0';
  921. s = a->ctext;
  922. es = s+sz;
  923. for(i=0; i<jar->nc; i++)
  924. s = seprint(s, es, "%K\n", &jar->c[i]);
  925. }
  926. respond(r, nil);
  927. }
  928. void
  929. cookieread(Req *r)
  930. {
  931. Aux *a;
  932. a = r->fid->aux;
  933. readstr(r, a->ctext);
  934. respond(r, nil);
  935. }
  936. void
  937. cookiewrite(Req *r)
  938. {
  939. Aux *a;
  940. int sz;
  941. a = r->fid->aux;
  942. sz = r->ifcall.count+r->ifcall.offset;
  943. if(sz > strlen(a->ctext)){
  944. if(sz >= MaxCtext){
  945. respond(r, "cookie file too large");
  946. return;
  947. }
  948. a->ctext = erealloc9p(a->ctext, sz+1);
  949. a->ctext[sz] = '\0';
  950. }
  951. memmove(a->ctext+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
  952. r->ofcall.count = r->ifcall.count;
  953. respond(r, nil);
  954. }
  955. void
  956. cookieclunk(Fid *fid)
  957. {
  958. char *p, *nextp;
  959. Aux *a;
  960. int i;
  961. a = fid->aux;
  962. if(a == nil)
  963. return;
  964. for(i=0; i<jar->nc; i++)
  965. jar->c[i].mark = 1;
  966. for(p=a->ctext; *p; p=nextp){
  967. if((nextp = strchr(p, '\n')) != nil)
  968. *nextp++ = '\0';
  969. else
  970. nextp = "";
  971. addtojar(jar, p, 0);
  972. }
  973. for(i=0; i<jar->nc; i++)
  974. if(jar->c[i].mark)
  975. delcookie(jar, &jar->c[i]);
  976. syncjar(jar);
  977. free(a->dom);
  978. free(a->path);
  979. free(a->inhttp);
  980. free(a->outhttp);
  981. free(a->ctext);
  982. free(a);
  983. }
  984. void
  985. closecookies(void)
  986. {
  987. closejar(jar);
  988. }
  989. void
  990. initcookies(char *file)
  991. {
  992. char *home;
  993. fmtinstall('J', jarfmt);
  994. fmtinstall('K', cookiefmt);
  995. if(file == nil){
  996. home = getenv("home");
  997. if(home == nil)
  998. sysfatal("no cookie file specified and no $home");
  999. file = emalloc9p(strlen(home)+30);
  1000. strcpy(file, home);
  1001. strcat(file, "/lib/webcookies");
  1002. }
  1003. jar = readjar(file);
  1004. if(jar == nil)
  1005. sysfatal("readjar: %r");
  1006. }
  1007. void
  1008. httpsetcookie(char *hdr, char *dom, char *path)
  1009. {
  1010. if(path == nil)
  1011. path = "/";
  1012. parsehttp(jar, hdr, dom, path);
  1013. syncjar(jar);
  1014. }
  1015. char*
  1016. httpcookies(char *dom, char *path, int issecure)
  1017. {
  1018. char buf[1024];
  1019. Jar *j;
  1020. syncjar(jar);
  1021. j = cookiesearch(jar, dom, path, issecure);
  1022. snprint(buf, sizeof buf, "%J", j);
  1023. closejar(j);
  1024. return estrdup(buf);
  1025. }