#include "dat.h" static char secstore[100]; /* server name */ /* bind in the default network and cs */ static void bindnetcs(void) { int srvfd; if(access("/net/cs", AEXIST) < 0){ if((srvfd = open("#s/cs", ORDWR)) >= 0){ mount(srvfd, -1, "/net", MBEFORE, ""); close(srvfd); } } if(access("/net/il", AEXIST) < 0) bind("#I", "/net", MBEFORE); } int _authdial(char *net, char *authdom) { int fd; int vanilla; vanilla = net == nil || strcmp(net, "/net") == 0; if(vanilla) bindnetcs(); /* let cs figure things out */ fd = authdial(net, authdom); if(fd >= 0) return fd; /* if this is the default network */ if(vanilla){ /* ... and cs isn't running */ if(access("/net/cs", AEXIST) >= 0) return -1; /* ... use the auth sever passed to us as an arg */ if(authaddr == nil) return -1; fd = dial(netmkaddr(authaddr, "il", "566"), 0, 0, 0); if(fd >= 0) return fd; fd = dial(netmkaddr(authaddr, "tcp", "567"), 0, 0, 0); } return fd; } /* * prompt user for a key. don't care about memory leaks, runs standalone */ static Attr* promptforkey(char *params) { char *v; int fd; Attr *a, *attr; char *def; fd = open("/dev/cons", ORDWR); if(fd < 0) sysfatal("opening /dev/cons: %r"); attr = parseattr(params); fprint(fd, "\n!Adding key:"); for(a=attr; a; a=a->next) if(a->type != AttrQuery && s_to_c(a->name)[0] != '!') fprint(fd, " %q=%q", s_to_c(a->name), s_to_c(a->val)); fprint(fd, "\n"); for(a=attr; a; a=a->next){ v = s_to_c(a->name); if(a->type != AttrQuery || v[0]=='!') continue; def = nil; if(strcmp(v, "user") == 0) def = getuser(); a->val = readcons(v, def, 0); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } for(a=attr; a; a=a->next){ v = s_to_c(a->name); if(a->type != AttrQuery || v[0]!='!') continue; def = nil; if(strcmp(v+1, "user") == 0) def = getuser(); a->val = readcons(v+1, def, 1); if(a->val == nil) sysfatal("user terminated key input"); a->type = AttrNameval; } fprint(fd, "!\n"); close(fd); return attr; } /* * send a key to the mounted factotum */ static int sendkey(Attr *attr) { int fd, rv; char buf[1024]; fd = open("/mnt/factotum/ctl", ORDWR); if(fd < 0) sysfatal("opening /mnt/factotum/ctl: %r"); rv = fprint(fd, "key %A\n", attr); read(fd, buf, sizeof buf); close(fd); return rv; } /* askuser */ void askuser(char *params) { Attr *attr; attr = promptforkey(params); if(attr == nil) sysfatal("no key supplied"); if(sendkey(attr) < 0) sysfatal("sending key to factotum: %r"); } ulong conftaggen; int canusekey(Fsstate *fss, Key *k) { int i; if(strfindattr(k->attr, "confirm")){ for(i=0; inconf; i++) if(fss->conf[i].key == k) return fss->conf[i].canuse; if(fss->nconf%16 == 0) fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0]))); fss->conf[fss->nconf].key = k; k->ref++; fss->conf[fss->nconf].canuse = -1; fss->conf[fss->nconf].tag = conftaggen++; fss->nconf++; return -1; } return 1; } /* closekey */ void closekey(Key *k) { if(k == nil) return; if(--k->ref != 0) return; freeattr(k->attr); freeattr(k->privattr); k->attr = (void*)~1; k->privattr = (void*)~1; k->proto = nil; free(k); } static uchar* pstring(uchar *p, uchar *e, char *s) { uint n; if(p == nil) return nil; if(s == nil) s = ""; n = strlen(s); if(p+n+BIT16SZ >= e) return nil; PBIT16(p, n); p += BIT16SZ; memmove(p, s, n); p += n; return p; } static uchar* pcarray(uchar *p, uchar *e, uchar *s, uint n) { if(p == nil) return nil; if(s == nil){ if(n > 0) sysfatal("pcarray"); s = (uchar*)""; } if(p+n+BIT16SZ >= e) return nil; PBIT16(p, n); p += BIT16SZ; memmove(p, s, n); p += n; return p; } uchar* convAI2M(AuthInfo *ai, uchar *p, int n) { uchar *e = p+n; p = pstring(p, e, ai->cuid); p = pstring(p, e, ai->suid); p = pstring(p, e, ai->cap); p = pcarray(p, e, ai->secret, ai->nsecret); return p; } int failure(Fsstate *s, char *fmt, ...) { char e[ERRMAX]; va_list arg; if(fmt == nil) rerrstr(s->err, sizeof(s->err)); else { va_start(arg, fmt); snprint(e, sizeof e, fmt, arg); va_end(arg); strecpy(s->err, s->err+sizeof(s->err), e); errstr(e, sizeof e); } flog("%d: failure %s", s->seqnum, s->err); return RpcFailure; } static int hasqueries(Attr *a) { for(; a; a=a->next) if(a->type == AttrQuery) return 1; return 0; } char *ignored[] = { "role", }; static int ignoreattr(char *s) { int i; for(i=0; isysuser, owner) != 0){ werrstr("%q can't use %q's keys", fss->sysuser, owner); return failure(fss, nil); } break; } if(fmt){ va_start(arg, fmt); doprint(buf, buf+sizeof buf, fmt, arg); va_end(arg); attr1 = parseattr(buf); }else attr1 = nil; p = strfindattr(attr0, "proto"); if(p == nil) p = strfindattr(attr1, "proto"); if(p && findproto(p) == nil){ werrstr("unknown protocol %s", p); freeattr(attr1); return failure(fss, nil); } nmatch = 0; for(i=0; inkey; i++){ k = ring->key[i]; if(matchattr(attr0, k->attr, k->privattr) && matchattr(attr1, k->attr, k->privattr)){ if(nmatch++ < skip) continue; if(!(who&Knoconf)){ switch(canusekey(fss, k)){ case -1: freeattr(attr1); return RpcConfirm; case 0: continue; case 1: break; } } freeattr(attr1); k->ref++; *ret = k; return RpcOk; } } flog("%d: no key matches %A %A", fss->seqnum, attr0, attr1); werrstr("no key matches %A %A", attr0, attr1); s = RpcFailure; if(askforkeys && (hasqueries(attr0) || hasqueries(attr1))){ if(nmatch == 0){ attr0 = copyattr(attr0); for(l=&attr0; *l; l=&(*l)->next) ; *l = attr1; for(l=&attr0; *l; ){ if(ignoreattr(s_to_c((*l)->name))){ a = *l; *l = (*l)->next; a->next = nil; freeattr(a); }else l = &(*l)->next; } attr0 = sortattr(attr0); snprint(fss->keyinfo, sizeof fss->keyinfo, "%A", attr0); freeattr(attr0); attr1 = nil; /* attr1 was linked to attr0 */ }else fss->keyinfo[0] = '\0'; s = RpcNeedkey; } freeattr(attr1); if(s == RpcFailure) return failure(fss, nil); /* loads error string */ return s; } int findp9authkey(Key **k, Fsstate *fss) { char *dom; /* * We don't use fss->attr here because we don't * care about what the user name is set to, for instance. */ if(dom = strfindattr(fss->attr, "dom")) return findkey(k, fss, Kowner, 0, nil, "proto=p9sk1 dom=%q role=server user?", dom); else return findkey(k, fss, Kowner, 0, nil, "proto=p9sk1 role=server dom? user?"); } Proto* findproto(char *name) { int i; for(i=0; prototab[i]; i++) if(strcmp(name, prototab[i]->name) == 0) return prototab[i]; return nil; } char* getnvramkey(int flag) { char *s; Nvrsafe safe; memset(&safe, 0, sizeof safe); /* * readnvram can return -1 meaning nvram wasn't written, * but safe still holds good data. */ if(readnvram(&safe, flag)<0 && safe.authid[0]=='0') return nil; s = emalloc(512); fmtinstall('H', encodeconv); sprint(s, "key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", safe.authid, safe.authdom, DESKEYLEN, safe.machkey); writehostowner(safe.authid); return s; } int isclient(char *role) { if(role == nil){ werrstr("role not specified"); return -1; } if(strcmp(role, "server") == 0) return 0; if(strcmp(role, "client") == 0) return 1; werrstr("unknown role %q", role); return -1; } static int hasname(Attr *a0, Attr *a1, char *name) { return findattr(a0, name) || findattr(a1, name); } static int hasnameval(Attr *a0, Attr *a1, char *name, char *val) { Attr *a; for(a=findattr(a0, name); a; a=findattr(a->next, name)) if(strcmp(s_to_c(a->val), val) == 0) return 1; for(a=findattr(a1, name); a; a=findattr(a->next, name)) if(strcmp(s_to_c(a->val), val) == 0) return 1; return 0; } int matchattr(Attr *pat, Attr *a0, Attr *a1) { int type; for(; pat; pat=pat->next){ type = pat->type; if(ignoreattr(s_to_c(pat->name))) type = AttrDefault; switch(type){ case AttrQuery: /* name=something be present */ if(!hasname(a0, a1, s_to_c(pat->name))) return 0; break; case AttrNameval: /* name=val must be present */ if(!hasnameval(a0, a1, s_to_c(pat->name), s_to_c(pat->val))) return 0; break; case AttrDefault: /* name=val must be present if name=anything is present */ if(hasname(a0, a1, s_to_c(pat->name)) && !hasnameval(a0, a1, s_to_c(pat->name), s_to_c(pat->val))) return 0; break; } } return 1; } /* * create a change uid capability */ char* mkcap(char *user) { uchar rand[20]; char *cap; char *key; int nuser; uchar hash[SHA1dlen]; int fd; if(strchr(user, '@') != nil) return nil; /* create the capability */ nuser = strlen(user); cap = emalloc(nuser+1+sizeof(rand)*3+1); strcpy(cap, user); cap[nuser] = '@'; memrandom(rand, sizeof(rand)); key = cap+nuser+1; enc64(key, sizeof(rand)*3, rand, sizeof(rand)); /* hash the capability */ hmac_sha1((uchar*)user, strlen(user), (uchar*)key, strlen(key), hash, nil); /* give the kernel the hash */ fd = open("#ยค/caphash", OWRITE); if(fd < 0){ free(cap); return nil; } if(write(fd, hash, SHA1dlen) < 0){ free(cap); close(fd); return nil; } close(fd); return cap; } int phaseerror(Fsstate *s, char *op) { char tmp[32]; werrstr("protocol phase error: %s in state %s", op, phasename(s, s->phase, tmp)); return RpcPhase; } char* phasename(Fsstate *fss, int phase, char *tmp) { char *name; if(fss->phase == Broken) name = "Broken"; else if(phase == Established) name = "Established"; else if(phase == Notstarted) name = "Notstarted"; else if(phase < 0 || phase >= fss->maxphase || (name = fss->phasename[phase]) == nil){ sprint(tmp, "%d", phase); name = tmp; } return name; } static int outin(char *prompt, char *def, int len) { String *s; s = readcons(prompt, def, 0); if(s == nil) return -1; strncpy(def, s_to_c(s), len); def[len-1] = 0; s_free(s); return strlen(def); } /* * get host owner and set it */ void promptforhostowner(void) { char owner[64], *p; /* hack for bitsy; can't prompt during boot */ if(p = getenv("user")){ writehostowner(p); return; } strcpy(owner, "none"); do{ outin("user", owner, sizeof(owner)); } while(*owner == 0); writehostowner(owner); } /* * Insert a key into the keyring. * If the public attributes are identical to some other key, replace that one. */ int replacekey(Key *kn) { int i; Key *k; for(i=0; inkey; i++){ k = ring->key[i]; if(matchattr(kn->attr, k->attr, nil) && matchattr(k->attr, kn->attr, nil)){ closekey(k); kn->ref++; ring->key[i] = kn; return 0; } } if(ring->nkey%16 == 0) ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0])); kn->ref++; ring->key[ring->nkey++] = kn; return 0; } char* safecpy(char *to, char *from, int n) { memset(to, 0, n); if(n == 1) return to; strncpy(to, from, n-1); return to; } int secdial(void) { char *p, buf[80], *f[3]; int fd, nf; bindnetcs(); p = secstore; /* take it from writehostowner, if set there */ if(*p == 0) /* else use the authserver */ p = "$auth"; fd = dial(netmkaddr(p, "tcp", "5356"), 0, 0, 0); if(fd >= 0) return fd; /* if cs isn't running, try the auth server */ if(access("/net/cs", AEXIST) >= 0) return -1; if(authaddr == nil) return -1; /* * authaddr is something like il!host!566 or tcp!host!567. * extract host, accounting for a change of format to something * like il!host or tcp!host or host. */ safecpy(buf, authaddr, sizeof buf); nf = getfields(buf, f, nelem(f), 0, "!"); switch(nf){ default: return -1; case 1: p = f[0]; break; case 2: case 3: p = f[1]; break; } fd = dial(netmkaddr(p, "tcp", "5356"), 0, 0, 0); if(fd >= 0) return fd; return -1; } Attr* setattr(Attr *a, char *fmt, ...) { char buf[1024]; va_list arg; Attr *b; va_start(arg, fmt); doprint(buf, buf+sizeof buf, fmt, arg); va_end(arg); b = parseattr(buf); a = setattrs(a, b); setmalloctag(a, getcallerpc(&a)); freeattr(b); return a; } /* * add attributes in list b to list a. If any attributes are in * both lists, replace those in a by those in b. */ Attr* setattrs(Attr *a, Attr *b) { int found; Attr **l, *freea; for(; b; b=b->next){ found = 0; for(l=&a; *l; ){ if(strcmp(s_to_c(b->name), s_to_c((*l)->name)) == 0){ switch(b->type){ case AttrNameval: if(!found){ found = 1; s_free((*l)->val); (*l)->val = s_incref(b->val); (*l)->type = AttrNameval; l = &(*l)->next; }else{ freea = *l; *l = (*l)->next; freea->next = nil; freeattr(freea); } break; case AttrQuery: found++; break; } }else l = &(*l)->next; } if(found == 0){ *l = mkattr(b->type, s_to_c(b->name), s_to_c(b->val), nil); setmalloctag(*l, getcallerpc(&a)); } } return a; } void setmalloctaghere(void *v) { setmalloctag(v, getcallerpc(&v)); } Attr* sortattr(Attr *a) { int i; Attr *anext, *a0, *a1, **l; if(a == nil || a->next == nil) return a; /* cut list in halves */ a0 = nil; a1 = nil; i = 0; for(; a; a=anext){ anext = a->next; if(i++%2){ a->next = a0; a0 = a; }else{ a->next = a1; a1 = a; } } /* sort */ a0 = sortattr(a0); a1 = sortattr(a1); /* merge */ l = &a; while(a0 || a1){ if(a1==nil){ anext = a0; a0 = a0->next; }else if(a0==nil){ anext = a1; a1 = a1->next; }else if(strcmp(s_to_c(a0->name), s_to_c(a1->name)) < 0){ anext = a0; a0 = a0->next; }else{ anext = a1; a1 = a1->next; } *l = anext; l = &(*l)->next; } *l = nil; return a; } char* strfindattr(Attr *a, char *n) { a = findattr(a, n); if(a == nil) return nil; return s_to_c(a->val); } int toosmall(Fsstate *fss, uint n) { fss->rpc.nwant = n; return RpcToosmall; } void writehostowner(char *owner) { int fd; char *s; if((s = strchr(owner,'@')) != nil){ *s++ = 0; strncpy(secstore, s, (sizeof secstore)-1); } fd = open("#c/hostowner", OWRITE); if(fd >= 0){ if(fprint(fd, "%s", owner) < 0) fprint(2, "setting #c/hostowner to %q: %r\n", owner); close(fd); } }