summaryrefslogtreecommitdiff
path: root/emu/port/devcap.c
diff options
context:
space:
mode:
Diffstat (limited to 'emu/port/devcap.c')
-rw-r--r--emu/port/devcap.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/emu/port/devcap.c b/emu/port/devcap.c
new file mode 100644
index 00000000..00d1c2be
--- /dev/null
+++ b/emu/port/devcap.c
@@ -0,0 +1,243 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "mp.h"
+#include "libsec.h"
+
+/*
+ * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
+ */
+
+enum {
+ Captimeout = 15, /* seconds until expiry */
+ Capidletime = 60 /* idle seconds before capwatch exits */
+};
+
+typedef struct Caps Caps;
+struct Caps
+{
+ uchar hash[SHA1dlen];
+ ulong time;
+ Caps* next;
+};
+
+struct {
+ QLock l;
+ Caps* caps;
+ int kpstarted;
+} allcaps;
+
+enum {
+ Qdir,
+ Qhash,
+ Quse
+};
+
+static Dirtab capdir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "capuse", {Quse, 0}, 0, 0222,
+ "caphash", {Qhash, 0}, 0, 0200,
+};
+
+static int ncapdir = nelem(capdir);
+
+static void
+capwatch(void *a)
+{
+ Caps *c, **l;
+ int idletime;
+
+ USED(a);
+ idletime = 0;
+ for(;;){
+ osmillisleep(30*1000);
+ qlock(&allcaps.l);
+ for(l = &allcaps.caps; (c = *l) != nil;)
+ if(++c->time > Captimeout){
+ *l = c->next;
+ free(c);
+ }else
+ l = &c->next;
+ if(allcaps.caps == nil){
+ if(++idletime > Capidletime){
+ allcaps.kpstarted = 0;
+ qunlock(&allcaps.l);
+ pexit("", 0);
+ }
+ }else
+ idletime = 0;
+ qunlock(&allcaps.l);
+ }
+}
+
+static Chan *
+capattach(char *spec)
+{
+ return devattach(0x00A4, spec); /* L'¤' */
+}
+
+static Walkqid*
+capwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen);
+}
+
+static int
+capstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, capdir, nelem(capdir), devgen);
+}
+
+static Chan*
+capopen(Chan *c, int omode)
+{
+ if(c->qid.type & QTDIR) {
+ if(omode != OREAD)
+ error(Eisdir);
+ c->mode = omode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+ }
+
+ if(c->qid.path == Qhash && !iseve())
+ error(Eperm);
+
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+capclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+capread(Chan *c, void *va, long n, vlong vl)
+{
+ USED(vl);
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, va, n, capdir, ncapdir, devgen);
+
+ default:
+ error(Eperm);
+ break;
+ }
+ return n;
+}
+
+static int
+capwritehash(uchar *a, int l)
+{
+ Caps *c;
+
+ if(l != SHA1dlen)
+ return -1;
+ c = malloc(sizeof(*c));
+ if(c == nil)
+ return -1;
+ memmove(c->hash, a, l);
+ c->time = 0;
+ qlock(&allcaps.l);
+ c->next = allcaps.caps;
+ allcaps.caps = c;
+ if(!allcaps.kpstarted){
+ allcaps.kpstarted = 1;
+ kproc("capwatch", capwatch, 0, 0);
+ }
+ qunlock(&allcaps.l);
+ return 0;
+}
+
+static int
+capwriteuse(uchar *a, int len)
+{
+ int n;
+ uchar digest[SHA1dlen];
+ char buf[128], *p, *users[3];
+ Caps *c, **l;
+
+ if(len >= sizeof(buf)-1)
+ return -1;
+ memmove(buf, a, len);
+ buf[len] = 0;
+ p = strrchr(buf, '@');
+ if(p == nil)
+ return -1;
+ *p++ = 0;
+ len = strlen(p);
+ n = strlen(buf);
+ if(len == 0 || n == 0)
+ return -1;
+ hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil);
+ n = getfields(buf, users, nelem(users), 0, "@");
+ if(n == 1)
+ users[1] = users[0];
+ else if(n != 2)
+ return -1;
+ if(*users[0] == 0 || *users[1] == 0)
+ return -1;
+ qlock(&allcaps.l);
+ for(l = &allcaps.caps; (c = *l) != nil; l = &c->next)
+ if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){
+ *l = c->next;
+ qunlock(&allcaps.l);
+ free(c);
+ if(n == 2 && strcmp(up->env->user, users[0]) != 0)
+ return -1;
+ setid(users[1], 0); /* could use users[2] to mean `host OS user' */
+ return 0;
+ }
+ qunlock(&allcaps.l);
+ return -1;
+}
+
+static long
+capwrite(Chan* c, void* buf, long n, vlong offset)
+{
+ USED(offset);
+ switch((ulong)c->qid.path){
+ case Qhash:
+ if(capwritehash(buf, n) < 0)
+ error(Ebadarg);
+ return n;
+ case Quse:
+ if(capwriteuse(buf, n) < 0)
+ error("invalid capability");
+ return n;
+ }
+ error(Ebadarg);
+ return 0;
+}
+
+static void
+capremove(Chan *c)
+{
+ if(c->qid.path != Qhash || !iseve())
+ error(Eperm);
+ ncapdir = nelem(capdir)-1;
+}
+
+Dev capdevtab = {
+ 0x00A4, /* L'¤' */
+ "cap",
+
+ devinit,
+ capattach,
+ capwalk,
+ capstat,
+ capopen,
+ devcreate,
+ capclose,
+ capread,
+ devbread,
+ capwrite,
+ devbwrite,
+ capremove,
+ devwstat
+};