123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- #include <errno.h>
- #include <fcntl.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <time.h>
- #include <unistd.h>
- #include "util.h"
- int dflag = 0;
- #define errstr (strerror(errno))
- static void sysfatal(char *fmt, ...);
- static void say(char *fmt, ...);
- static void warn(char *fmt, ...);
- enum {
- ANamelen = 28,
- ADomlen = 48,
- AErrlen = 64,
- Challen = 8,
- Secretlen = 32,
- Ticketreqlen = 1+ANamelen+ADomlen+Challen+ANamelen+ANamelen,
- Ticketlen = 1+Challen+2*ANamelen+Deskeylen,
- Passreqlen = 1+2*ANamelen+1+Secretlen,
- ATreq = 1,
- APass = 3,
- AOk = 4,
- AErr = 5,
- ATs = 64,
- ATc = 65,
- ATp = 68,
- };
- typedef struct User User;
- struct User
- {
- int keyok;
- uchar key[Deskeylen];
- int status;
- int expire;
- };
- typedef struct Ticketreq Ticketreq;
- typedef struct Ticket Ticket;
- typedef struct Passreq Passreq;
- struct Ticketreq
- {
- char which;
- char authid[ANamelen+1];
- char authdom[ADomlen+1];
- uchar chal[Challen];
- char hostid[ANamelen+1];
- char uid[ANamelen+1];
- };
- struct Ticket
- {
- uchar which;
- uchar chal[Challen];
- char cuid[ANamelen+1];
- char suid[ANamelen+1];
- uchar key[Deskeylen];
- };
- struct Passreq
- {
- int which;
- char oldpw[ANamelen+1];
- char newpw[ANamelen+1];
- int changesecret;
- char secret[Secretlen+1];
- };
- static void transact(void);
- static void
- usage(void)
- {
- fprint(2, "usage: authsrv9 [-d]\n");
- exit(1);
- }
- int
- main(int argc, char *argv[])
- {
- int c;
- randominit();
- openlog("authsrv9", 0, LOG_AUTH);
- while((c = getopt(argc, argv, "d")) != -1)
- switch(c) {
- case 'd':
- dflag++;
- break;
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
- if(argc != 0)
- usage();
- /* openbsd's inetd redirects stderr to stdout (the network)... */
- close(2);
- open("/dev/null", O_WRONLY);
- alarm(2*60);
- for(;;)
- transact();
- }
- static void
- ewrite(uchar *p, int n)
- {
- if(write(1, p, n) != n)
- sysfatal("write: %s", errstr);
- say("wrote %d bytes", n);
- }
- static void
- autherror(int fatal, char *err)
- {
- uchar buf[1+AErrlen];
- say("autherror, fatal %d, err %s", fatal, err);
- memset(buf, 0, sizeof buf);
- buf[0] = AErr;
- memmove(buf+1, err, min(strlen(err), AErrlen));
- ewrite(buf, sizeof buf);
- if(fatal)
- exit(1);
- }
- static int
- readfile(char *path, void *buf, int buflen, int exact)
- {
- int fd;
- int n;
- memset(buf, 0, buflen);
- fd = open(path, O_RDONLY);
- if(fd < 0)
- return -1;
- n = readn(fd, buf, buflen);
- close(fd);
- if(n < 0)
- return -1;
- if(!exact && n == buflen)
- return -1;
- if(exact && n != buflen)
- return -1;
- return n;
- }
- static int
- writefile(char *path, uchar *buf, int buflen)
- {
- int fd;
- int n;
- fd = open(path, O_WRONLY|O_CREAT|O_TRUNC);
- if(fd < 0)
- return -1;
- n = write(fd, buf, buflen);
- close(fd);
- return n;
- }
- static int
- getinfo(char **authid, char **authdom)
- {
- char aid[ANamelen+1], adom[ANamelen+1];
- int naid, nadom;
- naid = readfile("/auth/authid", aid, sizeof aid-1, 0);
- if(naid < 0)
- return -1;
- nadom = readfile("/auth/authdom", adom, sizeof adom-1, 0);
- if(nadom < 0)
- return -1;
- aid[naid] = '\0';
- adom[nadom] = '\0';
- *authid = estrdup(aid);
- *authdom = estrdup(adom);
- return 0;
- }
- static void
- getuserinfo(char *uid, User *u)
- {
- char pre[128];
- char path[128];
- char buf[128];
- User t;
- char *e;
- uint expire;
- snprintf(pre, sizeof pre, "/auth/users/%s", uid);
- u->keyok = 0;
- memset(u->key, 0, sizeof u->key);
- u->status = 0;
- u->expire = 1;
- snprintf(path, sizeof path, "%s/key", pre);
- if(readfile(path, t.key, sizeof t.key, 1) < 0)
- return;
- snprintf(path, sizeof path, "%s/status", pre);
- if(readfile(path, buf, sizeof buf, 0) < 0)
- return;
- t.status = 0;
- if(eq(buf, "ok"))
- t.status = 1;
- else if(!eq(buf, "disabled"))
- warn("bad status in %s: %s", path, buf);
- snprintf(path, sizeof path, "%s/expire", pre);
- if(readfile(path, buf, sizeof buf, 0) < 0)
- return;
- t.expire = 1;
- if(eq(buf, "never")) {
- t.expire = 0;
- } else {
- expire = (uint)strtol(buf, &e, 10);
- if(*e == '\0')
- t.expire = expire;
- else
- warn("bad expire in %s: %s (remainder %s)", path, buf, e);
- }
- t.keyok = 1;
- memmove(u, &t, sizeof t);
- }
- static int
- move(void *to, void *from, int n)
- {
- memmove(to, from, n);
- return n;
- }
- static void
- ticketrequnpack(Ticketreq *tr, uchar *p)
- {
- memset(tr, 0, sizeof tr[0]);
- tr->which = *p++;
- p += move(tr->authid, p, ANamelen);
- p += move(tr->authdom, p, ADomlen);
- p += move(tr->chal, p, Challen);
- p += move(tr->hostid, p, ANamelen);
- p += move(tr->uid, p, ANamelen);
- }
- static void
- ticketmk(Ticket *t, int which, uchar *chal, char *hostid, char *idr, uchar *key)
- {
- memset(t, 0, sizeof (Ticket));
- t->which = which;
- memmove(t->chal, chal, sizeof t->chal);
- strcpy(t->cuid, hostid);
- strcpy(t->suid, idr);
- memmove(t->key, key, sizeof t->key);
- }
- static void
- ticketpack(Ticket *t, uchar *buf)
- {
- uchar *p;
- p = buf;
- *p++ = t->which;
- p += move(p, t->chal, Challen);
- p += move(p, t->cuid, ANamelen);
- p += move(p, t->suid, ANamelen);
- p += move(p, t->key, Deskeylen);
- }
- static void
- genkey(uchar *p)
- {
- randombuf(p, Deskeylen);
- }
- static void
- clear(void *p, int n)
- {
- memset(p, 0, n);
- }
- int
- allowed(char *uid)
- {
- char *path;
- char buf[2*1024+1];
- int fd;
- int n;
- char *p, *e;
- path = "/auth/badusers";
- fd = open(path, O_RDONLY);
- if(fd < 0) {
- warn("open %s: %s", path, errstr);
- return 0;
- }
- n = readn(fd, buf, sizeof buf-1);
- if(n < 0) {
- warn("read %s: %s", path, errstr);
- return 0;
- }
- if(n == sizeof buf) {
- warn("%s too long", path);
- return 0;
- }
- buf[n] = '\0';
- p = buf;
- for(;;) {
- e = strchr(p, '\n');
- if(e == nil)
- break;
- *e = '\0';
- if(strcmp(p, uid) == 0)
- return 0;
- p = e+1;
- }
- return 1;
- }
- static void
- authtreq(Ticketreq *tr)
- {
- char *authid, *authdom;
- User au, u;
- uchar ks[Deskeylen], kc[Deskeylen], kn[Deskeylen];
- uint now;
- char idr[ANamelen+1];
- Ticket tc, ts;
- uchar tcbuf[Ticketlen], tsbuf[Ticketlen];
- uchar tresp[1+Ticketlen+Ticketlen];
- int cok, sok;
- char *msg;
- /* # C->A: AuthTreq, IDs, DN, CHs, IDc, IDr */
- if(getinfo(&authid, &authdom) < 0) {
- warn("could not get authid & authdom");
- sysfatal("could not get authid & authdom");
- }
- now = time(nil);
- getuserinfo(authid, &au);
- say("authid, keyok %d, status %d, expire %u, now %u\n", au.keyok, au.status, au.expire, now);
- genkey(ks);
- sok = eq(tr->authid, authid) && eq(tr->authdom, authdom) && au.keyok && au.status && (au.expire == 0 || now > au.expire);
- getuserinfo(tr->hostid, &u);
- say("hostid '%s', user '%s', keyok %d, status %d, expire %u, now %u\n", tr->hostid, tr->uid, u.keyok, u.status, u.expire, now);
- genkey(kc);
- cok = u.keyok && u.status && (u.expire == 0 || u.expire > now);
- if(sok && cok) {
- memmove(ks, au.key, Deskeylen);
- memmove(kc, u.key, Deskeylen);
- }
- /* A->C: AuthOK, Kc{AuthTc, CHs, IDc, IDr, Kn}, Ks{AuthTs, CHs, IDc, IDr, Kn} */
- msg = "";
- strcpy(idr, "");
- if(eq(tr->hostid, tr->uid) || (eq(tr->hostid, authid) && allowed(tr->uid)))
- strcpy(idr, tr->uid);
- else
- msg = ", but hostid does not speak for uid";
- warn("ticketreq %s: authid %s, remote hostid %s, uid %s%s", (sok&&cok) ? "ok" : "bad", authid, tr->hostid, tr->uid, msg);
- genkey(kn);
- ticketmk(&tc, ATc, tr->chal, tr->hostid, idr, kn);
- ticketpack(&tc, tcbuf);
- authencrypt(kc, tcbuf, Ticketlen);
- if(0)say("ticket, cipher %s", hex(tcbuf, Ticketlen));
- ticketmk(&ts, ATs, tr->chal, tr->hostid, idr, kn);
- ticketpack(&ts, tsbuf);
- authencrypt(ks, tsbuf, Ticketlen);
- if(0)say("ticket, cipher %s", hex(tsbuf, Ticketlen));
- clear(kc, sizeof kc);
- clear(ks, sizeof ks);
- clear(kn, sizeof kn);
- clear(&tc, sizeof tc);
- clear(&ts, sizeof ts);
- clear(&au, sizeof au);
- clear(&u, sizeof u);
- tresp[0] = AOk;
- memmove(tresp+1, tcbuf, sizeof tcbuf);
- memmove(tresp+1+sizeof tcbuf, tsbuf, sizeof tsbuf);
- ewrite(tresp, sizeof tresp);
- }
- static void
- passwordrequnpack(Passreq *pr, uchar *buf)
- {
- uchar *p;
- memset(pr, 0, sizeof pr[0]);
- p = buf;
- pr->which = *p++;
- p += move(pr->oldpw, p, ANamelen);
- p += move(pr->newpw, p, ANamelen);
- pr->changesecret = *p++;
- p += move(pr->secret, p, Secretlen);
- }
- static char*
- checkpass(char *pw)
- {
- if(strlen(pw) < 8)
- return "bad password: too short";
- if(strlen(pw) > ANamelen)
- return "bad password: too long";
- return nil;
- }
- static void
- authpass(Ticketreq *tr)
- {
- User u;
- uint now;
- uchar kc[Deskeylen], kn[Deskeylen];
- Ticket tp;
- uchar tresp[1+Ticketlen];
- uchar prbuf[Passreqlen];
- Passreq pr;
- uchar okey[Deskeylen], nkey[Deskeylen];
- char *badpw;
- char path[128];
- int n;
- /*
- * C->A: AuthPass, IDc, DN, CHc, IDc, IDc
- * (request to change pass for tr->uid)
- */
- getuserinfo(tr->uid, &u);
- /* A->C: Kc{AuthTp, CHc, IDc, IDc, Kn} */
- now = time(nil);
- say("authpass for user %s, u.keyok %d, u.status %d, u.expire %u, now %u", tr->uid, u.keyok, u.status, u.expire, now);
- genkey(kc);
- if(u.keyok && u.status && (u.expire == 0 || now > u.expire))
- memmove(kc, u.key, Deskeylen);
- clear(&u, sizeof u);
- genkey(kn);
- ticketmk(&tp, ATp, tr->chal, tr->uid, tr->uid, kn);
- tresp[0] = AOk;
- ticketpack(&tp, tresp+1);
- clear(&tp, sizeof tp);
- authencrypt(kc, tresp+1, Ticketlen);
- ewrite(tresp, sizeof tresp);
- for(;;) {
- /* C->A: Kn{AuthPass, old, new, changesecret, secret} */
- n = readn(0, prbuf, sizeof prbuf);
- if(n < 0)
- sysfatal("read passwordreq: %s", errstr);
- if(n != sizeof prbuf)
- sysfatal("short read for password request, want %d, got %d", sizeof prbuf, n);
- authdecrypt(kn, prbuf, sizeof prbuf);
- passwordrequnpack(&pr, prbuf);
- clear(prbuf, sizeof prbuf);
- if(pr.which != APass) {
- warn("pass change: for uid %s: wrong message type, want %d, saw %d (wrong password used)", tr->uid, APass, pr.which);
- clear(&pr, sizeof pr);
- clear(kc, sizeof kc);
- autherror(1, "wrong message type for Passreq");
- }
- if(pr.changesecret) {
- warn("pass change: uid %s tried to change apop secret, not supported", tr->uid);
- autherror(0, "changing apop secret not supported");
- clear(&pr, sizeof pr);
- continue;
- }
- passtokey(okey, pr.oldpw);
- passtokey(nkey, pr.newpw);
- if(!memeq(kc, okey, sizeof okey)) {
- clear(&pr, sizeof pr);
- clear(okey, sizeof okey);
- clear(nkey, sizeof nkey);
- warn("pass change: uid %s gave bad old password", tr->uid);
- autherror(0, "bad old password");
- continue;
- }
- badpw = checkpass(pr.newpw);
- if(badpw != nil) {
- clear(&pr, sizeof pr);
- clear(okey, sizeof okey);
- clear(nkey, sizeof nkey);
- warn("pass change: uid %s gave bad new password: %s", tr->uid, badpw);
- autherror(0, badpw);
- continue;
- }
- snprintf(path, sizeof path, "/auth/users/%s/key", tr->uid);
- if(writefile(path, nkey, Deskeylen) < 0) {
- clear(&pr, sizeof pr);
- clear(okey, sizeof okey);
- clear(nkey, sizeof nkey);
- clear(kc, sizeof kc);
- warn("pass change: storing new key for user %s failed: %s", tr->uid, errstr);
- autherror(1, "storing new key failed");
- }
- clear(&pr, sizeof pr);
- clear(okey, sizeof okey);
- clear(nkey, sizeof nkey);
- clear(kc, sizeof kc);
- warn("pass change: password changed for user %s", tr->uid);
- /* A->C: AuthOK or AuthErr, 64-byte error message */
- tresp[0] = AOk;
- ewrite(tresp, 1);
- return;
- }
- }
- static void
- transact(void)
- {
- Ticketreq tr;
- uchar trbuf[Ticketreqlen];
- int n;
- say("reading ticketrequest");
- /* read ticket */
- n = readn(0, trbuf, sizeof trbuf);
- if(n < 0)
- sysfatal("read ticketreq: %s", errstr);
- if(n == 0)
- exit(0);
- if(n != sizeof trbuf)
- sysfatal("read ticketreq, want %d, got %d", sizeof trbuf, n);
- ticketrequnpack(&tr, trbuf);
- say("have ticketreq, which %d, authid %s authdom %s, chal %s hostid %s uid %s", tr.which, tr.authid, tr.authdom, hex(tr.chal, Challen), tr.hostid, tr.uid);
- switch(tr.which) {
- case ATreq:
- authtreq(&tr);
- break;
- case APass:
- authpass(&tr);
- break;
- default:
- autherror(1, "not supported");
- break;
- }
- }
- static void
- log(int level, char *fmt, va_list ap)
- {
- char *p;
- if(vasprintf(&p, fmt, ap) < 0)
- return;
- syslog(level, "%s (%s)", p, remoteaddr(0));
- free(p);
- }
- static void
- sysfatal(char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- log(LOG_NOTICE, fmt, ap);
- va_end(ap);
- exit(1);
- }
- static void
- warn(char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- log(LOG_WARNING, fmt, ap);
- va_end(ap);
- }
- static void
- say(char *fmt, ...)
- {
- va_list ap;
- if(!dflag)
- return;
- va_start(ap, fmt);
- log(LOG_INFO, fmt, ap);
- va_end(ap);
- }
|