changelog shortlog tags branches changeset files revisions annotate raw help

Mercurial > hg > plan9front / sys/src/cmd/webfs/fs.c

changeset 4343: 6d999c39a9f0
parent: e2715ad95a8f
child: e16a172bcae6
author: cinap_lenrek@felloff.net
date: Sat, 14 Mar 2015 01:09:37 +0100
permissions: -rw-r--r--
description: webfs: do not send credentials in automatic referer url
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7 
8 #include "dat.h"
9 #include "fns.h"
10 
11 typedef struct Webfid Webfid;
12 typedef struct Client Client;
13 
14 struct Client
15 {
16  Ref;
17 
18  char request[16];
19  Url *baseurl;
20  Url *url;
21  Key *hdr;
22 
23  int obody; /* body opend */
24  int cbody; /* body closed */
25  Buq *qbody;
26 };
27 
28 struct Webfid
29 {
30  int level;
31 
32  Client *client;
33  Key *key; /* copy for Qheader */
34  Buq *buq; /* reference for Qbody, Qpost */
35 };
36 
37 enum {
38  Qroot,
39  Qrctl,
40  Qclone,
41  Qclient,
42  Qctl,
43  Qbody,
44  Qpost,
45  Qparsed,
46  Qurl,
47  Qurlschm,
48  Qurluser,
49  Qurlpass,
50  Qurlhost,
51  Qurlport,
52  Qurlpath,
53  Qurlqwry,
54  Qurlfrag,
55  Qheader,
56 };
57 
58 static char *nametab[] = {
59  "/",
60  "ctl",
61  "clone",
62  nil,
63  "ctl",
64  "body",
65  "postbody",
66  "parsed",
67  "url",
68  "scheme",
69  "user",
70  "passwd",
71  "host",
72  "port",
73  "path",
74  "query",
75  "fragment",
76  nil,
77 };
78 
79 static char *mtpt;
80 static char *service;
81 static long time0;
82 static char *user;
83 static char *agent;
84 static Client client[64];
85 static int nclient;
86 
87 #define CLIENTID(c) (((Client*)(c)) - client)
88 
89 Client*
90 newclient(void)
91 {
92  Client *cl;
93  int i;
94 
95  for(i = 0; i < nclient; i++)
96  if(client[i].ref == 0)
97  break;
98  if(i >= nelem(client))
99  return nil;
100  if(i == nclient)
101  nclient++;
102  cl = &client[i];
103  incref(cl);
104 
105  cl->request[0] = 0;
106  cl->baseurl = nil;
107  cl->url = nil;
108  cl->hdr = nil;
109  cl->qbody = nil;
110 
111  return cl;
112 }
113 
114 void
115 freeclient(Client *cl)
116 {
117  Key *k;
118 
119  if(cl == nil || decref(cl))
120  return;
121 
122  buclose(cl->qbody, 0);
123  bufree(cl->qbody);
124 
125  while(k = cl->hdr){
126  cl->hdr = k->next;
127  free(k);
128  }
129 
130  freeurl(cl->url);
131  freeurl(cl->baseurl);
132 
133  memset(cl, 0, sizeof(*cl));
134 }
135 
136 static Url*
137 clienturl(Client *cl)
138 {
139  static Url nullurl;
140 
141  if(cl->qbody && cl->qbody->url)
142  return cl->qbody->url;
143  if(cl->url)
144  return cl->url;
145  return &nullurl;
146 }
147 
148 static void*
149 wfaux(Webfid *f)
150 {
151  if(f->level < Qclient)
152  return nil;
153  else if(f->level < Qurl)
154  return f->client;
155  else if(f->level < Qheader)
156  return clienturl(f->client);
157  else
158  return f->key;
159 }
160 
161 static void
162 fsmkqid(Qid *q, int level, void *aux)
163 {
164  q->type = 0;
165  q->vers = 0;
166  switch(level){
167  case Qroot:
168  case Qparsed:
169  case Qclient:
170  q->type = QTDIR;
171  default:
172  q->path = (level<<24) | (((uintptr)aux ^ time0) & 0x00ffffff);
173  }
174 }
175 
176 static char*
177 fshdrname(char *s)
178 {
179  char *k, *w;
180 
181  for(k=w=s; *k; k++)
182  if(isalnum(*k))
183  *w++ = tolower(*k);
184  *w = 0;
185  return s;
186 }
187 
188 static int
189 urlstr(char *buf, int nbuf, Url *u, int level)
190 {
191  char *s;
192 
193  if(level == Qurl)
194  return snprint(buf, nbuf, "%U", u);
195  if(level == Qurlpath)
196  return snprint(buf, nbuf, "%s", Upath(u));
197  if((s = (&u->scheme)[level - Qurlschm]) == nil){
198  buf[0] = 0;
199  return 0;
200  }
201  return snprint(buf, nbuf, "%s", s);
202 }
203 
204 
205 static void
206 fsmkdir(Dir *d, int level, void *aux)
207 {
208  char buf[1024];
209 
210  memset(d, 0, sizeof(*d));
211  fsmkqid(&d->qid, level, aux);
212  d->mode = 0444;
213  d->atime = d->mtime = time0;
214  d->uid = estrdup(user);
215  d->gid = estrdup(user);
216  d->muid = estrdup(user);
217  if(d->qid.type & QTDIR)
218  d->mode |= DMDIR | 0111;
219  switch(level){
220  case Qheader:
221  d->name = fshdrname(estrdup(((Key*)aux)->key));
222  d->length = strlen(((Key*)aux)->val);
223  break;
224  case Qclient:
225  snprint(buf, sizeof(buf), "%ld", CLIENTID(aux));
226  d->name = estrdup(buf);
227  break;
228  case Qctl:
229  case Qrctl:
230  case Qclone:
231  d->mode = 0666;
232  if(0){
233  case Qpost:
234  d->mode = 0222;
235  }
236  default:
237  d->name = estrdup(nametab[level]);
238  if(level >= Qurl && level <= Qurlfrag)
239  d->length = urlstr(buf, sizeof(buf), (Url*)aux, level);
240  }
241 }
242 
243 static void
244 fsattach(Req *r)
245 {
246  Webfid *f;
247 
248  if(r->ifcall.aname && r->ifcall.aname[0]){
249  respond(r, "invalid attach specifier");
250  return;
251  }
252  f = emalloc(sizeof(*f));
253  f->level = Qroot;
254  fsmkqid(&r->fid->qid, f->level, wfaux(f));
255  r->ofcall.qid = r->fid->qid;
256  r->fid->aux = f;
257  respond(r, nil);
258 }
259 
260 static void
261 fsstat(Req *r)
262 {
263  Webfid *f;
264 
265  f = r->fid->aux;
266  fsmkdir(&r->d, f->level, wfaux(f));
267  respond(r, nil);
268 }
269 
270 static char*
271 fswalk1(Fid *fid, char *name, Qid *qid)
272 {
273  Webfid *f;
274  int i, j;
275 
276  if(!(fid->qid.type&QTDIR))
277  return "walk in non-directory";
278 
279  f = fid->aux;
280  if(strcmp(name, "..") == 0){
281  switch(f->level){
282  case Qroot:
283  break;
284  case Qclient:
285  freeclient(f->client);
286  f->client = nil;
287  break;
288  default:
289  if(f->level > Qparsed)
290  f->level = Qparsed;
291  else
292  f->level = Qclient;
293  }
294  } else {
295  for(i=f->level+1; i < nelem(nametab); i++){
296  if(nametab[i]){
297  if(strcmp(name, nametab[i]) == 0)
298  break;
299  if(i == Qbody && strncmp(name, "body.", 5) == 0)
300  break;
301  }
302  if(i == Qclient){
303  j = atoi(name);
304  if(j >= 0 && j < nclient){
305  f->client = &client[j];
306  incref(f->client);
307  break;
308  }
309  }
310  if(i == Qheader && f->client && f->client->qbody){
311  char buf[128];
312  Key *k;
313 
314  for(k = f->client->qbody->hdr; k; k = k->next){
315  nstrcpy(buf, k->key, sizeof(buf));
316  if(!strcmp(name, fshdrname(buf)))
317  break;
318  }
319  if(k != nil){
320  /* need to copy as key is owned by qbody wich might go away */
321  f->key = addkey(0, k->key, k->val);
322  break;
323  }
324  }
325  }
326  if(i >= nelem(nametab))
327  return "directory entry not found";
328  f->level = i;
329  }
330  fsmkqid(qid, f->level, wfaux(f));
331  fid->qid = *qid;
332  return nil;
333 }
334 
335 static char*
336 fsclone(Fid *oldfid, Fid *newfid)
337 {
338  Webfid *f, *o;
339 
340  o = oldfid->aux;
341  if(o == nil || o->key || o->buq)
342  return "bad fid";
343  f = emalloc(sizeof(*f));
344  memmove(f, o, sizeof(*f));
345  if(f->client)
346  incref(f->client);
347  newfid->aux = f;
348  return nil;
349 }
350 
351 static void
352 fsopen(Req *r)
353 {
354  Webfid *f;
355  Client *cl;
356 
357  f = r->fid->aux;
358  cl = f->client;
359  switch(f->level){
360  case Qclone:
361  if((cl = newclient()) == nil){
362  respond(r, "no more clients");
363  return;
364  }
365  f->level = Qctl;
366  f->client = cl;
367  fsmkqid(&r->fid->qid, f->level, wfaux(f));
368  r->ofcall.qid = r->fid->qid;
369  break;
370  case Qpost:
371  if(cl->qbody && !cl->cbody){
372  Inuse:
373  respond(r, "client in use");
374  return;
375  }
376  case Qbody:
377  if(cl->obody)
378  goto Inuse;
379  if(cl->cbody){
380  bufree(cl->qbody);
381  cl->qbody = nil;
382  cl->cbody = 0;
383  }
384  if(cl->qbody == nil){
385  char *m;
386 
387  if(cl->url == nil){
388  respond(r, "no url set");
389  return;
390  }
391  cl->qbody = bualloc(16*1024);
392  if(f->level != Qbody){
393  f->buq = bualloc(64*1024);
394  if(!lookkey(cl->hdr, "Content-Type"))
395  cl->hdr = addkey(cl->hdr, "Content-Type",
396  "application/x-www-form-urlencoded");
397  m = "POST";
398  } else
399  m = "GET";
400  if(cl->request[0])
401  m = cl->request;
402 
403  /*
404  * some sites give a 403 Forbidden if we dont include
405  * a meaningless Accept header in the request.
406  */
407  if(!lookkey(cl->hdr, "Accept"))
408  cl->hdr = addkey(cl->hdr, "Accept", "*/*");
409 
410  if(!lookkey(cl->hdr, "Referer")){
411  char *r;
412  Url *u;
413 
414  /*
415  * Referer header is often required on broken
416  * websites even if the spec makes them optional,
417  * so we make one up.
418  */
419  if(u = url("/", cl->url)){
420  if(r = u->host){
421  u->host = smprint("%H", r);
422  free(r);
423  }
424 
425  /* do not send credentials */
426  free(u->user); u->user = nil;
427  free(u->pass); u->pass = nil;
428 
429  if(r = smprint("%U", u)){
430  cl->hdr = addkey(cl->hdr, "Referer", r);
431  free(r);
432  }
433  freeurl(u);
434  }
435  }
436 
437  if(!lookkey(cl->hdr, "Connection"))
438  cl->hdr = addkey(cl->hdr, "Connection", "keep-alive");
439 
440  if(agent && !lookkey(cl->hdr, "User-Agent"))
441  cl->hdr = addkey(cl->hdr, "User-Agent", agent);
442 
443  http(m, cl->url, cl->hdr, cl->qbody, f->buq);
444  cl->request[0] = 0;
445  cl->url = nil;
446  cl->hdr = nil;
447  }
448  if(f->buq)
449  break;
450  cl->obody = 1;
451  incref(cl->qbody);
452  bureq(f->buq = cl->qbody, r);
453  return;
454  }
455  respond(r, nil);
456 }
457 
458 static int
459 rootgen(int i, Dir *d, void *)
460 {
461  i += Qroot+1;
462  if(i < Qclient){
463  fsmkdir(d, i, 0);
464  return 0;
465  }
466  i -= Qclient;
467  if(i < nclient){
468  fsmkdir(d, Qclient, &client[i]);
469  return 0;
470  }
471  return -1;
472 }
473 
474 static int
475 clientgen(int i, Dir *d, void *aux)
476 {
477  i += Qclient+1;
478  if(i > Qparsed){
479  Client *cl = aux;
480  Key *k;
481 
482  i -= Qparsed+1;
483  if(cl == nil || cl->qbody == nil)
484  return -1;
485  for(k = cl->qbody->hdr; i > 0 && k; i--, k = k->next)
486  ;
487  if(k == nil || i > 0)
488  return -1;
489  i = Qheader;
490  aux = k;
491  }
492  fsmkdir(d, i, aux);
493  return 0;
494 }
495 
496 static int
497 parsedgen(int i, Dir *d, void *aux)
498 {
499  i += Qparsed+1;
500  if(i > Qurlfrag)
501  return -1;
502  fsmkdir(d, i, aux);
503  return 0;
504 }
505 
506 static void
507 fsread(Req *r)
508 {
509  char buf[1024];
510  Webfid *f;
511 
512  f = r->fid->aux;
513  switch(f->level){
514  case Qroot:
515  dirread9p(r, rootgen, nil);
516  respond(r, nil);
517  return;
518  case Qclient:
519  dirread9p(r, clientgen, f->client);
520  respond(r, nil);
521  return;
522  case Qparsed:
523  dirread9p(r, parsedgen, clienturl(f->client));
524  respond(r, nil);
525  return;
526  case Qrctl:
527  snprint(buf, sizeof(buf), "useragent %s\ntimeout %d\n", agent, timeout);
528  String:
529  readstr(r, buf);
530  respond(r, nil);
531  return;
532  case Qctl:
533  snprint(buf, sizeof(buf), "%ld\n", CLIENTID(f->client));
534  goto String;
535  case Qheader:
536  snprint(buf, sizeof(buf), "%s", f->key->val);
537  goto String;
538  case Qurl:
539  case Qurlschm:
540  case Qurluser:
541  case Qurlpass:
542  case Qurlhost:
543  case Qurlport:
544  case Qurlpath:
545  case Qurlqwry:
546  case Qurlfrag:
547  urlstr(buf, sizeof(buf), clienturl(f->client), f->level);
548  goto String;
549  case Qbody:
550  bureq(f->buq, r);
551  return;
552  }
553  respond(r, "not implemented");
554 }
555 
556 static char*
557 rootctl(Srv *fs, char *ctl, char *arg)
558 {
559  Url *u;
560 
561  if(debug)
562  fprint(2, "rootctl: %q %q\n", ctl, arg);
563 
564  if(!strcmp(ctl, "useragent")){
565  free(agent);
566  if(arg && *arg)
567  agent = estrdup(arg);
568  else
569  agent = nil;
570  return nil;
571  }
572 
573  if(!strcmp(ctl, "flushauth")){
574  u = nil;
575  if(arg && *arg)
576  u = saneurl(url(arg, 0));
577  flushauth(u, 0);
578  freeurl(u);
579  return nil;
580  }
581 
582  if(!strcmp(ctl, "timeout")){
583  if(arg && *arg)
584  timeout = atoi(arg);
585  else
586  timeout = 0;
587  if(timeout < 0)
588  timeout = 0;
589  return nil;
590  }
591 
592  /* ppreemptive authentication only basic
593  * auth supported, ctl message of the form:
594  * preauth url realm
595  */
596  if(!strcmp(ctl, "preauth")){
597  char *a[3], buf[256];
598  int rc;
599 
600  if(tokenize(arg, a, nelem(a)) != 2)
601  return "preauth - bad field count";
602  if((u = saneurl(url(a[0], 0))) == nil)
603  return "preauth - malformed url";
604  snprint(buf, sizeof(buf), "BASIC realm=\"%s\"", a[1]);
605  srvrelease(fs);
606  rc = authenticate(u, u, "GET", buf);
607  srvacquire(fs);
608  freeurl(u);
609  if(rc == -1)
610  return "preauth failed";
611  return nil;
612  }
613 
614  return "bad ctl message";
615 }
616 
617 static char*
618 clientctl(Client *cl, char *ctl, char *arg)
619 {
620  char *p;
621  Url *u;
622  Key *k;
623 
624  if(debug)
625  fprint(2, "clientctl: %q %q\n", ctl, arg);
626 
627  if(!strcmp(ctl, "url")){
628  if((u = saneurl(url(arg, cl->baseurl))) == nil)
629  return "bad url";
630  freeurl(cl->url);
631  cl->url = u;
632  }
633  else if(!strcmp(ctl, "baseurl")){
634  if((u = url(arg, 0)) == nil)
635  return "bad baseurl";
636  freeurl(cl->baseurl);
637  cl->baseurl = u;
638  }
639  else if(!strcmp(ctl, "request")){
640  p = cl->request;
641  nstrcpy(p, arg, sizeof(cl->request));
642  for(; *p && isalpha(*p); p++)
643  *p = toupper(*p);
644  *p = 0;
645  }
646  else if(!strcmp(ctl, "headers")){
647  while(arg && *arg){
648  ctl = arg;
649  while(*ctl && strchr(whitespace, *ctl))
650  ctl++;
651  if(arg = strchr(ctl, '\n'))
652  *arg++ = 0;
653  if(k = parsehdr(ctl)){
654  k->next = cl->hdr;
655  cl->hdr = k;
656  }
657  }
658  }
659  else {
660  char buf[128], **t;
661  static char *tab[] = {
662  "User-Agent",
663  "Content-Type",
664  nil,
665  };
666  for(t = tab; *t; t++){
667  nstrcpy(buf, *t, sizeof(buf));
668  if(!strcmp(ctl, fshdrname(buf))){
669  cl->hdr = delkey(cl->hdr, *t);
670  if(arg && *arg)
671  cl->hdr = addkey(cl->hdr, *t, arg);
672  break;
673  }
674  }
675  if(*t == nil)
676  return "bad ctl message";
677  }
678  return nil;
679 }
680 
681 static void
682 fswrite(Req *r)
683 {
684  int n;
685  Webfid *f;
686  char *s, *t;
687 
688  f = r->fid->aux;
689  switch(f->level){
690  case Qrctl:
691  case Qctl:
692  n = r->ofcall.count = r->ifcall.count;
693  s = emalloc(n+1);
694  memmove(s, r->ifcall.data, n);
695  while(n > 0 && strchr("\r\n", s[n-1]))
696  n--;
697  s[n] = 0;
698  t = s;
699  while(*t && strchr(whitespace, *t)==0)
700  t++;
701  while(*t && strchr(whitespace, *t))
702  *t++ = 0;
703  if(f->level == Qctl)
704  t = clientctl(f->client, s, t);
705  else
706  t = rootctl(r->srv, s, t);
707  free(s);
708  respond(r, t);
709  return;
710  case Qpost:
711  bureq(f->buq, r);
712  return;
713  }
714  respond(r, "not implemented");
715 }
716 
717 static void
718 fsflush(Req *r)
719 {
720  Webfid *f;
721  Req *o;
722 
723  if(o = r->oldreq)
724  if(f = o->fid->aux)
725  buflushreq(f->buq, o);
726  respond(r, nil);
727 }
728 
729 static void
730 fsdestroyfid(Fid *fid)
731 {
732  Webfid *f;
733 
734  if(f = fid->aux){
735  fid->aux = nil;
736  if(f->buq){
737  buclose(f->buq, 0);
738  if(f->client->qbody == f->buq){
739  f->client->obody = 0;
740  f->client->cbody = 1;
741  }
742  bufree(f->buq);
743  }
744  if(f->key)
745  free(f->key);
746  freeclient(f->client);
747  free(f);
748  }
749 }
750 
751 static void
752 fsstart(Srv*)
753 {
754  /* drop reference to old webfs mount */
755  if(mtpt != nil)
756  unmount(nil, mtpt);
757 }
758 
759 Srv fs =
760 {
761  .start=fsstart,
762  .attach=fsattach,
763  .stat=fsstat,
764  .walk1=fswalk1,
765  .clone=fsclone,
766  .open=fsopen,
767  .read=fsread,
768  .write=fswrite,
769  .flush=fsflush,
770  .destroyfid=fsdestroyfid,
771 };
772 
773 void
774 usage(void)
775 {
776  fprint(2, "usage: %s [-D] [-A useragent] [-T timeout] [-m mtpt] [-s service]\n", argv0);
777  exits("usage");
778 }
779 
780 void
781 main(int argc, char *argv[])
782 {
783  char *s;
784 
785  quotefmtinstall();
786  fmtinstall('U', Ufmt);
787  fmtinstall('H', Hfmt);
788  fmtinstall('E', Efmt);
789  fmtinstall('[', encodefmt);
790 
791  mtpt = "/mnt/web";
792  user = getuser();
793  time0 = time(0);
794  timeout = 10000;
795 
796  ARGBEGIN {
797  case 'D':
798  chatty9p++;
799  break;
800  case 'A':
801  agent = EARGF(usage());
802  break;
803  case 'T':
804  timeout = atoi(EARGF(usage()));
805  if(timeout < 0)
806  timeout = 0;
807  break;
808  case 'm':
809  mtpt = EARGF(usage());
810  break;
811  case 's':
812  service = EARGF(usage());
813  break;
814  case 'd':
815  debug++;
816  break;
817  default:
818  usage();
819  } ARGEND;
820 
821  rfork(RFNOTEG);
822 
823  if(agent == nil)
824  agent = "Mozilla/5.0 (compatible; hjdicks)";
825  agent = estrdup(agent);
826 
827  if(s = getenv("httpproxy")){
828  proxy = saneurl(url(s, 0));
829  if(proxy == nil || strcmp(proxy->scheme, "http") && strcmp(proxy->scheme, "https"))
830  sysfatal("invalid httpproxy url: %s", s);
831  free(s);
832  }
833 
834  postmountsrv(&fs, service, mtpt, MREPL);
835  exits(0);
836 }