From 2dae1ed53a73d81bfb86778793a6bda265d5140d Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Wed, 6 Jan 2016 03:09:00 +0100 Subject: [PATCH] auth: release dp9ik implementation and reentrant factotum --- sys/include/authsrv.h | 59 +++- sys/man/6/authsrv | 346 ++++++++++++++++++---- sys/src/cmd/auth/authcmdlib.h | 1 + sys/src/cmd/auth/authsrv.c | 425 +++++++++++++++------------- sys/src/cmd/auth/convkeys.c | 2 + sys/src/cmd/auth/debug.c | 92 ++++-- sys/src/cmd/auth/factotum/apop.c | 24 +- sys/src/cmd/auth/factotum/chap.c | 23 +- sys/src/cmd/auth/factotum/confirm.c | 55 +++- sys/src/cmd/auth/factotum/dat.h | 25 +- sys/src/cmd/auth/factotum/fs.c | 43 ++- sys/src/cmd/auth/factotum/log.c | 8 +- sys/src/cmd/auth/factotum/p9any.c | 32 ++- sys/src/cmd/auth/factotum/p9cr.c | 27 +- sys/src/cmd/auth/factotum/p9sk1.c | 381 ++++++++++++++++--------- sys/src/cmd/auth/factotum/rpc.c | 15 +- sys/src/cmd/auth/factotum/util.c | 105 +++++-- sys/src/cmd/auth/keyfs.c | 40 ++- sys/src/cmd/auth/lib/mkfile | 1 + sys/src/cmd/auth/lib/readwrite.c | 10 +- sys/src/cmd/auth/netkey.c | 7 +- sys/src/cmd/auth/passwd.c | 39 ++- sys/src/libauth/auth_userpasswd.c | 13 +- sys/src/libauthsrv/_asgetpakkey.c | 26 ++ sys/src/libauthsrv/_asgetresp.c | 38 ++- sys/src/libauthsrv/_asgetticket.c | 34 ++- sys/src/libauthsrv/authpak.c | 214 ++++++++++++++ sys/src/libauthsrv/convA2M.c | 33 ++- sys/src/libauthsrv/convM2A.c | 43 +-- sys/src/libauthsrv/convM2PR.c | 45 +-- sys/src/libauthsrv/convM2T.c | 60 ++-- sys/src/libauthsrv/convM2TR.c | 20 +- sys/src/libauthsrv/convPR2M.c | 32 ++- sys/src/libauthsrv/convT2M.c | 38 ++- sys/src/libauthsrv/convTR2M.c | 19 +- sys/src/libauthsrv/decaf.mp | 49 ++++ sys/src/libauthsrv/ed448.mp | 11 + sys/src/libauthsrv/edwards.mp | 40 +++ sys/src/libauthsrv/elligator2.mp | 30 ++ sys/src/libauthsrv/form1.c | 90 ++++++ sys/src/libauthsrv/mkfile | 22 +- sys/src/libauthsrv/msqrt.mp | 100 +++++++ sys/src/libauthsrv/passtokey.c | 14 +- sys/src/libauthsrv/spake2ee.mp | 35 +++ 44 files changed, 2034 insertions(+), 732 deletions(-) create mode 100644 sys/src/libauthsrv/_asgetpakkey.c create mode 100644 sys/src/libauthsrv/authpak.c create mode 100644 sys/src/libauthsrv/decaf.mp create mode 100644 sys/src/libauthsrv/ed448.mp create mode 100644 sys/src/libauthsrv/edwards.mp create mode 100644 sys/src/libauthsrv/elligator2.mp create mode 100644 sys/src/libauthsrv/form1.c create mode 100644 sys/src/libauthsrv/msqrt.mp create mode 100644 sys/src/libauthsrv/spake2ee.mp diff --git a/sys/include/authsrv.h b/sys/include/authsrv.h index 1808f1716..b423992c1 100644 --- a/sys/include/authsrv.h +++ b/sys/include/authsrv.h @@ -20,16 +20,27 @@ enum AERRLEN= 64, /* errstr max size in previous proto */ DOMLEN= 48, /* authentication domain name length */ DESKEYLEN= 7, /* encrypt/decrypt des key length */ - AESKEYLEN= 16, + AESKEYLEN= 16, /* encrypt/decrypt aes key length */ + CHALLEN= 8, /* plan9 sk1 challenge length */ NETCHLEN= 16, /* max network challenge length (used in AS protocol) */ CONFIGLEN= 14, SECRETLEN= 32, /* secret max size */ + NONCELEN= 32, + KEYDBOFF= 8, /* bytes of random data at key file's start */ OKEYDBLEN= ANAMELEN+DESKEYLEN+4+2, /* old key file entry length */ KEYDBLEN= OKEYDBLEN+SECRETLEN, /* key file entry length */ OMD5LEN= 16, + + /* AuthPAK constants */ + PAKKEYLEN= 32, + PAKSLEN= (448+7)/8, /* ed448 scalar */ + PAKPLEN= 4*PAKSLEN, /* point in extended format X,Y,Z,T */ + PAKHASHLEN= 2*PAKPLEN, /* hashed points PM,PN */ + PAKXLEN= PAKSLEN, /* random scalar secret key */ + PAKYLEN= PAKSLEN, /* decaf encoded public key */ }; /* encryption numberings (anti-replay) */ @@ -48,8 +59,7 @@ enum AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */ AuthHttp=13, /* http domain login */ AuthVNC=14, /* VNC server login (deprecated) */ - - + AuthPAK=19, /* authenticated diffie hellman key agreement */ AuthTs=64, /* ticket encrypted with server's key */ AuthTc, /* ticket encrypted with client's key */ AuthAs, /* server generated authenticator */ @@ -75,17 +85,19 @@ struct Ticket char chal[CHALLEN]; /* server challenge */ char cuid[ANAMELEN]; /* uid on client */ char suid[ANAMELEN]; /* uid on server */ - char key[DESKEYLEN]; /* nonce DES key */ + uchar key[NONCELEN]; /* nonce key */ + + char form; /* (not transmitted) format (0 = des, 1 = ccpoly) */ }; -#define TICKETLEN (CHALLEN+2*ANAMELEN+DESKEYLEN+1) +#define MAXTICKETLEN (12+CHALLEN+2*ANAMELEN+NONCELEN+16) struct Authenticator { char num; /* replay protection */ - char chal[CHALLEN]; - ulong id; /* authenticator id, ++'d with each auth */ + char chal[CHALLEN]; /* server/client challenge */ + uchar rand[NONCELEN]; /* server/client nonce */ }; -#define AUTHENTLEN (CHALLEN+4+1) +#define MAXAUTHENTLEN (12+CHALLEN+NONCELEN+16) struct Passwordreq { @@ -95,7 +107,7 @@ struct Passwordreq char changesecret; char secret[SECRETLEN]; /* new secret */ }; -#define PASSREQLEN (2*ANAMELEN+1+1+SECRETLEN) +#define MAXPASSREQLEN (12+2*ANAMELEN+1+SECRETLEN+16) struct OChapreply { @@ -115,8 +127,10 @@ struct OMSchapreply struct Authkey { - char des[DESKEYLEN]; - uchar aes[AESKEYLEN]; + char des[DESKEYLEN]; /* DES key from password */ + uchar aes[AESKEYLEN]; /* AES key from password */ + uchar pakkey[PAKKEYLEN]; /* shared key from AuthPAK exchange (see authpak_finish()) */ + uchar pakhash[PAKHASHLEN]; /* secret hash from AES key and user name (see authpak_hash()) */ }; /* @@ -132,10 +146,13 @@ extern int convPR2M(Passwordreq*, char*, int, Ticket*); extern int convM2PR(char*, int, Passwordreq*, Ticket*); /* - * convert ascii password to DES key + * convert ascii password to auth key */ extern void passtokey(Authkey*, char*); +extern void passtodeskey(char key[DESKEYLEN], char *p); +extern void passtoaeskey(uchar key[AESKEYLEN], char *p); + /* * Nvram interface */ @@ -169,7 +186,7 @@ struct Nvrsafe }; extern uchar nvcsum(void*, int); -extern int readnvram(Nvrsafe*, int); +extern int readnvram(Nvrsafe*, int); /* * call up auth server @@ -179,7 +196,23 @@ extern int authdial(char *netroot, char *authdom); /* * exchange messages with auth server */ +extern int _asgetpakkey(int, Ticketreq*, Authkey*); extern int _asgetticket(int, Ticketreq*, char*, int); extern int _asrequest(int, Ticketreq*); extern int _asgetresp(int, Ticket*, Authenticator*, Authkey *); extern int _asrdresp(int, char*, int); + +/* + * AuthPAK protocol + */ +typedef struct PAKpriv PAKpriv; +struct PAKpriv +{ + int isclient; + uchar x[PAKXLEN]; + uchar y[PAKYLEN]; +}; + +extern void authpak_hash(Authkey *k, char *u); +extern void authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient); +extern int authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN]); diff --git a/sys/man/6/authsrv b/sys/man/6/authsrv index 4ff5057b2..513657990 100644 --- a/sys/man/6/authsrv +++ b/sys/man/6/authsrv @@ -1,6 +1,6 @@ .TH AUTHSRV 6 .SH NAME -authsrv, p9any, p9sk1, p9sk2 \- authentication protocols +authsrv, p9any, p9sk1, dp9ik \- authentication protocols .SH DESCRIPTION This manual page describes the protocols used to authorize connections, confirm the identities @@ -14,8 +14,9 @@ The network database holds for each public machine, such as a CPU server or file server, the name of the authentication server that machine uses. .PP -Each machine contains three values important to authentication; a 56-bit DES -key, a 28-byte authentication ID, and a 48-byte authentication domain name. +Each machine contains four values important to authentication; a 56-bit DES +key, a 128-bit AES key, a 28-byte authentication ID, and a 48-byte authentication +domain name. The ID is a user name and identifies who is currently responsible for the kernel running on that machine. The domain name identifies the machines across which the ID is valid. @@ -29,7 +30,7 @@ The password is converted using .I passtokey (see .IR authsrv (2)) -into a 56-bit DES key and saved in memory. +into a 56-bit DES and 128-bit AES keys and saved in memory. The authentication domain is set to the null string. If possible, .I factotum @@ -60,7 +61,7 @@ client's host ID's key a nonce key created for a ticket .RB ( key ) .TP -.IR K { m } +.I K{m} message .I m encrypted with key @@ -91,6 +92,24 @@ client's ID client's desired ID on server .RB ( uid , .BR suid ) +.TP +.I YAc +client \(-> AS DH public key +.TP +.I YBc +AS \(-> client DH public key +.TP +.I YAs +server \(-> AS DH public key +.TP +.I YBs +AS \(-> server DH public key +.TP +.I RNc +client's 32-byte random string +.TP +.I RNs +server's 32-byte random string .PD .PP The parenthesized names are the ones used in the @@ -112,8 +131,9 @@ The message type constants .IR AuthChap , .IR AuthMSchap , .IR AuthCram , +.IR AuthVNC , and -.IR AuthVNC +.IR AuthPAK .RB ( type ) are defined in .BR , @@ -142,7 +162,6 @@ The protocol to obtain a ticket pair is: .IR CHs , .IR IDc , .IR IDr -.sp -\n(PDu .TP .IR A \(-> C .IR AuthOK , @@ -265,16 +284,254 @@ proving to the client that it also knows .I Kn and therefore .I Ks . -.PD +.SS "Password authenticated key exchange" +Initially, the server and client keys +.I Ks +and +.I Kc +where equivalent to the password derived 56-bit DES keys, which +made the encrypted tickets subject to offline dictionary attacks +and provides a too small key space against brute force attacks +on current hardware. .PP -.I P9sk2 -is an older variant of +The +.I AuthPAK +protocol is used to establish new 256-bit random keys with the +AS for +.I Ks +and +.I Kc +before each ticket request on the connection. +.PP +The protocol is based on SPAKE2EE, where a hash of the user's secret +is used to encypt the public keys of a Elliptic-Curve Diffie-Hellman +key exchange. The user's +.I ID +and 128-bit AES key is hashed and mapped (using Elligator2) +into two curve points +.I PM +and +.IR PN , +called the +.IR pakhash . +Both sides generate a random number +.IR xa / xb +and make the public keys +.IR YA / YB +as: +.IR YA = xa*G+PM , +.IR YB = xb*G+PN . +After the public keys have been exchanged, each side calculates the +shared secret as: +.IR Z = xa*(YB-PN) = xb*(YA-PM) . +The shared secret +.I Z +is then hashed with the transmitted public keys +.IR YA | YB +producing the 256-bit +.IR pakkey . +.PP +The +.I pakkey +is then used in place of +.I Ks +and +.I Kc +to authenticate and encrypt tickets from the AS using +Chacha20/Poly1305 AEAD for the next following +request made on the connection. +.PP +The protocol (for +.IR AuthTreq ) +to establish keys +.I Ks +and +.I Kc +with the AS for +.I IDs +and +.I IDc +is: +.TP +.IR C \(-> A +.IR AuthPAK , +.IR IDs , +.IR DN , +.IR CHs , +.IR IDc , +.IR IDr , +.IR YAs , +.I YAc +.TP +.IR A \(-> C +.IR AuthOK , +.IR YBs , +.I YBc +.PP +The protocol (for +.IR AuthApop , +.IR AuthChap ...) +to establish a single server key +.I Ks +for +.IR IDs : +.TP +.IR C \(-> A +.IR AuthPAK , +.IR \- , +.IR DN , +.IR CHs , +.IR IDs , +.IR IDc , +.I YAs +.TP +.IR A \(-> C +.IR AuthOK , +.I YBs +.PP +The protocol (for +.IR AuthPass ) +to establish a single client key +.I Kc +for +.IR IDc : +.TP +.IR C \(-> A +.IR AuthPAK , +.IR \- , +.IR \- , +.IR CHc , +.IR \- , +.IR IDc , +.I YAc +.TP +.IR A \(-> C +.IR AuthOK , +.I YBc +.SS "Dp9ik" +The +.I dp9ik +protocol is an extended version of .I p9sk1 -used only when connecting to pre-9P2000 remote -execution services. -It omits the first message and last -messages and therefore does not -authenticate the server to the client. +that adds the random strings +.I RNc +and +.I RNs +in the +.I authenticator +messages for the session key derivation and uses the +password authenticated key exchange as described above +to derive the ticket encryption keys +.I Ks +and +.IR Kc : +.TP +.IR C \(-> S +.I CHc +.br +The client starts by sending a random challenge to the server. +.TP +.IR S \(-> C +.IR AuthPAK , +.IR IDs , +.IR DN , +.IR CHs , +.IR \- , +.IR \- , +.IR YAs +.br +The server generates a new public key +.I YAs +and replies with a +.I AuthPAK +request giving its +.I IDs +and authentication domain +.I DNs +along with its own random challenge +.I CHs +and its public key +.IR YAs . +.TP +.IR C \(-> S +.IR YBs , +.IR Ks { AuthTs , +.IR CHs , +.IR IDc , +.IR IDr , +.IR Kn }, +.IR Kn { AuthAc , +.IR CHs , +.IR RNc } +.br +The client generates its own public key +.I YAc +and adds it along with +.I IDc +and +.I IDr +to the +.I AuthPAK +request and obtains the public keys +.I YBs +and +.I YBc +from the AS response. At this point, client and AS +have completed ther authenticated key exchange and +derive +.I Kc +as described above. +Then the client requests a ticket pair using the same +message but with +.I AuthPAK +type changed to +.IR AuthTreq . +It decrypts his ticket with +.I Kc +extracting the shared secret +.IR Kn . +The client relays the server's +.I YBs +and ticket along with an +.IR authenticator , +the +.I AuthAc +message. +The server finishes his authenticated key exchange +using +.I YBs +and derives +.I Ks +to decrypt his ticket to extract the shared secret +.IR Kn . +When the decryption of the clients authenticator using +.I Kn +is successfull then this proves to the server that the +client knows +.I Kn +and is therefore allowed to authenticate as +.IR IDr . +The random string +.I RNc +is used in the derivation of the session secret. +.TP +.IR S \(-> C +.IR Kn { AuthAs , +.IR CHc , +.IR RNs } +.br +The server replies with its own authenticator, +proving to the client that it also knows +.I Kn +and contributes its random string +.IR RNs +for the session secret. +.PP +The 2048-bit session secret is derived with a PRF hashing the +concatenated random strings +.IR RNc | RNs +with the the shared secret key +.IR Kn . .SS "P9any .I P9any is the standard Plan 9 authentication protocol. @@ -288,12 +545,10 @@ The negotiation protocol is: .IB proto@authdom .IB proto@authdom .I ... -.sp -\n(PDu .TP .IR C \(-> S .I proto .I dom -.sp -\n(PDu .TP .IR S \(-> C .B OK @@ -349,10 +604,10 @@ The protocol is: .TP .IR C \(-> A .IR AuthPass , -.IR IDc , -.IR DN , +.IR \- , +.IR \- , .IR CHc , -.IR IDc , +.IR \- , .IR IDc .br The client sends a password change ticket request. @@ -471,39 +726,34 @@ message is expected, a .I AuthErr message may be substituted. .de Ok -.sp -\n(PDu .TP .IR A \(-> S .IR AuthOK , -.IR Ks { \\$1 , -.IR IDs , -.IR DN , +.IR Ks { AuthTs , .IR CHs , -.IR IDs , +.IR IDc , .IR IDc , .IR Kn }, -.IR Kn { AuthTs , +.IR Kn { AuthAc , .IR CHs } .. .PP .TP .IR S \(-> A .IR AuthChal , -.IR IDs , +.IR \- , .IR DN , .IR CHs , .IR IDs , .IR IDc -.sp -\n(PDu .TP .IR A \(-> S .IR AuthOK , .IR challenge -.sp -\n(PDu .TP .IR S \(-> A .IR response -.Ok AuthChal +.Ok .IP This protocol allows the use of handheld authenticators such as SecureNet @@ -562,28 +812,26 @@ Users not listed are assumed to have the same id in both places. .TP .IR S \(-> A -AuthApop , -.IR IDs , +.IR AuthApop , +.IR \- , .IR DN , .IR CHs , -.IR \- , +.IR IDs , .IR \- -.sp -\n(PDu .TP .IR A \(-> S .IR AuthOKvar , .IR challenge -.sp -\n(PDu .TP .IR S \(-> A -AuthApop , -.IR IDs , +.IR AuthApop , +.IR \- , .IR DN , .IR CHs , -.IR IDc , +.IR IDs , .IR IDc ; hexadecimal MD5 checksum -.Ok AuthApop +.Ok .IP This protocol implements APOP authentication (see @@ -616,22 +864,20 @@ in .TP .IR S \(-> A .IR AuthChap , -.IR IDs , +.IR \- , .IR DN , .IR CHs , -.IR \- , +.IR IDs , .IR \- -.sp -\n(PDu .TP .IR A \(-> S .I challenge -.sp -\n(PDu .TP .IR S \(-> A .IR pktid , .IR IDc , .IR response -.Ok AuthChap +.Ok .IP This protocol implements CHAP authentication (see @@ -648,22 +894,20 @@ in .TP .IR S \(-> A .IR AuthMSchap , -.IR IDs , +.IR \- , .IR DN , .IR CHs , -.IR \- , +.IR IDs , .IR \- -.sp -\n(PDu .TP .IR A \(-> S .I challenge -.sp -\n(PDu .TP .IR S \(-> A .IR IDc , .IR lm-response , .IR nt-response -.Ok AuthMschap +.Ok .IP This protocol implements Microsoft's MS-CHAP authentication @@ -682,17 +926,15 @@ in .TP .IR S \(-> A .IR AuthVNC , -.IR IDs , +.IR \- , .IR DN , .IR CHs , .IR IDs , .IR IDc -.sp -\n(PDu .TP .IR A \(-> S .IR AuthOKvar , .I challenge -.sp -\n(PDu .TP .IR S \(-> A .I response diff --git a/sys/src/cmd/auth/authcmdlib.h b/sys/src/cmd/auth/authcmdlib.h index 1792e1a07..fc34717a8 100644 --- a/sys/src/cmd/auth/authcmdlib.h +++ b/sys/src/cmd/auth/authcmdlib.h @@ -53,6 +53,7 @@ int netcheck(void*, long, char*); char* netdecimal(char*); char* netresp(char*, long, char*); char* okpasswd(char*); +void private(void); int querybio(char*, char*, Acctbio*); void rdbio(char*, char*, Acctbio*); int readarg(int, char*, int); diff --git a/sys/src/cmd/auth/authsrv.c b/sys/src/cmd/auth/authsrv.c index 9c499e03f..340498657 100644 --- a/sys/src/cmd/auth/authsrv.c +++ b/sys/src/cmd/auth/authsrv.c @@ -8,11 +8,21 @@ #include #include "authcmdlib.h" -int debug; Ndb *db; char raddr[128]; uchar zeros[16]; +typedef struct Keyslot Keyslot; +struct Keyslot +{ + Authkey; + char id[ANAMELEN]; +}; +Keyslot hkey, akey, ukey; +uchar keyseed[SHA2_256dlen]; + +char ticketform; + /* Microsoft auth constants */ enum { MShashlen = 16, @@ -20,25 +30,26 @@ enum { MSresplen = 24, }; -int ticketrequest(Ticketreq*); +void pak(Ticketreq*); +void ticketrequest(Ticketreq*); void challengebox(Ticketreq*); void changepasswd(Ticketreq*); void apop(Ticketreq*, int); void chap(Ticketreq*); void mschap(Ticketreq*); -void http(Ticketreq*); void vnc(Ticketreq*); int speaksfor(char*, char*); void replyerror(char*, ...); void getraddr(char*); -void mkkey(Authkey*); +void initkeyseed(void); +void mkkey(Keyslot*); void mkticket(Ticketreq*, Ticket*); void nthash(uchar hash[MShashlen], char *passwd); void lmhash(uchar hash[MShashlen], char *passwd); void ntv2hash(uchar hash[MShashlen], char *passwd, char *user, char *dom); void mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]); void desencrypt(uchar data[8], uchar key[7]); -int tickauthreply(Ticketreq*, Authkey*); +void tickauthreply(Ticketreq*, Authkey*); void safecpy(char*, char*, int); void @@ -49,8 +60,9 @@ main(int argc, char *argv[]) int n; ARGBEGIN{ - case 'd': - debug++; + case 'N': + ticketform = 1; + break; }ARGEND strcpy(raddr, "unknown"); @@ -59,6 +71,9 @@ main(int argc, char *argv[]) alarm(10*60*1000); /* kill a connection after 10 minutes */ + private(); + initkeyseed(); + db = ndbopen("/lib/ndb/auth"); if(db == 0) syslog(0, AUTHLOG, "no /lib/ndb/auth"); @@ -89,48 +104,114 @@ main(int argc, char *argv[]) case AuthCram: apop(&tr, AuthCram); break; - case AuthHttp: - http(&tr); - break; case AuthVNC: vnc(&tr); break; + case AuthPAK: + pak(&tr); + continue; default: syslog(0, AUTHLOG, "unknown ticket request type: %d", tr.type); exits(0); } + /* invalidate pak keys */ + akey.id[0] = 0; + hkey.id[0] = 0; + ukey.id[0] = 0; } /* not reached */ } +void +pak1(char *u, Keyslot *k) +{ + uchar y[PAKYLEN]; + PAKpriv p; + + safecpy(k->id, u, sizeof(k->id)); + if(!findkey(KEYDB, k->id, k) || tsmemcmp(k->aes, zeros, AESKEYLEN) == 0) { + /* make one up so caller doesn't know it was wrong */ + mkkey(k); + authpak_hash(k, k->id); + } + authpak_new(&p, k, y, 0); + if(write(1, y, PAKYLEN) != PAKYLEN) + exits(0); + if(readn(0, y, PAKYLEN) != PAKYLEN) + exits(0); + if(authpak_finish(&p, k, y)) + exits(0); +} + +void +pak(Ticketreq *tr) +{ + static uchar ok[1] = {AuthOK}; + + if(write(1, ok, 1) != 1) + exits(0); + + /* invalidate pak keys */ + akey.id[0] = 0; + hkey.id[0] = 0; + ukey.id[0] = 0; + + if(tr->hostid[0]) { + if(tr->authid[0]) + pak1(tr->authid, &akey); + pak1(tr->hostid, &hkey); + } else if(tr->uid[0]) { + pak1(tr->uid, &ukey); + } + + ticketform = 1; +} + int +getkey(char *u, Keyslot *k) +{ + /* empty user id is an error */ + if(*u == 0) + exits(0); + + if(k == &hkey && strcmp(u, k->id) == 0) + return 1; + if(k == &akey && strcmp(u, k->id) == 0) + return 1; + if(k == &ukey && strcmp(u, k->id) == 0) + return 1; + + if(ticketform != 0) + exits(0); + + return findkey(KEYDB, u, k); +} + +void ticketrequest(Ticketreq *tr) { - Authkey akey, hkey; - char tbuf[2*TICKETLEN+1]; + char tbuf[2*MAXTICKETLEN+1]; Ticket t; int n; - if(!findkey(KEYDB, tr->authid, &akey)){ + if(tr->uid[0] == 0) + exits(0); + if(!getkey(tr->authid, &akey)){ /* make one up so caller doesn't know it was wrong */ mkkey(&akey); - if(debug) - syslog(0, AUTHLOG, "tr-fail authid %s", raddr); + syslog(0, AUTHLOG, "tr-fail authid %s", tr->authid); } - if(!findkey(KEYDB, tr->hostid, &hkey)){ + if(!getkey(tr->hostid, &hkey)){ /* make one up so caller doesn't know it was wrong */ mkkey(&hkey); - if(debug) - syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr); + syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr); } - mkticket(tr, &t); if(!speaksfor(tr->hostid, tr->uid)){ mkkey(&akey); mkkey(&hkey); - if(debug) - syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for", - tr->uid, tr->hostid, raddr, tr->uid, tr->authid); + syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for", + tr->uid, tr->hostid, raddr, tr->uid, tr->authid); } n = 0; tbuf[n++] = AuthOK; @@ -138,44 +219,34 @@ ticketrequest(Ticketreq *tr) n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &hkey); t.num = AuthTs; n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &akey); - if(write(1, tbuf, n) < 0){ - if(debug) - syslog(0, AUTHLOG, "tr-fail %s@%s(%s): hangup", - tr->uid, tr->hostid, raddr); + if(write(1, tbuf, n) != n) exits(0); - } - if(debug) - syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", - tr->uid, tr->hostid, raddr, tr->uid, tr->authid); - return 0; + syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s", tr->uid, tr->hostid, raddr, tr->uid, tr->authid); } void challengebox(Ticketreq *tr) { + char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], buf[NETCHLEN+1]; + char *key, *netkey, *err; long chal; - char *key, *netkey; - Authkey hkey; - char kbuf[DESKEYLEN], nkbuf[DESKEYLEN]; - char buf[NETCHLEN+1]; - char *err; + if(tr->uid[0] == 0) + exits(0); key = finddeskey(KEYDB, tr->uid, kbuf); netkey = finddeskey(NETKEYDB, tr->uid, nkbuf); if(key == nil && netkey == nil){ /* make one up so caller doesn't know it was wrong */ genrandom((uchar*)nkbuf, DESKEYLEN); netkey = nkbuf; - if(debug) - syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr); + syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr); } - if(!findkey(KEYDB, tr->hostid, &hkey)){ + + if(!getkey(tr->hostid, &hkey)){ /* make one up so caller doesn't know it was wrong */ mkkey(&hkey); - if(debug) - syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, - tr->uid, raddr); + syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid, tr->uid, raddr); } /* @@ -185,18 +256,15 @@ challengebox(Ticketreq *tr) buf[0] = AuthOK; chal = nfastrand(MAXNETCHAL); sprint(buf+1, "%lud", chal); - if(write(1, buf, NETCHLEN+1) < 0) + if(write(1, buf, NETCHLEN+1) != NETCHLEN+1) exits(0); - if(readn(0, buf, NETCHLEN) < 0) + if(readn(0, buf, NETCHLEN) != NETCHLEN) exits(0); if(!(key != nil && netcheck(key, chal, buf)) && !(netkey != nil && netcheck(netkey, chal, buf)) && (err = secureidcheck(tr->uid, buf)) != nil){ replyerror("cr-fail %s %s %s", err, tr->uid, raddr); logfail(tr->uid); - if(debug) - syslog(0, AUTHLOG, "cr-fail %s@%s(%s): bad resp", - tr->uid, tr->hostid, raddr); return; } succeed(tr->uid); @@ -204,33 +272,24 @@ challengebox(Ticketreq *tr) /* * reply with ticket & authenticator */ - if(tickauthreply(tr, &hkey) < 0){ - if(debug) - syslog(0, AUTHLOG, "cr-fail %s@%s(%s): hangup", - tr->uid, tr->hostid, raddr); - exits(0); - } + tickauthreply(tr, &hkey); - if(debug) - syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", - tr->uid, tr->hostid, raddr); + syslog(0, AUTHLOG, "cr-ok %s@%s(%s)", tr->uid, tr->hostid, raddr); } void changepasswd(Ticketreq *tr) { - Ticket t; - char tbuf[TICKETLEN+1]; - char prbuf[PASSREQLEN]; + char tbuf[MAXTICKETLEN+1], prbuf[MAXPASSREQLEN], *err; Passwordreq pr; - Authkey okey, nkey; - char *err; - int n; + Authkey nkey; + Ticket t; + int n, m; - if(!findkey(KEYDB, tr->uid, &okey)){ + if(!getkey(tr->uid, &ukey)){ /* make one up so caller doesn't know it was wrong */ - mkkey(&okey); - syslog(0, AUTHLOG, "cp-fail uid %s", raddr); + mkkey(&ukey); + syslog(0, AUTHLOG, "cp-fail uid %s@%s", tr->uid, raddr); } /* send back a ticket with a new key */ @@ -238,25 +297,30 @@ changepasswd(Ticketreq *tr) t.num = AuthTp; n = 0; tbuf[n++] = AuthOK; - n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &okey); + n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &ukey); if(write(1, tbuf, n) != n) exits(0); /* loop trying passwords out */ for(;;){ - n = readn(0, prbuf, sizeof(prbuf)); - if(n <= 0 || convM2PR(prbuf, n, &pr, &t) <= 0) - exits(0); + for(n=0; (m = convM2PR(prbuf, n, &pr, &t)) <= 0; n += m){ + m = -m; + if(m <= n || m > sizeof(prbuf)) + exits(0); + m -= n; + if(readn(0, prbuf+n, m) != m) + exits(0); + } if(pr.num != AuthPass){ replyerror("protocol botch1: %s", raddr); exits(0); } passtokey(&nkey, pr.old); - if(memcmp(nkey.des, okey.des, DESKEYLEN) != 0){ + if(tsmemcmp(ukey.des, nkey.des, DESKEYLEN) != 0){ replyerror("protocol botch2: %s", raddr); continue; } - if(memcmp(okey.aes, zeros, AESKEYLEN) != 0 && memcmp(okey.aes, nkey.aes, AESKEYLEN) != 0){ + if(tsmemcmp(ukey.aes, zeros, AESKEYLEN) != 0 && tsmemcmp(ukey.aes, nkey.aes, AESKEYLEN) != 0){ replyerror("protocol botch3: %s", raddr); continue; } @@ -276,56 +340,15 @@ changepasswd(Ticketreq *tr) replyerror("can't write key %s", raddr); continue; } + memmove(ukey.des, nkey.des, DESKEYLEN); + memmove(ukey.aes, nkey.aes, AESKEYLEN); break; } + succeed(tr->uid); prbuf[0] = AuthOK; - write(1, prbuf, 1); - succeed(tr->uid); - return; -} - -void -http(Ticketreq *tr) -{ - Ticket t; - char tbuf[TICKETLEN+1]; - Authkey key; - char *p; - Biobuf *b; - int n; - - /* use plan9 key when there is any */ - if(!findkey(KEYDB, tr->uid, &key)) - mkkey(&key); - - n = strlen(tr->uid); - b = Bopen("/sys/lib/httppasswords", OREAD); - if(b != nil){ - for(;;){ - p = Brdline(b, '\n'); - if(p == nil) - break; - p[Blinelen(b)-1] = 0; - if(strncmp(p, tr->uid, n) == 0) - if(p[n] == ' ' || p[n] == '\t'){ - p += n; - while(*p == ' ' || *p == '\t') - p++; - passtokey(&key, p); - } - } - Bterm(b); - } - - /* send back a ticket encrypted with the key */ - mkticket(tr, &t); - genrandom((uchar*)t.chal, CHALLEN); - t.num = AuthHr; - n = 0; - tbuf[n++] = AuthOK; - n += convT2M(&t, tbuf+n, sizeof(tbuf)-n, &key); - write(1, tbuf, n); + if(write(1, prbuf, 1) != 1) + exits(0); } static char* @@ -371,7 +394,6 @@ apop(Ticketreq *tr, int type) { int challen, i, n, tries; char *secret, *p; - Authkey hkey; Ticketreq treq; DigestState *s; char sbuf[SECRETLEN]; @@ -402,7 +424,7 @@ apop(Ticketreq *tr, int type) if(n <= 0 || convM2TR(trbuf, n, &treq) <= 0) exits(0); tr = &treq; - if(tr->type != type) + if(tr->type != type || tr->uid[0] == 0) exits(0); /* @@ -417,11 +439,11 @@ apop(Ticketreq *tr, int type) * lookup */ secret = findsecret(KEYDB, tr->uid, sbuf); - if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){ + if(!getkey(tr->hostid, &hkey) || secret == nil){ replyerror("apop-fail bad response %s", raddr); logfail(tr->uid); if(tries > 5) - return; + exits(0); continue; } @@ -436,11 +458,11 @@ apop(Ticketreq *tr, int type) s = md5((uchar*)chal, challen, 0, 0); md5((uchar*)secret, strlen(secret), digest, s); } - if(memcmp(digest, resp, MD5dlen) != 0){ + if(tsmemcmp(digest, resp, MD5dlen) != 0){ replyerror("apop-fail bad response %s", raddr); logfail(tr->uid); if(tries > 5) - return; + exits(0); continue; } break; @@ -451,15 +473,12 @@ apop(Ticketreq *tr, int type) /* * reply with ticket & authenticator */ - if(tickauthreply(tr, &hkey) < 0) - exits(0); + tickauthreply(tr, &hkey); - if(debug){ - if(type == AuthCram) - syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr); - else - syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr); - } + if(type == AuthCram) + syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr); + else + syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr); } enum { @@ -489,14 +508,16 @@ uchar swizzletab[256] = { void vnc(Ticketreq *tr) { - char *secret; - Authkey hkey; uchar chal[VNCchallen+6]; uchar reply[VNCchallen]; char sbuf[SECRETLEN]; + char *secret; DESstate s; int i; + if(tr->uid[0] == 0) + exits(0); + /* * Create a challenge and send it. */ @@ -504,35 +525,33 @@ vnc(Ticketreq *tr) chal[0] = AuthOKvar; sprint((char*)chal+1, "%-5d", VNCchallen); if(write(1, chal, sizeof(chal)) != sizeof(chal)) - return; + exits(0); /* * lookup keys (and swizzle bits) */ memset(sbuf, 0, sizeof(sbuf)); secret = findsecret(KEYDB, tr->uid, sbuf); - if(secret == nil){ + if(!getkey(tr->hostid, &hkey) || secret == nil){ + mkkey(&hkey); genrandom((uchar*)sbuf, sizeof(sbuf)); secret = sbuf; } for(i = 0; i < 8; i++) secret[i] = swizzletab[(uchar)secret[i]]; - if(!findkey(KEYDB, tr->hostid, &hkey)) - mkkey(&hkey); - /* * get response */ if(readn(0, reply, sizeof(reply)) != sizeof(reply)) - return; + exits(0); /* * decrypt response and compare */ setupDESstate(&s, (uchar*)secret, nil); desECBdecrypt(reply, sizeof(reply), &s); - if(memcmp(reply, chal+6, VNCchallen) != 0){ + if(tsmemcmp(reply, chal+6, VNCchallen) != 0){ replyerror("vnc-fail bad response %s", raddr); logfail(tr->uid); return; @@ -542,18 +561,15 @@ vnc(Ticketreq *tr) /* * reply with ticket & authenticator */ - if(tickauthreply(tr, &hkey) < 0) - exits(0); + tickauthreply(tr, &hkey); - if(debug) - syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr); + syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr); } void chap(Ticketreq *tr) { char *secret; - Authkey hkey; DigestState *s; char sbuf[SECRETLEN]; uchar digest[MD5dlen]; @@ -564,7 +580,8 @@ chap(Ticketreq *tr) * Create a challenge and send it. */ genrandom((uchar*)chal, sizeof(chal)); - write(1, chal, sizeof(chal)); + if(write(1, chal, sizeof(chal)) != sizeof(chal)) + exits(0); /* * get chap reply @@ -572,15 +589,17 @@ chap(Ticketreq *tr) if(readn(0, &reply, sizeof(reply)) < 0) exits(0); safecpy(tr->uid, reply.uid, sizeof(tr->uid)); + if(tr->uid[0] == 0) + exits(0); /* * lookup */ secret = findsecret(KEYDB, tr->uid, sbuf); - if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){ + if(!getkey(tr->hostid, &hkey) || secret == nil){ replyerror("chap-fail bad response %s", raddr); logfail(tr->uid); - exits(0); + return; } /* @@ -590,10 +609,10 @@ chap(Ticketreq *tr) md5((uchar*)secret, strlen(secret), 0, s); md5((uchar*)chal, sizeof(chal), digest, s); - if(memcmp(digest, reply.resp, MD5dlen) != 0){ + if(tsmemcmp(digest, reply.resp, MD5dlen) != 0){ replyerror("chap-fail bad response %s", raddr); logfail(tr->uid); - exits(0); + return; } succeed(tr->uid); @@ -601,23 +620,9 @@ chap(Ticketreq *tr) /* * reply with ticket & authenticator */ - if(tickauthreply(tr, &hkey) < 0) - exits(0); + tickauthreply(tr, &hkey); - if(debug) - syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr); -} - -void -printresp(uchar resp[MSresplen]) -{ - char buf[200], *p; - int i; - - p = buf; - for(i=0; iuid, raddr); } enum { @@ -666,7 +671,6 @@ void mschap(Ticketreq *tr) { char *secret; - Authkey hkey; char sbuf[SECRETLEN], windom[128]; uchar chal[CHALLEN], ntblob[1024]; uchar hash[MShashlen]; @@ -681,7 +685,8 @@ mschap(Ticketreq *tr) * Create a challenge and send it. */ genrandom(chal, sizeof(chal)); - write(1, chal, sizeof(chal)); + if(write(1, chal, sizeof(chal)) != sizeof(chal)) + exits(0); /* * get chap reply @@ -734,15 +739,17 @@ mschap(Ticketreq *tr) } safecpy(tr->uid, reply.uid, sizeof(tr->uid)); + if(tr->uid[0] == 0) + exits(0); + /* * lookup */ secret = findsecret(KEYDB, tr->uid, sbuf); - if(!findkey(KEYDB, tr->hostid, &hkey) || secret == nil){ - replyerror("mschap-fail bad response %s/%s(%s)", - tr->uid, tr->hostid, raddr); + if(!getkey(tr->hostid, &hkey) || secret == nil){ + replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr); logfail(tr->uid); - exits(0); + return; } if(ntbloblen > 0){ @@ -756,14 +763,14 @@ mschap(Ticketreq *tr) */ s = hmac_md5(chal, 8, hash, MShashlen, nil, nil); hmac_md5((uchar*)reply.LMresp+16, 8, hash, MShashlen, resp, s); - lmok = memcmp(resp, reply.LMresp, 16) == 0; + lmok = tsmemcmp(resp, reply.LMresp, 16) == 0; /* * NtResponse = Cat(HMAC_MD5(NtHash, Cat(SC, NtBlob)), NtBlob) */ s = hmac_md5(chal, 8, hash, MShashlen, nil, nil); hmac_md5(ntblob, ntbloblen, hash, MShashlen, resp, s); - ntok = memcmp(resp, reply.NTresp, 16) == 0; + ntok = tsmemcmp(resp, reply.NTresp, 16) == 0; if(lmok || ntok || windom[0] == '\0') break; @@ -774,11 +781,11 @@ mschap(Ticketreq *tr) } else { lmhash(hash, secret); mschalresp(resp, hash, chal); - lmok = memcmp(resp, reply.LMresp, MSresplen) == 0; + lmok = tsmemcmp(resp, reply.LMresp, MSresplen) == 0; nthash(hash, secret); mschalresp(resp, hash, chal); - ntok = memcmp(resp, reply.NTresp, MSresplen) == 0; - dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0; + ntok = tsmemcmp(resp, reply.NTresp, MSresplen) == 0; + dupe = tsmemcmp(reply.LMresp, reply.NTresp, MSresplen) == 0; } /* @@ -795,10 +802,9 @@ mschap(Ticketreq *tr) * windows clients don't seem to use the feature. */ if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){ - replyerror("mschap-fail bad response %s/%s(%s) %d,%d,%d", - tr->uid, tr->hostid, raddr, dupe, lmok, ntok); + replyerror("mschap-fail bad response %s/%s(%s)", tr->uid, tr->hostid, raddr); logfail(tr->uid); - exits(0); + return; } succeed(tr->uid); @@ -806,11 +812,9 @@ mschap(Ticketreq *tr) /* * reply with ticket & authenticator */ - if(tickauthreply(tr, &hkey) < 0) - exits(0); + tickauthreply(tr, &hkey); - if(debug) - replyerror("mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr); + syslog(0, AUTHLOG, "mschap-ok %s/%s(%s)", tr->uid, tr->hostid, raddr); nthash(hash, secret); md4(hash, 16, hash2, 0); @@ -818,7 +822,7 @@ mschap(Ticketreq *tr) sha1(hash2, 16, 0, s); sha1(chal, 8, digest, s); - if(write(1, digest, 16) < 0) + if(write(1, digest, 16) != 16) exits(0); } @@ -997,10 +1001,35 @@ getraddr(char *dir) } void -mkkey(Authkey *k) +initkeyseed(void) { - genrandom((uchar*)k->des, DESKEYLEN); - genrandom((uchar*)k->aes, AESKEYLEN); + static char info[] = "PRF key for generation of dummy user keys"; + char k[DESKEYLEN], *u; + + u = getuser(); + if(!finddeskey(KEYDB, u, k)){ + syslog(0, AUTHLOG, "user %s not in keydb", u); + exits(0); + } + hmac_sha2_256((uchar*)info, sizeof(info)-1, (uchar*)k, sizeof(k), keyseed, nil); + memset(k, 0, sizeof(k)); +} + +void +mkkey(Keyslot *k) +{ + uchar h[SHA2_256dlen]; + Authkey *a = k; + + genrandom((uchar*)a, sizeof(Authkey)); + + /* + * the DES key has to be constant for a user in each response, + * so we make one up pseudo randomly from a keyseed and user name. + */ + hmac_sha2_256((uchar*)k->id, strlen(k->id), keyseed, sizeof(keyseed), h, nil); + memmove(a->des, h, DESKEYLEN); + memset(h, 0, sizeof(h)); } void @@ -1008,35 +1037,35 @@ mkticket(Ticketreq *tr, Ticket *t) { memset(t, 0, sizeof(Ticket)); memmove(t->chal, tr->chal, CHALLEN); - safecpy(t->cuid, tr->uid, sizeof(t->cuid)); - safecpy(t->suid, tr->uid, sizeof(t->suid)); - genrandom((uchar*)t->key, DESKEYLEN); + safecpy(t->cuid, tr->uid, ANAMELEN); + safecpy(t->suid, tr->uid, ANAMELEN); + genrandom(t->key, NONCELEN); + t->form = ticketform; } /* * reply with ticket and authenticator */ -int -tickauthreply(Ticketreq *tr, Authkey *hkey) +void +tickauthreply(Ticketreq *tr, Authkey *key) { Ticket t; Authenticator a; - char buf[TICKETLEN+AUTHENTLEN+1]; + char buf[MAXTICKETLEN+MAXAUTHENTLEN+1]; int n; mkticket(tr, &t); t.num = AuthTs; n = 0; buf[n++] = AuthOK; - n += convT2M(&t, buf+n, sizeof(buf)-n, hkey); + n += convT2M(&t, buf+n, sizeof(buf)-n, key); memset(&a, 0, sizeof(a)); memmove(a.chal, t.chal, CHALLEN); + genrandom(a.rand, NONCELEN); a.num = AuthAc; - a.id = 0; n += convA2M(&a, buf+n, sizeof(buf)-n, &t); if(write(1, buf, n) != n) - return -1; - return 0; + exits(0); } void diff --git a/sys/src/cmd/auth/convkeys.c b/sys/src/cmd/auth/convkeys.c index 252c43f1c..e55c1f419 100644 --- a/sys/src/cmd/auth/convkeys.c +++ b/sys/src/cmd/auth/convkeys.c @@ -43,6 +43,8 @@ main(int argc, char *argv[]) usage(); file = argv[0]; + private(); + /* get original key */ if(usepass){ print("enter password file is encoded with\n"); diff --git a/sys/src/cmd/auth/debug.c b/sys/src/cmd/auth/debug.c index 4a182a640..e52afdab2 100644 --- a/sys/src/cmd/auth/debug.c +++ b/sys/src/cmd/auth/debug.c @@ -116,8 +116,8 @@ readcons(char *prompt, char *def, int raw, char *buf, int nbuf) } } -void authdialfutz(char*, char*); -void authfutz(char*, char*); +void authdialfutz(char*, char*, char*); +void authfutz(char*, char*, char*); /* scan factotum for p9sk1 keys; check them */ void @@ -143,7 +143,7 @@ debugfactotumkeys(void) a = _parseattr(s+4); free(s); proto = _strfindattr(a, "proto"); - if(proto==nil || strcmp(proto, "p9sk1")!=0) + if(proto==nil || (strcmp(proto, "p9sk1")!=0 && strcmp(proto, "dp9ik")!=0)) continue; dom = _strfindattr(a, "dom"); if(dom == nil){ @@ -157,17 +157,17 @@ debugfactotumkeys(void) _freeattr(a); continue; } - print("p9sk1 key: %A\n", a); + print("key: %A\n", a); found = 1; - authdialfutz(dom, user); + authdialfutz(dom, user, proto); _freeattr(a); } if(!found) - print("no p9sk1 keys found in factotum\n"); + print("no p9sk1/dp9ik keys found in factotum\n"); } void -authdialfutz(char *dom, char *user) +authdialfutz(char *dom, char *user, char *proto) { int fd; char *server; @@ -177,7 +177,7 @@ authdialfutz(char *dom, char *user) if(fd >= 0){ print("\tsuccessfully dialed auth server\n"); close(fd); - authfutz(dom, user); + authfutz(dom, user, proto); return; } print("\tcannot dial auth server: %r\n"); @@ -205,11 +205,44 @@ authdialfutz(char *dom, char *user) print("\tdial %s failed: %r\n", addr); } +int +getpakkeys(int fd, Ticketreq *tr, Authkey *akey, Authkey *hkey) +{ + uchar y[PAKYLEN]; + PAKpriv p; + int ret, type; + + ret = -1; + type = tr->type; + tr->type = AuthPAK; + if(_asrequest(fd, tr) < 0 || _asrdresp(fd, (char*)y, 0) < 0) + goto out; + + authpak_hash(akey, tr->authid); + authpak_new(&p, akey, y, 1); + if(write(fd, y, PAKYLEN) != PAKYLEN + || readn(fd, y, PAKYLEN) != PAKYLEN + || authpak_finish(&p, akey, y)) + goto out; + + authpak_hash(hkey, tr->hostid); + authpak_new(&p, hkey, y, 1); + if(write(fd, y, PAKYLEN) != PAKYLEN + || readn(fd, y, PAKYLEN) != PAKYLEN + || authpak_finish(&p, hkey, y)) + goto out; + + ret = 0; +out: + tr->type = type; + return ret; +} + void -authfutz(char *dom, char *user) +authfutz(char *dom, char *user, char *proto) { int fd, nobootes, n, m; - char pw[128], prompt[128], tbuf[2*TICKETLEN]; + char pw[128], prompt[128], tbuf[2*MAXTICKETLEN]; Authkey key, booteskey; Ticket t; Ticketreq tr; @@ -219,6 +252,7 @@ authfutz(char *dom, char *user) if(pw[0] == '\0') return; passtokey(&key, pw); + booteskey = key; fd = authdial(nil, dom); if(fd < 0){ @@ -234,6 +268,13 @@ authfutz(char *dom, char *user) strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user); strecpy(tr.uid, tr.uid+sizeof tr.uid, user); memset(tr.chal, 0xAA, sizeof tr.chal); + + if(strcmp(proto, "dp9ik") == 0 && getpakkeys(fd, &tr, &booteskey, &key) < 0){ + print("\tgetpakkeys failed: %r\n"); + close(fd); + return; + } + if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){ print("\t_asgetticket failed: %r\n"); close(fd); @@ -252,7 +293,7 @@ authfutz(char *dom, char *user) return; } - convM2T(tbuf+m, n-m, &t, &key); + convM2T(tbuf+m, n-m, &t, &booteskey); if(t.num != AuthTs){ print("\tcannot decrypt ticket2 from auth server (bad t.num=0x%.2ux)\n", t.num); print("\tauth server and you do not agree on key for %s@%s\n", user, dom); @@ -269,9 +310,24 @@ authfutz(char *dom, char *user) /* try ticket request using bootes key */ snprint(prompt, sizeof prompt, "\tcpu server owner for domain %s ", dom); readcons(prompt, "glenda", 0, tr.authid, sizeof tr.authid); - if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){ + snprint(prompt, sizeof prompt, "\tpassword for %s@%s [hit enter to skip test]", tr.authid, dom); + readcons(prompt, nil, 1, pw, sizeof pw); + if(pw[0] == '\0'){ + nobootes=1; + goto Nobootes; + } + nobootes = 0; + passtokey(&booteskey, pw); + + if(strcmp(proto, "dp9ik") == 0 && getpakkeys(fd, &tr, &booteskey, &key) < 0){ + print("\tgetpakkeys failed: %r\n"); close(fd); + return; + } + + if((n = _asgetticket(fd, &tr, tbuf, sizeof(tbuf))) < 0){ print("\t_asgetticket failed: %r\n"); + close(fd); return; } m = convM2T(tbuf, n, &t, &key); @@ -286,16 +342,7 @@ authfutz(char *dom, char *user) print("\tauth server is rogue\n"); return; } - - snprint(prompt, sizeof prompt, "\tpassword for %s@%s [hit enter to skip test]", tr.authid, dom); - readcons(prompt, nil, 1, pw, sizeof pw); - if(pw[0] == '\0'){ - nobootes=1; - goto Nobootes; - } - nobootes = 0; - passtokey(&booteskey, pw); - + convM2T(tbuf+m, n-m, &t, &booteskey); if(t.num != AuthTs){ print("\tcannot decrypt ticket2 from auth server (bad t.num=0x%.2ux)\n", t.num); @@ -321,7 +368,6 @@ Nobootes:; * auth server (assumes running cpu service) * to test that bootes key is right over there */ - } void diff --git a/sys/src/cmd/auth/factotum/apop.c b/sys/src/cmd/auth/factotum/apop.c index e46b33d3c..d29806a83 100644 --- a/sys/src/cmd/auth/factotum/apop.c +++ b/sys/src/cmd/auth/factotum/apop.c @@ -20,6 +20,7 @@ struct State int asfd; int astype; Key *key; + Authkey k; Ticket t; Ticketreq tr; char chal[128]; @@ -213,29 +214,23 @@ dochal(State *s) s->asfd = -1; - /* send request to authentication server and get challenge */ /* send request to authentication server and get challenge */ if((dom = _strfindattr(s->key->attr, "dom")) == nil || (user = _strfindattr(s->key->attr, "user")) == nil){ werrstr("apop/dochal cannot happen"); goto err; } - - s->asfd = _authdial(nil, dom); - - /* could generate our own challenge on error here */ - if(s->asfd < 0) - goto err; + memmove(&s->k, s->key->priv, sizeof(Authkey)); memset(&s->tr, 0, sizeof(s->tr)); safecpy(s->tr.authdom, dom, sizeof s->tr.authdom); safecpy(s->tr.hostid, user, sizeof(s->tr.hostid)); s->tr.type = s->astype; - alarm(30*1000); - if(_asrequest(s->asfd, &s->tr) < 0){ - alarm(0); + + s->asfd = _authreq(&s->tr, &s->k); + if(s->asfd < 0) goto err; - } + alarm(30*1000); n = _asrdresp(s->asfd, s->chal, sizeof s->chal); alarm(0); if(n <= 5) @@ -272,7 +267,7 @@ doreply(State *s, char *user, char *response) alarm(0); goto err; } - n = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv); + n = _asgetresp(s->asfd, &s->t, &a, &s->k); alarm(0); if(n < 0){ /* leave connection open so we can try again */ @@ -282,7 +277,7 @@ doreply(State *s, char *user, char *response) s->asfd = -1; if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ + || tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ if(s->key->successes == 0) disablekey(s->key); werrstr(Easproto); @@ -290,8 +285,7 @@ doreply(State *s, char *user, char *response) } s->key->successes++; if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 - || a.id != 0){ + || tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){ werrstr(Easproto); goto err; } diff --git a/sys/src/cmd/auth/factotum/chap.c b/sys/src/cmd/auth/factotum/chap.c index 9559b3941..67e817ae0 100644 --- a/sys/src/cmd/auth/factotum/chap.c +++ b/sys/src/cmd/auth/factotum/chap.c @@ -41,6 +41,7 @@ struct State int astype; int asfd; Key *key; + Authkey k; Ticket t; Ticketreq tr; char chal[ChapChallen]; @@ -309,19 +310,18 @@ dochal(State *s) werrstr("chap/dochal cannot happen"); goto err; } - s->asfd = _authdial(nil, dom); - if(s->asfd < 0) - goto err; - + memmove(&s->k, s->key->priv, sizeof(Authkey)); + memset(&s->tr, 0, sizeof(s->tr)); safecpy(s->tr.authdom, dom, sizeof(s->tr.authdom)); safecpy(s->tr.hostid, user, sizeof(s->tr.hostid)); s->tr.type = s->astype; - alarm(30*1000); - if(_asrequest(s->asfd, &s->tr) < 0){ - alarm(0); + + s->asfd = _authreq(&s->tr, &s->k); + if(s->asfd < 0) goto err; - } + + alarm(30*1000); n = readn(s->asfd, s->chal, sizeof s->chal); alarm(0); if(n != sizeof s->chal) @@ -347,7 +347,7 @@ doreply(State *s, uchar *reply, int nreply) alarm(0); goto err; } - n = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv); + n = _asgetresp(s->asfd, &s->t, &a, &s->k); if(n < 0){ alarm(0); /* leave connection open so we can try again */ @@ -361,7 +361,7 @@ doreply(State *s, uchar *reply, int nreply) s->asfd = -1; if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ + || tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ if(s->key->successes == 0) disablekey(s->key); werrstr(Easproto); @@ -369,8 +369,7 @@ doreply(State *s, uchar *reply, int nreply) } s->key->successes++; if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 - || a.id != 0){ + || tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0){ werrstr(Easproto); return -1; } diff --git a/sys/src/cmd/auth/factotum/confirm.c b/sys/src/cmd/auth/factotum/confirm.c index 6e57919e6..d35337e7c 100644 --- a/sys/src/cmd/auth/factotum/confirm.c +++ b/sys/src/cmd/auth/factotum/confirm.c @@ -16,16 +16,19 @@ confirmflush(Req *r) { Req **l; + qlock(&confbuf); for(l=&cusewait; *l; l=&(*l)->aux){ if(*l == r){ *l = r->aux; if(r->aux == nil) cuselast = l; r->aux = nil; + qunlock(&confbuf); respond(r, "interrupted"); return; } } + qunlock(&confbuf); logbufflush(&confbuf, r); } @@ -43,7 +46,7 @@ hastag(Fsstate *fss, int tag, int *tagoff) } int -confirmwrite(char *s) +confirmwrite(Srv *srv, char *s) { char *t, *ans; int allow, tagoff; @@ -54,15 +57,18 @@ confirmwrite(char *s) a = _parseattr(s); if(a == nil){ + _freeattr(a); werrstr("empty write"); return -1; } if((t = _strfindattr(a, "tag")) == nil){ + _freeattr(a); werrstr("no tag"); return -1; } tag = strtoul(t, 0, 0); if((ans = _strfindattr(a, "answer")) == nil){ + _freeattr(a); werrstr("no answer"); return -1; } @@ -71,14 +77,18 @@ confirmwrite(char *s) else if(strcmp(ans, "no") == 0) allow = 0; else{ + _freeattr(a); werrstr("bad answer"); return -1; } - r = nil; + _freeattr(a); + srvrelease(srv); + qlock(&confbuf); + fss = nil; tagoff = -1; - for(l=&cusewait; *l; l=&(*l)->aux){ - r = *l; - if(hastag(r->fid->aux, tag, &tagoff)){ + for(l=&cusewait; (r = *l) != nil; l=&(*l)->aux){ + fss = r->fid->aux; + if(hastag(fss, tag, &tagoff)){ *l = r->aux; if(r->aux == nil) cuselast = l; @@ -86,13 +96,17 @@ confirmwrite(char *s) break; } } + qunlock(&confbuf); if(r == nil || tagoff == -1){ + srvacquire(srv); werrstr("tag not found"); return -1; } - fss = r->fid->aux; + qlock(fss); fss->conf[tagoff].canuse = allow; rpcread(r); + qunlock(fss); + srvacquire(srv); return 0; } @@ -118,9 +132,11 @@ confirmqueue(Req *r, Fsstate *fss) respond(r, "no confirmations to wait for (bug)"); return; } - *cuselast = r; + qlock(&confbuf); r->aux = nil; + *cuselast = r; cuselast = &r->aux; + qunlock(&confbuf); } /* Yes, I am unhappy that the code below is a copy of the code above. */ @@ -140,38 +156,45 @@ needkeyflush(Req *r) { Req **l; + qlock(&needkeybuf); for(l=&needwait; *l; l=&(*l)->aux){ if(*l == r){ *l = r->aux; if(r->aux == nil) needlast = l; r->aux = nil; + qunlock(&needkeybuf); respond(r, "interrupted"); return; } } + qunlock(&needkeybuf); logbufflush(&needkeybuf, r); } int -needkeywrite(char *s) +needkeywrite(Srv *srv, char *s) { char *t; ulong tag; Attr *a; Req *r, **l; + Fsstate *fss; a = _parseattr(s); if(a == nil){ + _freeattr(a); werrstr("empty write"); return -1; } if((t = _strfindattr(a, "tag")) == nil){ + _freeattr(a); werrstr("no tag"); return -1; } tag = strtoul(t, 0, 0); r = nil; + qlock(&needkeybuf); for(l=&needwait; *l; l=&(*l)->aux){ r = *l; if(r->tag == tag){ @@ -182,15 +205,23 @@ needkeywrite(char *s) break; } } + qunlock(&needkeybuf); if(r == nil){ + _freeattr(a); werrstr("tag not found"); return -1; } + fss = r->fid->aux; + srvrelease(srv); + qlock(fss); if(s = _strfindattr(a, "error")){ werrstr("%s", s); - retrpc(r, RpcErrstr, (Fsstate*)r->fid->aux); + retrpc(r, RpcErrstr, fss); }else rpcread(r); + _freeattr(a); + qunlock(fss); + srvacquire(srv); return 0; } @@ -204,9 +235,13 @@ needkeyqueue(Req *r, Fsstate *fss) snprint(msg, sizeof msg, "needkey tag=%lud %s", r->tag, fss->keyinfo); logbufappend(&needkeybuf, msg); - *needlast = r; + + qlock(&needkeybuf); r->aux = nil; + *needlast = r; needlast = &r->aux; + qunlock(&needkeybuf); + return 0; } diff --git a/sys/src/cmd/auth/factotum/dat.h b/sys/src/cmd/auth/factotum/dat.h index ecf4a0bf6..724891b2b 100644 --- a/sys/src/cmd/auth/factotum/dat.h +++ b/sys/src/cmd/auth/factotum/dat.h @@ -45,6 +45,7 @@ typedef struct State State; struct Fsstate { + QLock; char *sysuser; /* user according to system */ /* keylist, protolist */ @@ -76,7 +77,8 @@ struct Fsstate struct Key { - int ref; + Ref; + Attr *attr; Attr *privattr; /* private attributes, like *data */ Proto *proto; @@ -97,12 +99,16 @@ struct Keyinfo /* for findkey */ struct Keyring { + QLock; + Key **key; int nkey; }; struct Logbuf { + QLock; + Req *wait; Req **waitlast; int rp; @@ -133,11 +139,11 @@ extern char Etoolarge[]; /* confirm.c */ void confirmread(Req*); void confirmflush(Req*); -int confirmwrite(char*); +int confirmwrite(Srv*, char*); void confirmqueue(Req*, Fsstate*); void needkeyread(Req*); void needkeyflush(Req*); -int needkeywrite(char*); +int needkeywrite(Srv*, char*); int needkeyqueue(Req*, Fsstate*); /* fs.c */ @@ -162,12 +168,7 @@ void logread(Req*); void logflush(Req*); void logbufflush(Logbuf*, Req*); void logbufread(Logbuf*, Req*); -void logbufproc(Logbuf*); void logbufappend(Logbuf*, char*); -void needkeyread(Req*); -void needkeyflush(Req*); -int needkeywrite(char*); -int needkeyqueue(Req*, Fsstate*); /* rpc.c */ int ctlwrite(char*, int); @@ -185,7 +186,8 @@ void retrpc(Req*, int, Fsstate*); #pragma varargck argpos findkey 3 #pragma varargck argpos setattr 2 -int _authdial(char*, char*); +int _authdial(char*); +int _authreq(Ticketreq *, Authkey *); void askuser(char*); int attrnamefmt(Fmt *fmt); int canusekey(Fsstate*, Key*); @@ -199,7 +201,7 @@ Keyinfo* mkkeyinfo(Keyinfo*, Fsstate*, Attr*); int findkey(Key**, Keyinfo*, char*, ...); int findp9authkey(Key**, Fsstate*); Proto *findproto(char*); -char *getnvramkey(int); +int getnvramkey(int); void initcap(void); int isclient(char*); int matchattr(Attr*, Attr*, Attr*); @@ -221,7 +223,7 @@ void writehostowner(char*); /* protocols */ extern Proto apop, cram; /* apop.c */ -extern Proto p9any, p9sk1, p9sk2; /* p9sk.c */ +extern Proto p9any, p9sk1, dp9ik; /* p9sk.c */ extern Proto chap, mschap, mschapv2, mschap2; /* chap.c */ extern Proto p9cr, vnc; /* p9cr.c */ extern Proto pass; /* pass.c */ @@ -231,4 +233,3 @@ extern Proto wep; /* wep.c */ extern Proto httpdigest; /* httpdigest.c */ extern Proto ecdsa; /* ecdsa.c */ extern Proto wpapsk; /* wpapsk.c */ - diff --git a/sys/src/cmd/auth/factotum/fs.c b/sys/src/cmd/auth/factotum/fs.c index 7d195ac01..edfb7861f 100644 --- a/sys/src/cmd/auth/factotum/fs.c +++ b/sys/src/cmd/auth/factotum/fs.c @@ -36,7 +36,7 @@ prototab[] = &p9any, &p9cr, &p9sk1, - &p9sk2, + &dp9ik, &pass, /* &srs, */ &rsa, @@ -148,15 +148,8 @@ main(int argc, char **argv) } if(sflag){ - s = getnvramkey(kflag ? NVwrite : NVwriteonerr); - if(s == nil) + if(getnvramkey(kflag ? NVwrite : NVwriteonerr) < 0) fprint(2, "factotum warning: cannot read nvram: %r\n"); - else if(ctlwrite(s, 0) < 0) - fprint(2, "factotum warning: cannot add nvram key: %r\n"); - if (s != nil) { - memset(s, 0, strlen(s)); - free(s); - } } else if(uflag) promptforhostowner(); owner = getuser(); @@ -418,9 +411,11 @@ fsdestroyfid(Fid *fid) fss = fid->aux; if(fss == nil) return; + qlock(fss); if(fss->ps) (*fss->proto->close)(fss); _freeattr(fss->attr); + qunlock(fss); free(fss); } @@ -452,14 +447,14 @@ keylist(int i, char *a, uint n, Fsstate *fss) int wb; Keyinfo ki; Key *k; - static char zero[Nearend]; k = nil; mkkeyinfo(&ki, fss, nil); ki.attr = nil; + ki.noconf = 1; ki.skip = i; ki.usedisabled = 1; - if(findkey(&k, &ki, "") != RpcOk) + if(findkey(&k, &ki, nil) != RpcOk) return 0; memset(a + n - Nearend, 0, Nearend); @@ -488,6 +483,24 @@ protolist(int i, char *a, uint n, Fsstate *fss) return n; } +static void +fsrpcio(Req *r) +{ + Fsstate *fss; + Srv *srv; + + fss = r->fid->aux; + srv = r->srv; + srvrelease(srv); + qlock(fss); + if(r->ifcall.type == Tread) + rpcread(r); + else + rpcwrite(r); + qunlock(fss); + srvacquire(srv); +} + static void fsread(Req *r) { @@ -507,7 +520,7 @@ fsread(Req *r) respond(r, nil); break; case Qrpc: - rpcread(r); + fsrpcio(r); break; case Qneedkey: needkeyread(r); @@ -540,7 +553,7 @@ fswrite(Req *r) respond(r, "bug in fswrite"); break; case Qrpc: - rpcwrite(r); + fsrpcio(r); break; case Qneedkey: case Qconfirm: @@ -552,10 +565,10 @@ fswrite(Req *r) default: abort(); case Qneedkey: - ret = needkeywrite(s); + ret = needkeywrite(r->srv, s); break; case Qconfirm: - ret = confirmwrite(s); + ret = confirmwrite(r->srv, s); break; case Qctl: ret = ctlwrite(s, 0); diff --git a/sys/src/cmd/auth/factotum/log.c b/sys/src/cmd/auth/factotum/log.c index 26c3cfc5f..d5286cb93 100644 --- a/sys/src/cmd/auth/factotum/log.c +++ b/sys/src/cmd/auth/factotum/log.c @@ -1,6 +1,6 @@ #include "dat.h" -void +static void logbufproc(Logbuf *lb) { char *s; @@ -43,12 +43,14 @@ logbufproc(Logbuf *lb) void logbufread(Logbuf *lb, Req *r) { + qlock(lb); if(lb->waitlast == nil) lb->waitlast = &lb->wait; *(lb->waitlast) = r; lb->waitlast = &r->aux; r->aux = nil; logbufproc(lb); + qunlock(lb); } void @@ -56,6 +58,7 @@ logbufflush(Logbuf *lb, Req *r) { Req **l; + qlock(lb); for(l=&lb->wait; *l; l=&(*l)->aux){ if(*l == r){ *l = r->aux; @@ -66,6 +69,7 @@ logbufflush(Logbuf *lb, Req *r) break; } } + qunlock(lb); } void @@ -74,12 +78,14 @@ logbufappend(Logbuf *lb, char *buf) if(debug) fprint(2, "%s\n", buf); + qlock(lb); if(lb->msg[lb->wp]) free(lb->msg[lb->wp]); lb->msg[lb->wp] = estrdup9p(buf); if(++lb->wp == nelem(lb->msg)) lb->wp = 0; logbufproc(lb); + qunlock(lb); } Logbuf logbuf; diff --git a/sys/src/cmd/auth/factotum/p9any.c b/sys/src/cmd/auth/factotum/p9any.c index 204e99b25..367f6b4d2 100644 --- a/sys/src/cmd/auth/factotum/p9any.c +++ b/sys/src/cmd/auth/factotum/p9any.c @@ -13,7 +13,8 @@ #include "dat.h" static Proto *negotiable[] = { - &p9sk1, + &p9sk1, /* has to be first because of drawterm bug */ + &dp9ik, }; struct State @@ -98,6 +99,7 @@ setupfss(Fsstate *fss, State *s, Key *k) fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name); fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom")); s->subfss.attr = fss->attr; + s->subfss.proto = s->subproto; s->subfss.phase = Notstarted; s->subfss.sysuser = fss->sysuser; s->subfss.seqnum = fss->seqnum; @@ -229,8 +231,8 @@ getdom(char *p) return p+1; } -static Proto* -findneg(char *name) +static int +findprotox(char *name) { int i, len; char *p; @@ -239,13 +241,27 @@ findneg(char *name) len = p-name; else len = strlen(name); - for(i=0; iname, name, len) == 0 && negotiable[i]->name[len] == 0) - return negotiable[i]; + return i; + return -1; +} + +static Proto* +findneg(char *name) +{ + int x = findprotox(name); + if(x >= 0) + return negotiable[x]; return nil; } +static int +protopref(void *a, void *b) +{ + return findprotox(*(char**)b) - findprotox(*(char**)a); +} + static int p9anywrite(Fsstate *fss, void *va, uint n) { @@ -274,7 +290,11 @@ p9anywrite(Fsstate *fss, void *va, uint n) free(a); return failure(fss, "unknown version of p9any"); } + if(--m > 0) + memmove(token, token+1, m*sizeof(token[0])); } + /* put prefered protocols first */ + qsort(token, m, sizeof(token[0]), protopref); /* * look for a key @@ -285,7 +305,7 @@ p9anywrite(Fsstate *fss, void *va, uint n) k = nil; p = nil; dom = nil; - for(i=(s->version==1?0:1); ikey->privattr, "!password"); - if(pw == nil) - return failure(fss, "vncresponse cannot happen"); - passtokey(&key, pw); memset(buf, 0, 8); sprint((char*)buf, "%d", atoi(s->chal)); - if(encrypt(key.des, buf, 8) < 0) + akey = (Authkey*)s->key->priv; + if(encrypt(akey->des, buf, 8) < 0) return failure(fss, "can't encrypt response"); chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal); @@ -287,7 +284,7 @@ p9crwrite(Fsstate *fss, void *va, uint n) return failure(fss, Easproto); } /* get ticket plus authenticator from auth server */ - ret = _asgetresp(s->asfd, &s->t, &a, (Authkey*)s->key->priv); + ret = _asgetresp(s->asfd, &s->t, &a, &s->k); alarm(0); if(ret < 0) @@ -295,15 +292,14 @@ p9crwrite(Fsstate *fss, void *va, uint n) /* check ticket */ if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ + || tsmemcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){ if (s->key->successes == 0) disablekey(s->key); return failure(fss, Easproto); } s->key->successes++; if(a.num != AuthAc - || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 - || a.id != 0) + || tsmemcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0) return failure(fss, Easproto); fss->haveai = 1; @@ -321,19 +317,16 @@ getchal(State *s, Fsstate *fss) { int n; + memmove(&s->k, s->key->priv, sizeof(Authkey)); + safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid)); safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom)); s->tr.type = s->astype; - /* get challenge from auth server */ - s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom")); + s->asfd = _authreq(&s->tr, &s->k); if(s->asfd < 0) return failure(fss, Easproto); alarm(30*1000); - if(_asrequest(s->asfd, &s->tr) < 0){ - alarm(0); - return failure(fss, Easproto); - } n = _asrdresp(s->asfd, s->chal, s->challen); alarm(0); if(n <= 0){ diff --git a/sys/src/cmd/auth/factotum/p9sk1.c b/sys/src/cmd/auth/factotum/p9sk1.c index 0d43ad6a2..4dd8fad7d 100644 --- a/sys/src/cmd/auth/factotum/p9sk1.c +++ b/sys/src/cmd/auth/factotum/p9sk1.c @@ -1,30 +1,38 @@ /* - * p9sk1, p9sk2 - Plan 9 secret (private) key authentication. - * p9sk2 is an incomplete flawed variant of p9sk1. + * p9sk1, dp9ik - Plan 9 secret (private) key authentication. + * dp9ik uses AuthPAK diffie hellman key exchange with the + * auth server to protect the password derived key from offline + * dictionary attacks. * * Client protocol: - * write challenge[challen] (p9sk1 only) - * read tickreq[tickreqlen] - * write ticket[ticketlen] - * read authenticator[authentlen] + * write challenge[challen] + * read pakreq[ticketreqlen + pakylen] (dp9ik only) + * write paky[pakylen] + * read tickreq[tickreqlen] (p9sk1 only) + * write ticket + authenticator + * read authenticator * * Server protocol: - * read challenge[challen] (p9sk1 only) - * write tickreq[tickreqlen] - * read ticket[ticketlen] - * write authenticator[authentlen] + * read challenge[challen] + * write pakreq[ticketreqlen + pakylen] (dp9ik only) + * read paky[pakylen] + * write tickreq[tickreqlen] (p9sk1 only) + * read ticket + authenticator + * write authenticator */ #include "dat.h" struct State { - int vers; Key *key; Ticket t; Ticketreq tr; + Authkey k; + PAKpriv p; char cchal[CHALLEN]; - char tbuf[TICKETLEN+AUTHENTLEN]; + uchar y[PAKYLEN], rand[2*NONCELEN]; + char tbuf[MAXTICKETLEN + MAXAUTHENTLEN]; int tbuflen; uchar *secret; int speakfor; @@ -34,33 +42,41 @@ enum { /* client phases */ CHaveChal, - CNeedTreq, + CNeedPAKreq, /* dp9ik only */ + CHavePAKy, + CNeedTreq, /* p9sk1 only */ CHaveTicket, CNeedAuth, /* server phases */ SNeedChal, - SHaveTreq, + SHavePAKreq, /* dp9ik only */ + SNeedPAKy, + SHaveTreq, /* p9sk1 only */ SNeedTicket, SHaveAuth, - + Maxphase, }; static char *phasenames[Maxphase] = { [CHaveChal] "CHaveChal", +[CNeedPAKreq] "CNeedPAKreq", +[CHavePAKy] "CHavePAKy", [CNeedTreq] "CNeedTreq", [CHaveTicket] "CHaveTicket", [CNeedAuth] "CNeedAuth", [SNeedChal] "SNeedChal", +[SHavePAKreq] "SHavePAKreq", +[SNeedPAKy] "SNeedPAKy", [SHaveTreq] "SHaveTreq", [SNeedTicket] "SNeedTicket", [SHaveAuth] "SHaveAuth", }; -static int gettickets(State*, Ticketreq *, char*, int); +static int gettickets(State*, Ticketreq*, uchar*, char*, int); static int p9skinit(Proto *p, Fsstate *fss) @@ -78,25 +94,13 @@ p9skinit(Proto *p, Fsstate *fss) fss = fss; fss->phasename = phasenames; fss->maxphase = Maxphase; - if(p == &p9sk1) - s->vers = 1; - else if(p == &p9sk2) - s->vers = 2; - else - abort(); + fss->proto = p; if(iscli){ - switch(s->vers){ - case 1: - fss->phase = CHaveChal; - genrandom((uchar*)s->cchal, CHALLEN); - break; - case 2: - fss->phase = CNeedTreq; - break; - } + fss->phase = CHaveChal; + genrandom((uchar*)s->cchal, CHALLEN); }else{ s->tr.type = AuthTreq; - attr = setattr(_copyattr(fss->attr), "proto=p9sk1"); + attr = setattr(_copyattr(fss->attr), "proto=%q", p->name); mkkeyinfo(&ki, fss, attr); ki.user = nil; ret = findkey(&k, &ki, "user? dom?"); @@ -108,16 +112,9 @@ p9skinit(Proto *p, Fsstate *fss) safecpy(s->tr.authid, _strfindattr(k->attr, "user"), sizeof(s->tr.authid)); safecpy(s->tr.authdom, _strfindattr(k->attr, "dom"), sizeof(s->tr.authdom)); s->key = k; + memmove(&s->k, k->priv, sizeof(Authkey)); genrandom((uchar*)s->tr.chal, sizeof s->tr.chal); - switch(s->vers){ - case 1: - fss->phase = SNeedChal; - break; - case 2: - fss->phase = SHaveTreq; - memmove(s->cchal, s->tr.chal, CHALLEN); - break; - } + fss->phase = SNeedChal; } s->tbuflen = 0; s->secret = nil; @@ -125,6 +122,39 @@ p9skinit(Proto *p, Fsstate *fss) return RpcOk; } +static int +establish(Fsstate *fss) +{ + AuthInfo *ai; + State *s; + + s = fss->ps; + ai = &fss->ai; + + ai->cuid = s->t.cuid; + ai->suid = s->t.suid; + if(fss->proto == &dp9ik){ + static char info[] = "Plan 9 session secret"; + + ai->nsecret = 256; + ai->secret = emalloc(ai->nsecret); + hkdf_x( s->rand, 2*NONCELEN, + (uchar*)info, sizeof(info)-1, + s->t.key, NONCELEN, + ai->secret, ai->nsecret, + hmac_sha2_256, SHA2_256dlen); + } else { + ai->nsecret = 8; + ai->secret = emalloc(ai->nsecret); + des56to64((uchar*)s->t.key, ai->secret); + } + s->secret = ai->secret; + + fss->haveai = 1; + fss->phase = Established; + return RpcOk; +} + static int p9skread(Fsstate *fss, void *a, uint *n) { @@ -142,13 +172,37 @@ p9skread(Fsstate *fss, void *a, uint *n) return toosmall(fss, m); *n = m; memmove(a, s->cchal, m); - fss->phase = CNeedTreq; + if(fss->proto == &dp9ik) + fss->phase = CNeedPAKreq; + else + fss->phase = CNeedTreq; + return RpcOk; + + case SHavePAKreq: + m = TICKREQLEN + PAKYLEN; + if(*n < m) + return toosmall(fss, m); + s->tr.type = AuthPAK; + *n = convTR2M(&s->tr, a, *n); + authpak_new(&s->p, &s->k, (uchar*)a + *n, 1); + *n += PAKYLEN; + fss->phase = SNeedPAKy; + return RpcOk; + + case CHavePAKy: + m = PAKYLEN; + if(*n < m) + return toosmall(fss, m); + *n = m; + memmove(a, s->y, m); + fss->phase = CHaveTicket; return RpcOk; case SHaveTreq: m = TICKREQLEN; if(*n < m) return toosmall(fss, m); + s->tr.type = AuthTreq; *n = convTR2M(&s->tr, a, *n); fss->phase = SNeedTicket; return RpcOk; @@ -168,15 +222,7 @@ p9skread(Fsstate *fss, void *a, uint *n) return toosmall(fss, m); *n = m; memmove(a, s->tbuf, m); - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - s->secret = emalloc(8); - des56to64((uchar*)s->t.key, s->secret); - fss->ai.secret = s->secret; - fss->ai.nsecret = 8; - fss->haveai = 1; - fss->phase = Established; - return RpcOk; + return establish(fss); } } @@ -184,12 +230,12 @@ static int p9skwrite(Fsstate *fss, void *a, uint n) { int m, ret, sret; - char tbuf[2*TICKETLEN], *user; + char tbuf[2*PAKYLEN+2*MAXTICKETLEN], *user; Attr *attr; - Authenticator auth; State *s; Key *srvkey; Keyinfo ki; + Authenticator auth; s = fss->ps; switch(fss->phase){ @@ -201,24 +247,32 @@ p9skwrite(Fsstate *fss, void *a, uint n) if(n < m) return toosmall(fss, m); memmove(s->cchal, a, m); - fss->phase = SHaveTreq; + if(fss->proto == &dp9ik) + fss->phase = SHavePAKreq; + else + fss->phase = SHaveTreq; return RpcOk; + case CNeedPAKreq: + m = TICKREQLEN+PAKYLEN; + if(n < m) + return toosmall(fss, m); case CNeedTreq: - m = convM2TR(a, n, &s->tr); - if(m <= 0) - return toosmall(fss, -m); + m = TICKREQLEN; + if(n < m) + return toosmall(fss, m); - /* remember server's chal */ - if(s->vers == 2) - memmove(s->cchal, s->tr.chal, CHALLEN); + m = convM2TR(a, n, &s->tr); + if(m <= 0 || s->tr.type != (fss->phase==CNeedPAKreq ? AuthPAK : AuthTreq)) + return failure(fss, Easproto); if(s->key != nil) closekey(s->key); attr = _delattr(_delattr(_copyattr(fss->attr), "role"), "user"); - attr = setattr(attr, "proto=p9sk1"); + attr = setattr(attr, "proto=%q", fss->proto->name); user = _strfindattr(fss->attr, "user"); + /* * If our client is the user who started factotum (client==owner), then * he can use whatever keys we have to speak as whoever he pleases. @@ -241,7 +295,7 @@ p9skwrite(Fsstate *fss, void *a, uint n) attr = setattr(attr, "user=%q", user); mkkeyinfo(&ki, fss, attr); ret = findkey(&s->key, &ki, - "role=client dom=%q %s", s->tr.authdom, p9sk1.keyprompt); + "role=client dom=%q %s", s->tr.authdom, fss->proto->keyprompt); if(ret == RpcOk) closekey(srvkey); else if(sret == RpcOk){ @@ -256,27 +310,32 @@ p9skwrite(Fsstate *fss, void *a, uint n) } /* fill in the rest of the request */ - s->tr.type = AuthTreq; safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof s->tr.hostid); if(s->speakfor) safecpy(s->tr.uid, fss->sysuser, sizeof s->tr.uid); else safecpy(s->tr.uid, s->tr.hostid, sizeof s->tr.uid); - /* get tickets, from auth server or invent if we can */ - ret = gettickets(s, &s->tr, tbuf, sizeof(tbuf)); - if(ret < 0){ + /* get tickets from authserver or invent if we can */ + if(fss->phase == CNeedPAKreq) + ret = gettickets(s, &s->tr, (uchar*)a + m, tbuf, sizeof(tbuf)); + else + ret = gettickets(s, &s->tr, nil, tbuf, sizeof(tbuf)); + if(ret <= 0){ _freeattr(attr); return failure(fss, nil); } - - m = convM2T(tbuf, ret, &s->t, (Authkey*)s->key->priv); + m = convM2T(tbuf, ret, &s->t, &s->k); + if(m <= 0 || (fss->proto == &dp9ik && s->t.form == 0)){ + _freeattr(attr); + return failure(fss, Easproto); + } if(s->t.num != AuthTc){ if(s->key->successes == 0 && !s->speakfor) disablekey(s->key); if(askforkeys && !s->speakfor){ snprint(fss->keyinfo, sizeof fss->keyinfo, - "%A %s", attr, p9sk1.keyprompt); + "%A %s", attr, fss->proto->keyprompt); _freeattr(attr); return RpcNeedkey; }else{ @@ -288,32 +347,44 @@ p9skwrite(Fsstate *fss, void *a, uint n) _freeattr(attr); ret -= m; memmove(s->tbuf, tbuf+m, ret); - + genrandom(s->rand, NONCELEN); auth.num = AuthAc; memmove(auth.chal, s->tr.chal, CHALLEN); - auth.id = 0; + memmove(auth.rand, s->rand, NONCELEN); ret += convA2M(&auth, s->tbuf+ret, sizeof(s->tbuf)-ret, &s->t); s->tbuflen = ret; - fss->phase = CHaveTicket; + if(fss->phase == CNeedPAKreq) + fss->phase = CHavePAKy; + else + fss->phase = CHaveTicket; + return RpcOk; + + case SNeedPAKy: + m = PAKYLEN; + if(n < m) + return toosmall(fss, m); + if(authpak_finish(&s->p, &s->k, (uchar*)a)) + return failure(fss, Easproto); + fss->phase = SNeedTicket; return RpcOk; case SNeedTicket: - m = convM2T(a, n, &s->t, (Authkey*)s->key->priv); - if(m <= 0) - return toosmall(fss, -m); + m = convM2T(a, n, &s->t, &s->k); + if(m <= 0 || convM2A((char*)a+m, n-m, &auth, &s->t) <= 0) + return toosmall(fss, MAXTICKETLEN + MAXAUTHENTLEN); + if(fss->proto == &dp9ik && s->t.form == 0) + return failure(fss, Easproto); if(s->t.num != AuthTs - || memcmp(s->t.chal, s->tr.chal, CHALLEN) != 0) + || tsmemcmp(s->t.chal, s->tr.chal, CHALLEN) != 0) return failure(fss, Easproto); - ret = convM2A((char*)a+m, n-m, &auth, &s->t); - if(ret <= 0) - return toosmall(fss, -ret + m); if(auth.num != AuthAc - || memcmp(auth.chal, s->tr.chal, CHALLEN) != 0 - || auth.id != 0) + || tsmemcmp(auth.chal, s->tr.chal, CHALLEN) != 0) return failure(fss, Easproto); + memmove(s->rand, auth.rand, NONCELEN); + genrandom(s->rand + NONCELEN, NONCELEN); auth.num = AuthAs; memmove(auth.chal, s->cchal, CHALLEN); - auth.id = 0; + memmove(auth.rand, s->rand + NONCELEN, NONCELEN); s->tbuflen = convA2M(&auth, s->tbuf, sizeof(s->tbuf), &s->t); fss->phase = SHaveAuth; return RpcOk; @@ -323,18 +394,10 @@ p9skwrite(Fsstate *fss, void *a, uint n) if(m <= 0) return toosmall(fss, -m); if(auth.num != AuthAs - || memcmp(auth.chal, s->cchal, CHALLEN) != 0 - || auth.id != 0) + || tsmemcmp(auth.chal, s->cchal, CHALLEN) != 0) return failure(fss, Easproto); - fss->ai.cuid = s->t.cuid; - fss->ai.suid = s->t.suid; - s->secret = emalloc(8); - des56to64((uchar*)s->t.key, s->secret); - fss->ai.secret = s->secret; - fss->ai.nsecret = 8; - fss->haveai = 1; - fss->phase = Established; - return RpcOk; + memmove(s->rand+NONCELEN, auth.rand, NONCELEN); + return establish(fss); } } @@ -352,6 +415,7 @@ p9skclose(Fsstate *fss) closekey(s->key); s->key = nil; } + memset(s, 0, sizeof(State)); free(s); } @@ -386,14 +450,27 @@ static int p9skaddkey(Key *k, int before) { Authkey *akey; - char *s; + char *s, *u; + u = _strfindattr(k->attr, "user"); + if(u == nil){ + werrstr("no user attribute"); + return -1; + } akey = emalloc(sizeof(Authkey)); if(s = _strfindattr(k->privattr, "!hex")){ - if(hexparse(s, (uchar*)akey->des, DESKEYLEN) < 0){ - free(akey); - werrstr("malformed key data"); - return -1; + if(k->proto == &dp9ik){ + if(hexparse(s, akey->aes, AESKEYLEN) < 0){ + free(akey); + werrstr("malformed key data"); + return -1; + } + } else { + if(hexparse(s, (uchar*)akey->des, DESKEYLEN) < 0){ + free(akey); + werrstr("malformed key data"); + return -1; + } } }else if(s = _strfindattr(k->privattr, "!password")){ passtokey(akey, s); @@ -402,6 +479,10 @@ p9skaddkey(Key *k, int before) free(akey); return -1; } + if(k->proto == &dp9ik) + authpak_hash(akey, u); + else + memset(akey->aes, 0, AESKEYLEN); /* don't attempt AuthPAK for p9sk1 key */ k->priv = akey; return replacekey(k, before); } @@ -409,31 +490,12 @@ p9skaddkey(Key *k, int before) static void p9skclosekey(Key *k) { + memset(k->priv, 0, sizeof(Authkey)); free(k->priv); } static int -getastickets(State *s, Ticketreq *tr, char *tbuf, int tbuflen) -{ - int asfd, rv; - char *dom; - - if((dom = _strfindattr(s->key->attr, "dom")) == nil){ - werrstr("auth key has no domain"); - return -1; - } - asfd = _authdial(nil, dom); - if(asfd < 0) - return -1; - alarm(30*1000); - rv = _asgetticket(asfd, tr, tbuf, tbuflen); - alarm(0); - close(asfd); - return rv; -} - -static int -mkserverticket(State *s, char *tbuf, int tbuflen) +mkservertickets(State *s, uchar *y, char *tbuf, int tbuflen) { Ticketreq *tr = &s->tr; Ticket t; @@ -446,31 +508,86 @@ mkserverticket(State *s, char *tbuf, int tbuflen) return -1; */ memset(&t, 0, sizeof(t)); + ret = 0; + if(y != nil){ + t.form = 1; + authpak_new(&s->p, &s->k, s->y, 0); + authpak_finish(&s->p, &s->k, y); + } memmove(t.chal, tr->chal, CHALLEN); strcpy(t.cuid, tr->uid); strcpy(t.suid, tr->uid); - genrandom((uchar*)t.key, DESKEYLEN); + genrandom((uchar*)t.key, sizeof(t.key)); t.num = AuthTc; - ret = convT2M(&t, tbuf, tbuflen, (Authkey*)s->key->priv); + ret += convT2M(&t, tbuf+ret, tbuflen-ret, &s->k); t.num = AuthTs; - ret += convT2M(&t, tbuf+ret, tbuflen-ret, (Authkey*)s->key->priv); + ret += convT2M(&t, tbuf+ret, tbuflen-ret, &s->k); + memset(&t, 0, sizeof(t)); + return ret; } static int -gettickets(State *s, Ticketreq *tr, char *tbuf, int tbuflen) +getastickets(State *s, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen) +{ + int asfd, rv; + char *dom; + + if((dom = _strfindattr(s->key->attr, "dom")) == nil){ + werrstr("auth key has no domain"); + return -1; + } + asfd = _authdial(dom); + if(asfd < 0) + return -1; + alarm(30*1000); + if(y != nil){ + rv = -1; + s->tr.type = AuthPAK; + if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN) + goto Out; + + authpak_new(&s->p, &s->k, (uchar*)tbuf, 1); + if(write(asfd, tbuf, PAKYLEN) != PAKYLEN) + goto Out; + + if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN) + goto Out; + + memmove(s->y, tbuf, PAKYLEN); + if(authpak_finish(&s->p, &s->k, (uchar*)tbuf+PAKYLEN)) + goto Out; + } + s->tr.type = AuthTreq; + rv = _asgetticket(asfd, tr, tbuf, tbuflen); +Out: + alarm(0); + close(asfd); + return rv; +} + +static int +gettickets(State *s, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen) { int ret; - ret = getastickets(s, tr, tbuf, tbuflen); - if(ret >= 0) + if(tr->authdom[0] == 0 + || tr->authid[0] == 0 + || tr->hostid[0] == 0 + || tr->uid[0] == 0){ + werrstr("bad ticket request"); + return -1; + } + memmove(&s->k, s->key->priv, sizeof(Authkey)); + ret = getastickets(s, tr, y, tbuf, tbuflen); + if(ret > 0) return ret; - return mkserverticket(s, tbuf, tbuflen); + return mkservertickets(s, y, tbuf, tbuflen); } Proto p9sk1 = { .name= "p9sk1", -.init= p9skinit, +.init= p9skinit, .write= p9skwrite, .read= p9skread, .close= p9skclose, @@ -479,11 +596,13 @@ Proto p9sk1 = { .keyprompt= "user? !password?" }; -Proto p9sk2 = { -.name= "p9sk2", -.init= p9skinit, +Proto dp9ik = { +.name= "dp9ik", +.init= p9skinit, .write= p9skwrite, .read= p9skread, .close= p9skclose, +.addkey= p9skaddkey, +.closekey= p9skclosekey, +.keyprompt= "user? !password?" }; - diff --git a/sys/src/cmd/auth/factotum/rpc.c b/sys/src/cmd/auth/factotum/rpc.c index 92275e95c..7c3ab5894 100644 --- a/sys/src/cmd/auth/factotum/rpc.c +++ b/sys/src/cmd/auth/factotum/rpc.c @@ -429,15 +429,20 @@ ctlwrite(char *a, int atzero) return -1; } } - for(i=0; inkey; ){ - if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){ + Again: + qlock(ring); + for(i=0; inkey; i++){ + k = ring->key[i]; + if(matchattr(attr, k->attr, k->privattr)){ nmatch++; - closekey(ring->key[i]); ring->nkey--; memmove(&ring->key[i], &ring->key[i+1], (ring->nkey-i)*sizeof(ring->key[0])); - }else - i++; + qunlock(ring); + closekey(k); + goto Again; + } } + qunlock(ring); _freeattr(attr); if(nmatch == 0){ werrstr("found no keys to delete"); diff --git a/sys/src/cmd/auth/factotum/util.c b/sys/src/cmd/auth/factotum/util.c index 3fdae8ea0..8448143dc 100644 --- a/sys/src/cmd/auth/factotum/util.c +++ b/sys/src/cmd/auth/factotum/util.c @@ -1,6 +1,7 @@ #include "dat.h" static char secstore[100]; /* server name */ +static uchar zeros[16]; /* bind in the default network and cs */ static int @@ -72,14 +73,13 @@ netndbauthaddr(void) } int -_authdial(char *net, char *authdom) +_authdial(char *authdom) { - int i, fd, vanilla; + int i, fd; alarm(30*1000); - vanilla = net==nil || strcmp(net, "/net")==0; - if(!vanilla || bindnetcs()>=0) - fd = authdial(net, authdom); + if(bindnetcs()>=0) + fd = authdial(nil, authdom); else { /* * If we failed to mount /srv/cs, assume that @@ -106,6 +106,31 @@ _authdial(char *net, char *authdom) return fd; } +int +_authreq(Ticketreq *tr, Authkey *k) +{ + int fd; + + fd = _authdial(tr->authdom); + if(fd < 0) + return -1; + alarm(30*1000); + if(tsmemcmp(k->aes, zeros, AESKEYLEN) != 0){ + if(_asgetpakkey(fd, tr, k) < 0){ + alarm(0); + close(fd); + return -1; + } + } + if(_asrequest(fd, tr) < 0){ + alarm(0); + close(fd); + return -1; + } + alarm(0); + return fd; +} + /* * prompt user for a key. don't care about memory leaks, runs standalone */ @@ -200,8 +225,8 @@ canusekey(Fsstate *fss, Key *k) return fss->conf[i].canuse; if(fss->nconf%16 == 0) fss->conf = erealloc(fss->conf, (fss->nconf+16)*(sizeof(fss->conf[0]))); + incref(k); fss->conf[fss->nconf].key = k; - k->ref++; fss->conf[fss->nconf].canuse = -1; fss->conf[fss->nconf].tag = conftaggen++; fss->nconf++; @@ -216,7 +241,7 @@ closekey(Key *k) { if(k == nil) return; - if(--k->ref != 0) + if(decref(k)) return; if(k->proto && k->proto->closekey) (*k->proto->closekey)(k); @@ -377,6 +402,7 @@ findkey(Key **ret, Keyinfo *ki, char *fmt, ...) return failure(ki->fss, nil); } + qlock(ring); nmatch = 0; for(i=0; inkey; i++){ k = ring->key[i]; @@ -391,6 +417,7 @@ findkey(Key **ret, Keyinfo *ki, char *fmt, ...) if(!ki->noconf){ switch(canusekey(ki->fss, k)){ case -1: + qunlock(ring); _freeattr(attr1); return RpcConfirm; case 0: @@ -402,11 +429,13 @@ findkey(Key **ret, Keyinfo *ki, char *fmt, ...) _freeattr(attr1); _freeattr(attr2); _freeattr(attr3); - k->ref++; + incref(k); *ret = k; + qunlock(ring); return RpcOk; } } + qunlock(ring); flog("%d: no key matches %A %A %A %A", ki->fss->seqnum, attr0, attr1, attr2, attr3); werrstr("no key matches %A %A", attr0, attr1); _freeattr(attr2); @@ -446,6 +475,7 @@ findp9authkey(Key **k, Fsstate *fss) { char *dom; Keyinfo ki; + int rv; /* * We don't use fss->attr here because we don't @@ -454,10 +484,18 @@ findp9authkey(Key **k, Fsstate *fss) mkkeyinfo(&ki, fss, nil); ki.attr = nil; ki.user = nil; - if(dom = _strfindattr(fss->attr, "dom")) - return findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom); + dom = _strfindattr(fss->attr, "dom"); + if(dom != nil) + rv = findkey(k, &ki, "proto=dp9ik dom=%q role=server user?", dom); else - return findkey(k, &ki, "proto=p9sk1 role=server dom? user?"); + rv = findkey(k, &ki, "proto=dp9ik role=server dom? user?"); + if(rv != RpcOk){ + if(dom != nil) + rv = findkey(k, &ki, "proto=p9sk1 dom=%q role=server user?", dom); + else + rv = findkey(k, &ki, "proto=p9sk1 role=server dom? user?"); + } + return rv; } Proto* @@ -471,12 +509,11 @@ findproto(char *name) return nil; } -char* +int getnvramkey(int flag) { Nvrsafe safe; char *s; - int i; memset(&safe, 0, sizeof safe); /* @@ -484,24 +521,30 @@ getnvramkey(int flag) * but safe still holds good data. */ if(readnvram(&safe, flag)<0 && safe.authid[0]==0) - return nil; + return -1; - /* - * only use nvram key if it is non-zero - */ - for(i = 0; i < DESKEYLEN; i++) - if(safe.machkey[i] != 0) - break; - if(i == DESKEYLEN) - return nil; - - fmtinstall('H', encodefmt); - s = smprint("key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", - safe.authid, safe.authdom, DESKEYLEN, safe.machkey); + if(tsmemcmp(safe.machkey, zeros, DESKEYLEN) != 0){ + s = smprint("key proto=p9sk1 user=%q dom=%q !hex=%.*H !password=______", + safe.authid, safe.authdom, DESKEYLEN, safe.machkey); + if(s != nil){ + ctlwrite(s, 0); + memset(s, 0, strlen(s)); + free(s); + } + } + if(tsmemcmp(safe.aesmachkey, zeros, AESKEYLEN) != 0){ + s = smprint("key proto=dp9ik user=%q dom=%q !hex=%.*H !password=______", + safe.authid, safe.authdom, AESKEYLEN, safe.aesmachkey); + if(s != nil){ + ctlwrite(s, 0); + memset(s, 0, strlen(s)); + free(s); + } + } writehostowner(safe.authid); memset(&safe, 0, sizeof safe); - return s; + return 0; } int @@ -781,24 +824,26 @@ replacekey(Key *kn, int before) int i; Key *k; + qlock(ring); + incref(kn); 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; + qunlock(ring); + closekey(k); return 0; } } if(ring->nkey%16 == 0) ring->key = erealloc(ring->key, (ring->nkey+16)*sizeof(ring->key[0])); - kn->ref++; if(before){ memmove(ring->key+1, ring->key, ring->nkey*sizeof ring->key[0]); ring->key[0] = kn; ring->nkey++; }else ring->key[ring->nkey++] = kn; + qunlock(ring); return 0; } diff --git a/sys/src/cmd/auth/keyfs.c b/sys/src/cmd/auth/keyfs.c index 0265fde46..0fa72e3ad 100644 --- a/sys/src/cmd/auth/keyfs.c +++ b/sys/src/cmd/auth/keyfs.c @@ -25,6 +25,7 @@ enum { Quser, Qkey, Qaeskey, + Qpakhash, Qsecret, Qlog, Qstatus, @@ -54,8 +55,7 @@ struct Fid { struct User { char *name; - char key[DESKEYLEN]; - uchar aeskey[AESKEYLEN]; + Authkey key; char secret[SECRETLEN]; ulong expire; /* 0 == never */ uchar status; @@ -73,6 +73,7 @@ char *qinfo[Qmax] = { [Quser] ".", [Qkey] "key", [Qaeskey] "aeskey", + [Qpakhash] "pakhash", [Qsecret] "secret", [Qlog] "log", [Qexpire] "expire", @@ -180,6 +181,7 @@ main(int argc, char *argv[]) if(pipe(p) < 0) error("can't make pipe: %r"); + private(); if(usepass) getpass(&authkey, nil, 0, 0); else { @@ -374,7 +376,7 @@ Open(Fid *f) mode = rhdr.mode; if(f->qtype == Quser && (mode & (OWRITE|OTRUNC))) return "user already exists"; - if(f->qtype == Qaeskey && !keydbaes) + if((f->qtype == Qaeskey || f->qtype == Qpakhash) && !keydbaes) return "keyfile not in aes format"; thdr.qid = mkqid(f->user, f->qtype); thdr.iounit = messagesize - IOHDRSZ; @@ -458,6 +460,7 @@ Read(Fid *f) return 0; case Qkey: case Qaeskey: + case Qpakhash: case Qsecret: if(f->user->status != Sok) return "user disabled"; @@ -468,13 +471,17 @@ Read(Fid *f) m = 0; switch(f->qtype){ case Qkey: - data = f->user->key; + data = (char*)f->user->key.des; m = DESKEYLEN; break; case Qaeskey: - data = (char*)f->user->aeskey; + data = (char*)f->user->key.aes; m = AESKEYLEN; break; + case Qpakhash: + data = (char*)f->user->key.pakhash; + m = PAKHASHLEN; + break; case Qsecret: data = f->user->secret; Readstr: @@ -531,14 +538,15 @@ Write(Fid *f) case Qkey: if(n != DESKEYLEN) return "garbled write data"; - memmove(f->user->key, data, DESKEYLEN); - thdr.count = DESKEYLEN; + memmove(f->user->key.des, data, n); + thdr.count = n; break; case Qaeskey: if(n != AESKEYLEN) return "garbled write data"; - memmove(f->user->aeskey, data, AESKEYLEN); - thdr.count = AESKEYLEN; + memmove(f->user->key.aes, data, n); + authpak_hash(&f->user->key, f->user->name); + thdr.count = n; break; case Qsecret: if(n >= SECRETLEN) @@ -728,7 +736,7 @@ writeusers(void) for(u = users[i]; u != nil; u = u->link){ strncpy((char*)p, u->name, Namelen); p += Namelen; - memmove(p, u->key, DESKEYLEN); + memmove(p, u->key.des, DESKEYLEN); p += DESKEYLEN; *p++ = u->status; *p++ = u->warnings; @@ -740,7 +748,7 @@ writeusers(void) memmove(p, u->secret, SECRETLEN); p += SECRETLEN; if(keydbaes){ - memmove(p, u->aeskey, AESKEYLEN); + memmove(p, u->key.aes, AESKEYLEN); p += AESKEYLEN; } } @@ -773,6 +781,8 @@ writeusers(void) free(buf); close(fd); + + newkeys(); } int @@ -897,7 +907,7 @@ readusers(void) u = finduser((char*)ep); if(u == nil) u = installuser((char*)ep); - memmove(u->key, ep + Namelen, DESKEYLEN); + memmove(u->key.des, ep + Namelen, DESKEYLEN); p = ep + Namelen + DESKEYLEN; u->status = *p++; u->warnings = *p++; @@ -908,8 +918,10 @@ readusers(void) memmove(u->secret, p, SECRETLEN); u->secret[SECRETLEN-1] = 0; p += SECRETLEN; - if(keydbaes) - memmove(u->aeskey, p, AESKEYLEN); + if(keydbaes){ + memmove(u->key.aes, p, AESKEYLEN); + authpak_hash(&u->key, u->name); + } nu++; } free(buf); diff --git a/sys/src/cmd/auth/lib/mkfile b/sys/src/cmd/auth/lib/mkfile index 9e54d35fc..dd26e966a 100644 --- a/sys/src/cmd/auth/lib/mkfile +++ b/sys/src/cmd/auth/lib/mkfile @@ -6,6 +6,7 @@ OFILES=\ keyfmt.$O\ netcheck.$O\ okpasswd.$O\ + private.$O\ readwrite.$O\ readarg.$O\ readln.$O\ diff --git a/sys/src/cmd/auth/lib/readwrite.c b/sys/src/cmd/auth/lib/readwrite.c index b5a01e91e..ccc08a6a4 100644 --- a/sys/src/cmd/auth/lib/readwrite.c +++ b/sys/src/cmd/auth/lib/readwrite.c @@ -66,8 +66,14 @@ findkey(char *db, char *user, Authkey *key) int ret; memset(key, 0, sizeof(Authkey)); - ret = finddeskey(db, user, key->des) != nil; - ret |= findaeskey(db, user, key->aes) != nil; + ret = findaeskey(db, user, key->aes) != nil; + if(ret){ + char filename[Maxpath]; + snprint(filename, sizeof filename, "%s/%s/pakhash", db, user); + if(readfile(filename, (char*)key->pakhash, PAKHASHLEN) != PAKHASHLEN) + authpak_hash(key, user); + } + ret |= finddeskey(db, user, key->des) != nil; return ret; } diff --git a/sys/src/cmd/auth/netkey.c b/sys/src/cmd/auth/netkey.c index faca0f3d5..30ac5602c 100644 --- a/sys/src/cmd/auth/netkey.c +++ b/sys/src/cmd/auth/netkey.c @@ -15,8 +15,7 @@ usage(void) void main(int argc, char *argv[]) { - Authkey key; - char buf[32], pass[32]; + char buf[32], pass[32], key[DESKEYLEN]; char *s; int n; @@ -34,7 +33,7 @@ main(int argc, char *argv[]) } readln("Password: ", pass, sizeof pass, 1); - passtokey(&key, pass); + passtodeskey(key, pass); for(;;){ print("challenge: "); @@ -44,7 +43,7 @@ main(int argc, char *argv[]) buf[n] = '\0'; n = strtol(buf, 0, 10); sprint(buf, "%d", n); - netcrypt(key.des, buf); + netcrypt(key, buf); print("response: %s\n", buf); } } diff --git a/sys/src/cmd/auth/passwd.c b/sys/src/cmd/auth/passwd.c index 8e979092b..c7a526622 100644 --- a/sys/src/cmd/auth/passwd.c +++ b/sys/src/cmd/auth/passwd.c @@ -7,7 +7,7 @@ void main(int argc, char **argv) { - int fd, n; + int fd, n, try; Ticketreq tr; Ticket t; Passwordreq pr; @@ -20,6 +20,8 @@ main(int argc, char **argv) ARGBEGIN{ }ARGEND + private(); + s = nil; if(argc > 0){ user = argv[0]; @@ -34,6 +36,10 @@ main(int argc, char **argv) if(fd < 0) error("authdial: %r"); + memset(&tr, 0, sizeof(tr)); + strncpy(tr.uid, user, sizeof(tr.uid)-1); + tr.type = AuthPass; + /* * get a password from the user and try to decrypt the * ticket. If it doesn't work we've got a bad password, @@ -43,18 +49,31 @@ main(int argc, char **argv) readln("Plan 9 Password: ", pr.old, sizeof pr.old, 1); passtokey(&key, pr.old); - memset(&tr, 0, sizeof(tr)); - strcpy(tr.uid, user); - tr.type = AuthPass; - + /* + * negotiate PAK key. we need to retry in case the AS does + * not support the AuthPAK request or when the user has + * not yet setup a new key and the AS made one up. + */ + try = 0; + authpak_hash(&key, tr.uid); + if(_asgetpakkey(fd, &tr, &key) < 0){ +Retry: + try++; + close(fd); + fd = authdial(nil, s); + if(fd < 0) + error("authdial: %r"); + } /* send ticket request to AS */ if(_asrequest(fd, &tr) < 0) error("%r"); if(_asgetresp(fd, &t, nil, &key) < 0) error("%r"); - - if(t.num != AuthTp || strcmp(t.cuid, tr.uid) != 0) + if(t.num != AuthTp || strcmp(t.cuid, tr.uid) != 0){ + if(try == 0) + goto Retry; error("bad password"); + } /* loop trying new passwords */ for(;;){ @@ -62,8 +81,7 @@ main(int argc, char **argv) *pr.new = 0; readln("change Plan 9 Password? (y/n) ", buf, sizeof buf, 0); if(*buf == 'y' || *buf == 'Y'){ - readln("Password(8 to 31 characters): ", pr.new, - sizeof pr.new, 1); + readln("Password: ", pr.new, sizeof pr.new, 1); readln("Confirm: ", buf, sizeof buf, 1); if(strcmp(pr.new, buf)){ print("!mismatch\n"); @@ -81,8 +99,7 @@ main(int argc, char **argv) else strcpy(pr.secret, pr.new); } else { - readln("Secret(0 to 256 characters): ", pr.secret, - sizeof pr.secret, 1); + readln("Secret: ", pr.secret, sizeof pr.secret, 1); readln("Confirm: ", buf, sizeof buf, 1); if(strcmp(pr.secret, buf)){ print("!mismatch\n"); diff --git a/sys/src/libauth/auth_userpasswd.c b/sys/src/libauth/auth_userpasswd.c index e113d83a4..b5a2c89a9 100644 --- a/sys/src/libauth/auth_userpasswd.c +++ b/sys/src/libauth/auth_userpasswd.c @@ -11,13 +11,13 @@ * this was copied from inet's guard. */ static void -netresp(Authkey *key, long chal, char *answer) +netresp(char key[DESKEYLEN], long chal, char *answer) { uchar buf[8]; memset(buf, 0, sizeof buf); snprint((char *)buf, sizeof buf, "%lud", chal); - if(encrypt(key->des, buf, 8) < 0) + if(encrypt(key, buf, 8) < 0) abort(); sprint(answer, "%.8ux", buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]); } @@ -25,8 +25,7 @@ netresp(Authkey *key, long chal, char *answer) AuthInfo* auth_userpasswd(char *user, char *passwd) { - char resp[16]; - Authkey key; + char resp[16], key[DESKEYLEN]; AuthInfo *ai; Chalstate *ch; @@ -38,9 +37,9 @@ auth_userpasswd(char *user, char *passwd) if((ch = auth_challenge("user=%q proto=p9cr role=server", user)) == nil) return nil; - passtokey(&key, passwd); - netresp(&key, atol(ch->chal), resp); - memset(&key, 0, sizeof(Authkey)); + passtodeskey(key, passwd); + netresp(key, atol(ch->chal), resp); + memset(key, 0, sizeof(key)); ch->resp = resp; ch->nresp = strlen(resp); diff --git a/sys/src/libauthsrv/_asgetpakkey.c b/sys/src/libauthsrv/_asgetpakkey.c new file mode 100644 index 000000000..c30b16b84 --- /dev/null +++ b/sys/src/libauthsrv/_asgetpakkey.c @@ -0,0 +1,26 @@ +#include +#include +#include + +int +_asgetpakkey(int fd, Ticketreq *tr, Authkey *a) +{ + uchar y[PAKYLEN]; + PAKpriv p; + int type; + + type = tr->type; + tr->type = AuthPAK; + if(_asrequest(fd, tr) != 0){ + tr->type = type; + return -1; + } + tr->type = type; + authpak_new(&p, a, y, 1); + if(write(fd, y, PAKYLEN) != PAKYLEN + || _asrdresp(fd, (char*)y, PAKYLEN) != PAKYLEN){ + memset(&p, 0, sizeof(p)); + return -1; + } + return authpak_finish(&p, a, y); +} diff --git a/sys/src/libauthsrv/_asgetresp.c b/sys/src/libauthsrv/_asgetresp.c index da0e2bd20..3f7cb5d4f 100644 --- a/sys/src/libauthsrv/_asgetresp.c +++ b/sys/src/libauthsrv/_asgetresp.c @@ -5,28 +5,40 @@ int _asgetresp(int fd, Ticket *t, Authenticator *a, Authkey *k) { - char tbuf[TICKETLEN+AUTHENTLEN]; + char buf[MAXTICKETLEN+MAXAUTHENTLEN], err[ERRMAX]; int n, m; - m = TICKETLEN; memset(t, 0, sizeof(Ticket)); - if(a != nil){ - m += AUTHENTLEN; + if(a != nil) memset(a, 0, sizeof(Authenticator)); - } - n = _asrdresp(fd, tbuf, m); - if(n <= 0) + strcpy(err, "AS protocol botch"); + errstr(err, ERRMAX); + + if(_asrdresp(fd, buf, 0) < 0) return -1; - m = convM2T(tbuf, n, t, k); - if(m <= 0) - return -1; - - if(a != nil){ - if(convM2A(tbuf+m, n-m, a, t) <= 0) + for(n = 0; (m = convM2T(buf, n, t, k)) <= 0; n += m){ + m = -m; + if(m <= n || m > sizeof(buf)) + return -1; + m -= n; + if(readn(fd, buf+n, m) != m) return -1; } + if(a != nil){ + for(n = 0; (m = convM2A(buf, n, a, t)) <= 0; n += m){ + m = -m; + if(m <= n || m > sizeof(buf)) + return -1; + m -= n; + if(readn(fd, buf+n, m) != m) + return -1; + } + } + + errstr(err, ERRMAX); + return 0; } diff --git a/sys/src/libauthsrv/_asgetticket.c b/sys/src/libauthsrv/_asgetticket.c index 11a344621..d7e30b2a8 100644 --- a/sys/src/libauthsrv/_asgetticket.c +++ b/sys/src/libauthsrv/_asgetticket.c @@ -2,16 +2,36 @@ #include #include -static char *pbmsg = "AS protocol botch"; - int _asgetticket(int fd, Ticketreq *tr, char *tbuf, int tbuflen) { - if(_asrequest(fd, tr) < 0){ - werrstr(pbmsg); + char err[ERRMAX]; + int i, n, m, r; + + strcpy(err, "AS protocol botch"); + errstr(err, ERRMAX); + + if(_asrequest(fd, tr) < 0) return -1; + if(_asrdresp(fd, tbuf, 0) < 0) + return -1; + + r = 0; + for(i = 0; i<2; i++){ + for(n=0; (m = convM2T(tbuf, n, nil, nil)) <= 0; n += m){ + m = -m; + if(m <= n || m > tbuflen) + return -1; + m -= n; + if(readn(fd, tbuf+n, m) != m) + return -1; + } + r += n; + tbuf += n; + tbuflen -= n; } - if(tbuflen > 2*TICKETLEN) - tbuflen = 2*TICKETLEN; - return _asrdresp(fd, tbuf, tbuflen); + + errstr(err, ERRMAX); + + return r; } diff --git a/sys/src/libauthsrv/authpak.c b/sys/src/libauthsrv/authpak.c new file mode 100644 index 000000000..50f7ee6d0 --- /dev/null +++ b/sys/src/libauthsrv/authpak.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include + +#include "msqrt.mpc" +#include "decaf.mpc" +#include "edwards.mpc" +#include "elligator2.mpc" +#include "spake2ee.mpc" +#include "ed448.mpc" + +typedef struct PAKcurve PAKcurve; +struct PAKcurve +{ + Lock; + mpint *P; + mpint *A; + mpint *D; + mpint *X; + mpint *Y; +}; + +static PAKcurve* +authpak_curve(void) +{ + static PAKcurve a; + + lock(&a); + if(a.P == nil){ + a.P = mpnew(0); + a.A = mpnew(0); + a.D = mpnew(0); + a.X = mpnew(0); + a.Y = mpnew(0); + ed448_curve(a.P, a.A, a.D, a.X, a.Y); + a.P = mpfield(a.P); + } + unlock(&a); + return &a; +} + +void +authpak_hash(Authkey *k, char *u) +{ + static char info[] = "Plan 9 AuthPAK hash"; + uchar *bp, salt[SHA2_256dlen], h[2*PAKSLEN]; + mpint *H, *PX,*PY,*PZ,*PT; + PAKcurve *c; + + H = mpnew(0); + PX = mpnew(0); + PY = mpnew(0); + PZ = mpnew(0); + PT = mpnew(0); + + sha2_256((uchar*)u, strlen(u), salt, nil); + + hkdf_x( salt, SHA2_256dlen, + (uchar*)info, sizeof(info)-1, + k->aes, AESKEYLEN, + h, sizeof(h), + hmac_sha2_256, SHA2_256dlen); + + c = authpak_curve(); + + betomp(h + 0*PAKSLEN, PAKSLEN, H); /* HM */ + spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT); /* PM */ + + bp = k->pakhash; + mptober(PX, bp, PAKSLEN), bp += PAKSLEN; + mptober(PY, bp, PAKSLEN), bp += PAKSLEN; + mptober(PZ, bp, PAKSLEN), bp += PAKSLEN; + mptober(PT, bp, PAKSLEN), bp += PAKSLEN; + + betomp(h + 1*PAKSLEN, PAKSLEN, H); /* HN */ + spake2ee_h2P(c->P,c->A,c->D, H, PX,PY,PZ,PT); /* PN */ + + mptober(PX, bp, PAKSLEN), bp += PAKSLEN; + mptober(PY, bp, PAKSLEN), bp += PAKSLEN; + mptober(PZ, bp, PAKSLEN), bp += PAKSLEN; + mptober(PT, bp, PAKSLEN); + + mpfree(PX); + mpfree(PY); + mpfree(PZ); + mpfree(PT); + mpfree(H); +} + +void +authpak_new(PAKpriv *p, Authkey *k, uchar y[PAKYLEN], int isclient) +{ + mpint *PX,*PY,*PZ,*PT, *X, *Y; + PAKcurve *c; + uchar *bp; + + memset(p, 0, sizeof(PAKpriv)); + p->isclient = isclient != 0; + + X = mpnew(0); + Y = mpnew(0); + + PX = mpnew(0); + PY = mpnew(0); + PZ = mpnew(0); + PT = mpnew(0); + + PX->flags |= MPtimesafe; + PY->flags |= MPtimesafe; + PZ->flags |= MPtimesafe; + PT->flags |= MPtimesafe; + + bp = k->pakhash + PAKPLEN*(p->isclient == 0); + betomp(bp, PAKSLEN, PX), bp += PAKSLEN; + betomp(bp, PAKSLEN, PY), bp += PAKSLEN; + betomp(bp, PAKSLEN, PZ), bp += PAKSLEN; + betomp(bp, PAKSLEN, PT); + + c = authpak_curve(); + + X->flags |= MPtimesafe; + mpnrand(c->P, genrandom, X); + + spake2ee_1(c->P,c->A,c->D, X, c->X,c->Y, PX,PY,PZ,PT, Y); + + mptober(X, p->x, PAKXLEN); + mptober(Y, p->y, PAKYLEN); + + memmove(y, p->y, PAKYLEN); + + mpfree(PX); + mpfree(PY); + mpfree(PZ); + mpfree(PT); + + mpfree(X); + mpfree(Y); +} + +int +authpak_finish(PAKpriv *p, Authkey *k, uchar y[PAKYLEN]) +{ + static char info[] = "Plan 9 AuthPAK key"; + uchar *bp, z[PAKSLEN], salt[SHA2_256dlen]; + mpint *PX,*PY,*PZ,*PT, *X, *Y, *Z, *ok; + DigestState *s; + PAKcurve *c; + int ret; + + X = mpnew(0); + Y = mpnew(0); + Z = mpnew(0); + ok = mpnew(0); + + PX = mpnew(0); + PY = mpnew(0); + PZ = mpnew(0); + PT = mpnew(0); + + PX->flags |= MPtimesafe; + PY->flags |= MPtimesafe; + PZ->flags |= MPtimesafe; + PT->flags |= MPtimesafe; + + bp = k->pakhash + PAKPLEN*(p->isclient != 0); + betomp(bp, PAKSLEN, PX), bp += PAKSLEN; + betomp(bp, PAKSLEN, PY), bp += PAKSLEN; + betomp(bp, PAKSLEN, PZ), bp += PAKSLEN; + betomp(bp, PAKSLEN, PT); + + Z->flags |= MPtimesafe; + X->flags |= MPtimesafe; + betomp(p->x, PAKXLEN, X); + + betomp(y, PAKYLEN, Y); + + c = authpak_curve(); + spake2ee_2(c->P,c->A,c->D, PX,PY,PZ,PT, X, Y, ok, Z); + + if(mpcmp(ok, mpzero) == 0){ + ret = -1; + goto out; + } + + mptober(Z, z, sizeof(z)); + + s = sha2_256(p->isclient ? p->y : y, PAKYLEN, nil, nil); + sha2_256(p->isclient ? y : p->y, PAKYLEN, salt, s); + + hkdf_x( salt, SHA2_256dlen, + (uchar*)info, sizeof(info)-1, + z, sizeof(z), + k->pakkey, PAKKEYLEN, + hmac_sha2_256, SHA2_256dlen); + + ret = 0; +out: + memset(z, 0, sizeof(z)); + memset(p, 0, sizeof(PAKpriv)); + + mpfree(PX); + mpfree(PY); + mpfree(PZ); + mpfree(PT); + + mpfree(X); + mpfree(Y); + mpfree(Z); + mpfree(ok); + + return ret; +} diff --git a/sys/src/libauthsrv/convA2M.c b/sys/src/libauthsrv/convA2M.c index d285ad770..4db2df39e 100644 --- a/sys/src/libauthsrv/convA2M.c +++ b/sys/src/libauthsrv/convA2M.c @@ -2,26 +2,35 @@ #include #include -#define CHAR(x) *p++ = f->x -#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 -#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(p, f->x, n); p += n +extern int form1B2M(char *ap, int n, uchar key[32]); int convA2M(Authenticator *f, char *ap, int n, Ticket *t) { uchar *p; - if(n < AUTHENTLEN) + if(n < 1+CHALLEN) return 0; p = (uchar*)ap; - CHAR(num); - STRING(chal, CHALLEN); - LONG(id); - n = p - (uchar*)ap; - if(t) + *p++ = f->num; + memmove(p, f->chal, CHALLEN), p += CHALLEN; + switch(t->form){ + case 0: + if(n < 1+CHALLEN+4) + return 0; + + memset(p, 0, 4), p += 4; /* unused id field */ + n = p - (uchar*)ap; encrypt(t->key, ap, n); - return n; + return n; + case 1: + if(n < 12+CHALLEN+NONCELEN+16) + return 0; + + memmove(p, f->rand, NONCELEN), p += NONCELEN; + return form1B2M(ap, (char*)p - ap, t->key); + } + + return 0; } diff --git a/sys/src/libauthsrv/convM2A.c b/sys/src/libauthsrv/convM2A.c index b0d6712ca..0da7865fe 100644 --- a/sys/src/libauthsrv/convM2A.c +++ b/sys/src/libauthsrv/convM2A.c @@ -2,30 +2,35 @@ #include #include -#define CHAR(x) f->x = *p++ -#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 -#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(f->x, p, n); p += n +extern int form1M2B(char *ap, int n, uchar key[32]); int convM2A(char *ap, int n, Authenticator *f, Ticket *t) { - uchar *p, buf[AUTHENTLEN]; + uchar buf[MAXAUTHENTLEN], *p; + int m; memset(f, 0, sizeof(Authenticator)); - if(n < AUTHENTLEN) - return -AUTHENTLEN; - - if(t) { - memmove(buf, ap, AUTHENTLEN); - ap = (char*)buf; - decrypt(t->key, ap, AUTHENTLEN); + if(t->form == 0){ + m = 1+CHALLEN+4; + if(n < m) + return -m; + memmove(buf, ap, m); + decrypt(t->key, buf, m); + } else { + m = 12+CHALLEN+NONCELEN+16; + if(n < m) + return -m; + memmove(buf, ap, m); + if(form1M2B((char*)buf, m, t->key) < 0) + return m; } - p = (uchar*)ap; - CHAR(num); - STRING(chal, CHALLEN); - LONG(id); - n = p - (uchar*)ap; - return n; + p = buf; + f->num = *p++; + memmove(f->chal, p, CHALLEN); + p += CHALLEN; + if(t->form == 1) + memmove(f->rand, p, NONCELEN); + + return m; } diff --git a/sys/src/libauthsrv/convM2PR.c b/sys/src/libauthsrv/convM2PR.c index c7bd4974d..ecc46e649 100644 --- a/sys/src/libauthsrv/convM2PR.c +++ b/sys/src/libauthsrv/convM2PR.c @@ -2,35 +2,38 @@ #include #include -#define CHAR(x) f->x = *p++ -#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 -#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(f->x, p, n); p += n +extern int form1M2B(char *ap, int n, uchar key[32]); int convM2PR(char *ap, int n, Passwordreq *f, Ticket *t) { - uchar *p, buf[PASSREQLEN]; + uchar *p, buf[MAXPASSREQLEN]; + int m; memset(f, 0, sizeof(Passwordreq)); - if(n < PASSREQLEN) - return -PASSREQLEN; - - if(t){ - memmove(buf, ap, PASSREQLEN); - ap = (char*)buf; - decrypt(t->key, ap, PASSREQLEN); + if(t->form == 0){ + m = 1+2*ANAMELEN+1+SECRETLEN; + if(n < m) + return -m; + memmove(buf, ap, m); + decrypt(t->key, buf, m); + } else { + m = 12+2*ANAMELEN+1+SECRETLEN+16; + if(n < m) + return -m; + memmove(buf, ap, m); + if(form1M2B((char*)buf, m, t->key) < 0) + return m; } - p = (uchar*)ap; - CHAR(num); - STRING(old, ANAMELEN); + p = buf; + f->num = *p++; + memmove(f->old, p, ANAMELEN), p += ANAMELEN; + memmove(f->new, p, ANAMELEN), p += ANAMELEN; + f->changesecret = *p++; + memmove(f->secret, p, SECRETLEN); f->old[ANAMELEN-1] = 0; - STRING(new, ANAMELEN); f->new[ANAMELEN-1] = 0; - CHAR(changesecret); - STRING(secret, SECRETLEN); f->secret[SECRETLEN-1] = 0; - n = p - (uchar*)ap; - return n; + + return m; } diff --git a/sys/src/libauthsrv/convM2T.c b/sys/src/libauthsrv/convM2T.c index 425ebefdd..cfe97b734 100644 --- a/sys/src/libauthsrv/convM2T.c +++ b/sys/src/libauthsrv/convM2T.c @@ -2,34 +2,50 @@ #include #include -#define CHAR(x) f->x = *p++ -#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 -#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(f->x, p, n); p += n +extern int form1check(char *ap, int n); +extern int form1M2B(char *ap, int n, uchar key[32]); int -convM2T(char *ap, int n, Ticket *f, Authkey *key) +convM2T(char *ap, int n, Ticket *f, Authkey *k) { - uchar *p, buf[TICKETLEN]; + uchar buf[MAXTICKETLEN], *p; + int m; - memset(f, 0, sizeof(Ticket)); - if(n < TICKETLEN) - return -TICKETLEN; + if(f != nil) + memset(f, 0, sizeof(Ticket)); - if(key){ - memmove(buf, ap, TICKETLEN); - ap = (char*)buf; - decrypt(key->des, ap, TICKETLEN); + if(n < 8) + return -8; + + if(form1check(ap, n) < 0){ + m = 1+CHALLEN+2*ANAMELEN+DESKEYLEN; + if(n < m) + return -m; + if(f == nil || k == nil) + return m; + f->form = 0; + memmove(buf, ap, m); + decrypt(k->des, buf, m); + } else { + m = 12+CHALLEN+2*ANAMELEN+NONCELEN+16; + if(n < m) + return -m; + if(f == nil || k == nil) + return m; + f->form = 1; + memmove(buf, ap, m); + if(form1M2B((char*)buf, m, k->pakkey) < 0) + return m; } - p = (uchar*)ap; - CHAR(num); - STRING(chal, CHALLEN); - STRING(cuid, ANAMELEN); + p = buf; + f->num = *p++; + memmove(f->chal, p, CHALLEN), p += CHALLEN; + memmove(f->cuid, p, ANAMELEN), p += ANAMELEN; + memmove(f->suid, p, ANAMELEN), p += ANAMELEN; + memmove(f->key, p, f->form == 0 ? DESKEYLEN : NONCELEN); + f->cuid[ANAMELEN-1] = 0; - STRING(suid, ANAMELEN); f->suid[ANAMELEN-1] = 0; - STRING(key, DESKEYLEN); - n = p - (uchar*)ap; - return n; + + return m; } diff --git a/sys/src/libauthsrv/convM2TR.c b/sys/src/libauthsrv/convM2TR.c index d6491705b..79749873c 100644 --- a/sys/src/libauthsrv/convM2TR.c +++ b/sys/src/libauthsrv/convM2TR.c @@ -2,12 +2,6 @@ #include #include -#define CHAR(x) f->x = *p++ -#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 -#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(f->x, p, n); p += n - int convM2TR(char *ap, int n, Ticketreq *f) { @@ -18,16 +12,18 @@ convM2TR(char *ap, int n, Ticketreq *f) return -TICKREQLEN; p = (uchar*)ap; - CHAR(type); - STRING(authid, ANAMELEN); + f->type = *p++; + memmove(f->authid, p, ANAMELEN), p += ANAMELEN; + memmove(f->authdom, p, DOMLEN), p += DOMLEN; + memmove(f->chal, p, CHALLEN), p += CHALLEN; + memmove(f->hostid, p, ANAMELEN), p += ANAMELEN; + memmove(f->uid, p, ANAMELEN), p += ANAMELEN; + f->authid[ANAMELEN-1] = 0; - STRING(authdom, DOMLEN); f->authdom[DOMLEN-1] = 0; - STRING(chal, CHALLEN); - STRING(hostid, ANAMELEN); f->hostid[ANAMELEN-1] = 0; - STRING(uid, ANAMELEN); f->uid[ANAMELEN-1] = 0; n = p - (uchar*)ap; + return n; } diff --git a/sys/src/libauthsrv/convPR2M.c b/sys/src/libauthsrv/convPR2M.c index d5c0ee7ac..0f3463ef2 100644 --- a/sys/src/libauthsrv/convPR2M.c +++ b/sys/src/libauthsrv/convPR2M.c @@ -2,29 +2,33 @@ #include #include -#define CHAR(x) *p++ = f->x -#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 -#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(p, f->x, n); p += n +extern int form1B2M(char *ap, int n, uchar key[32]); int convPR2M(Passwordreq *f, char *ap, int n, Ticket *t) { uchar *p; - if(n < PASSREQLEN) + if(n < 1+2*ANAMELEN+1+SECRETLEN) return 0; p = (uchar*)ap; - CHAR(num); - STRING(old, ANAMELEN); - STRING(new, ANAMELEN); - CHAR(changesecret); - STRING(secret, SECRETLEN); - n = p - (uchar*)ap; - if(t) + *p++ = f->num; + memmove(p, f->old, ANAMELEN), p += ANAMELEN; + memmove(p, f->new, ANAMELEN), p += ANAMELEN; + *p++ = f->changesecret; + memmove(p, f->secret, SECRETLEN), p += SECRETLEN; + switch(t->form){ + case 0: + n = p - (uchar*)ap; encrypt(t->key, ap, n); - return n; + return n; + case 1: + if(n < 12+2*ANAMELEN+1+SECRETLEN+16) + return 0; + return form1B2M(ap, p - (uchar*)ap, t->key); + } + + return 0; } diff --git a/sys/src/libauthsrv/convT2M.c b/sys/src/libauthsrv/convT2M.c index 77edcef1f..5f1243498 100644 --- a/sys/src/libauthsrv/convT2M.c +++ b/sys/src/libauthsrv/convT2M.c @@ -1,29 +1,39 @@ #include #include #include +#include -#define CHAR(x) *p++ = f->x -#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 -#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(p, f->x, n); p += n +extern int form1B2M(char *ap, int n, uchar key[32]); int convT2M(Ticket *f, char *ap, int n, Authkey *key) { uchar *p; - if(n < TICKETLEN) + if(n < 1+CHALLEN+2*ANAMELEN) return 0; p = (uchar*)ap; - CHAR(num); - STRING(chal, CHALLEN); - STRING(cuid, ANAMELEN); - STRING(suid, ANAMELEN); - STRING(key, DESKEYLEN); - n = p - (uchar*)ap; - if(key) + *p++ = f->num; + memmove(p, f->chal, CHALLEN), p += CHALLEN; + memmove(p, f->cuid, ANAMELEN), p += ANAMELEN; + memmove(p, f->suid, ANAMELEN), p += ANAMELEN; + switch(f->form){ + case 0: + if(n < 1+CHALLEN+2*ANAMELEN+DESKEYLEN) + return 0; + + memmove(p, f->key, DESKEYLEN), p += DESKEYLEN; + n = p - (uchar*)ap; encrypt(key->des, ap, n); - return n; + return n; + case 1: + if(n < 12+CHALLEN+2*ANAMELEN+NONCELEN+16) + return 0; + + memmove(p, f->key, NONCELEN), p += NONCELEN; + return form1B2M(ap, p - (uchar*)ap, key->pakkey); + } + + return 0; } diff --git a/sys/src/libauthsrv/convTR2M.c b/sys/src/libauthsrv/convTR2M.c index 0bbe9cb6f..8664ed066 100644 --- a/sys/src/libauthsrv/convTR2M.c +++ b/sys/src/libauthsrv/convTR2M.c @@ -2,12 +2,6 @@ #include #include -#define CHAR(x) *p++ = f->x -#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 -#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 -#define LONG(x) VLONG(f->x) -#define STRING(x,n) memmove(p, f->x, n); p += n - int convTR2M(Ticketreq *f, char *ap, int n) { @@ -17,12 +11,13 @@ convTR2M(Ticketreq *f, char *ap, int n) return 0; p = (uchar*)ap; - CHAR(type); - STRING(authid, 28); /* BUG */ - STRING(authdom, DOMLEN); - STRING(chal, CHALLEN); - STRING(hostid, 28); /* BUG */ - STRING(uid, 28); /* BUG */ + *p++ = f->type; + memmove(p, f->authid, ANAMELEN), p += ANAMELEN; + memmove(p, f->authdom, DOMLEN), p += DOMLEN; + memmove(p, f->chal, CHALLEN), p += CHALLEN; + memmove(p, f->hostid, ANAMELEN), p += ANAMELEN; + memmove(p, f->uid, ANAMELEN), p += ANAMELEN; n = p - (uchar*)ap; + return n; } diff --git a/sys/src/libauthsrv/decaf.mp b/sys/src/libauthsrv/decaf.mp new file mode 100644 index 000000000..509e3edd7 --- /dev/null +++ b/sys/src/libauthsrv/decaf.mp @@ -0,0 +1,49 @@ +# negate r when n > (p-1)/2 +decaf_neg(p, n, r) { + mod(p) m = -r; + r = n > (p-1)>>1 ? m : r; +} + +# field F_p +# curve a*x**2+y**2==1+d*x**2*y**2 +# input X,Y,Z,T (extended coordinates) +decaf_encode(p,a,d, X,Y,Z,T, s) mod(p) { + r = misqrt((a-d)*(Z+Y)*(Z-Y), p); + u = (a-d)*r; + decaf_neg(p, -2*u*Z, r); + s = u*(r*(a*Z*X-d*Y*T)+Y)/a; + decaf_neg(p, s, s); +} + +# field F_p +# curve a*x**2+y**2==1+d*x**2*y**2 +# input s +# output in extended coordinates +decaf_decode(p,a,d, s, ok,X,Y,Z,T) { + if(s > (p-1)>>1){ + ok = 0; + } else mod(p) { + ss = s^2; + Z = 1+a*ss; + u = Z^2 - 4*d*ss; + v = u*ss; + if(v == 0) + ok = 1; + else { + ok = msqrt(v, p); + if(ok != 0){ + v = 1/ok; + ok = 1; + } + } + if(ok != 0) { + decaf_neg(p, u*v, v); + w = v * s * (2-Z); + if(s == 0) + w = w + 1; + X = 2*s; + Y = w * Z; + T = w * X; + } + } +} diff --git a/sys/src/libauthsrv/ed448.mp b/sys/src/libauthsrv/ed448.mp new file mode 100644 index 000000000..89c57727b --- /dev/null +++ b/sys/src/libauthsrv/ed448.mp @@ -0,0 +1,11 @@ +# Edwards Curve Ed448-Goldilocks + +# x^2+y^2 = 1-39081x^2y^2 +# modulo p = 2^448 - 1^224 - 1 +ed448_curve(p,a,d,x,y) { + p = (1<<448) - (1<<224) - 1; + a = 1; + d = -39081; + x = 117812161263436946737282484343310064665180535357016373416879082147939404277809514858788439644911793978499419995990477371552926308078495; + y = 19; +} diff --git a/sys/src/libauthsrv/edwards.mp b/sys/src/libauthsrv/edwards.mp new file mode 100644 index 000000000..33305b71a --- /dev/null +++ b/sys/src/libauthsrv/edwards.mp @@ -0,0 +1,40 @@ +# Edwards curve arithmetic +edwards_add(p,a,d, X1,Y1,Z1,T1, X2,Y2,Z2,T2, X3,Y3,Z3,T3) mod(p) { + A = X1*X2; + B = Y1*Y2; + C = d*T1*T2; + D = Z1*Z2; + E = (X1+Y1)*(X2+Y2); + E = E - A - B; + F = D - C; + G = D + C; + H = B - a*A; + X3 = E*F; + Y3 = G*H; + Z3 = F*G; + T3 = E*H; +} +edwards_sel(s, X1,Y1,Z1,T1, X2,Y2,Z2,T2, X3,Y3,Z3,T3){ + X3 = s != 0 ? X1 : X2; + Y3 = s != 0 ? Y1 : Y2; + Z3 = s != 0 ? Z1 : Z2; + T3 = s != 0 ? T1 : T2; +} +edwards_new(x,y,z,t, X,Y,Z,T) { + X = x; + Y = y; + Z = z; + T = t; +} +edwards_scale(p,a,d, s, X1,Y1,Z1,T1, X3,Y3,Z3,T3) { + X2,Y2,Z2,T2 = edwards_new(X1,Y1,Z1,T1); + X4,Y4,Z4,T4 = edwards_new( 0, 1, 1, 0); + X3,Y3,Z3,T3 = edwards_sel(s % 2, X2,Y2,Z2,T2, X4,Y4,Z4,T4); + k = s >> 1; j = p >> 1; + while(j != 0){ + X2,Y2,Z2,T2 = edwards_add(p,a,d, X2,Y2,Z2,T2, X2,Y2,Z2,T2); + X4,Y4,Z4,T4 = edwards_add(p,a,d, X2,Y2,Z2,T2, X3,Y3,Z3,T3); + X3,Y3,Z3,T3 = edwards_sel(k % 2, X4,Y4,Z4,T4, X3,Y3,Z3,T3); + k = k >> 1; j = j >> 1; + } +} diff --git a/sys/src/libauthsrv/elligator2.mp b/sys/src/libauthsrv/elligator2.mp new file mode 100644 index 000000000..820adc771 --- /dev/null +++ b/sys/src/libauthsrv/elligator2.mp @@ -0,0 +1,30 @@ +#elligator2: +# curve a*x^2+y^2==1+d*x^2*y^2 +# input r0 +# n is any non-square +# +elligator2(p,a,d, n, r0, X,Y,Z,T) mod(p) { + r = n*r0*r0; + D = (d*r+a-d)*(d*r-a*r-d); + N = (r+1)*(a-2*d); + ND = N*D; + if(ND == 0) { + c = 1; + e = 0; + } else { + e = msqrt(ND, p); + if(e != 0) { + c = 1; + e = 1/e; + } else { + c = -1; + e = n*r0*misqrt(n*ND, p); + } + } + s = c*N*e; + t = -c*N*(r-1)*((a-2*d)*e)^2-1; + X = 2*s*t; + Y = (1-a*s*s)*(1+a*s*s); + Z = (1+a*s*s)*t; + T = (2*s)*(1-a*s*s); +} diff --git a/sys/src/libauthsrv/form1.c b/sys/src/libauthsrv/form1.c new file mode 100644 index 000000000..9ff4a1fad --- /dev/null +++ b/sys/src/libauthsrv/form1.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +/* + * new ticket format: the reply protector/type is replaced by a + * 8 byte signature and a 4 byte counter forming the 12 byte + * nonce for chacha20/poly1305 encryption. a 16 byte poly1305 + * authentication tag is appended for message authentication. + * the counter is needed for the AuthPass message which uses + * the same key for several messages. + */ + +static struct { + char num; + char sig[8]; +} form1sig[] = { + AuthPass, "form1 PR", /* password change request encrypted with ticket key */ + AuthTs, "form1 Ts", /* ticket encrypted with server's key */ + AuthTc, "form1 Tc", /* ticket encrypted with client's key */ + AuthAs, "form1 As", /* server generated authenticator */ + AuthAc, "form1 Ac", /* client generated authenticator */ + AuthTp, "form1 Tp", /* ticket encrypted with client's key for password change */ + AuthHr, "form1 Hr", /* http reply */ +}; + +int +form1check(char *ap, int n) +{ + if(n < 8) + return -1; + + for(n=0; n=0; i--) + if(form1sig[i].num == *ap) + break; + if(i < 0) + abort(); + + p = (uchar*)ap + 12; + memmove(p, ap+1, --n); + + /* nonce[12] = sig[8] | counter[4] */ + memmove(ap, form1sig[i].sig, 8); + i = counter++; + ap[8] = i, ap[9] = i>>8, ap[10] = i>>16, ap[11] = i>>24; + + setupChachastate(&s, key, 32, (uchar*)ap, 12, 20); + ccpoly_encrypt(p, n, nil, 0, p+n, &s); + return 12+16 + n; +} + +int +form1M2B(char *ap, int n, uchar key[32]) +{ + Chachastate s; + uchar *p; + int num; + + num = form1check(ap, n); + if(num < 0) + return -1; + n -= 12+16; + if(n <= 0) + return -1; + + p = (uchar*)ap + 12; + setupChachastate(&s, key, 32, (uchar*)ap, 12, 20); + if(ccpoly_decrypt(p, n, nil, 0, p+n, &s)) + return -1; + + memmove(ap+1, p, n); + ap[0] = num; + return n+1; +} diff --git a/sys/src/libauthsrv/mkfile b/sys/src/libauthsrv/mkfile index d46fa52d7..9a058b0a4 100644 --- a/sys/src/libauthsrv/mkfile +++ b/sys/src/libauthsrv/mkfile @@ -2,10 +2,12 @@ LIB=/$objtype/lib/libauthsrv.a OFILES=\ + _asgetpakkey.$O\ _asgetticket.$O\ _asgetresp.$O\ _asrequest.$O\ _asrdresp.$O\ + authpak.$O\ authdial.$O\ convA2M.$O\ convM2A.$O\ @@ -15,17 +17,35 @@ OFILES=\ convPR2M.$O\ convT2M.$O\ convTR2M.$O\ + form1.$O\ nvcsum.$O\ passtokey.$O\ readnvram.$O\ HFILES=\ - /sys/include/authsrv.h\ + /sys/include/authsrv.h + +MPCFILES=\ + msqrt.mpc\ + decaf.mpc\ + edwards.mpc\ + elligator2.mpc\ + spake2ee.mpc\ + ed448.mpc\ UPDATE=\ mkfile\ $HFILES\ ${OFILES:%.$O=%.c}\ + ${MPCFILES:.mpc=%.mp}\ ${LIB:/$objtype/%=/386/%}\ + +CLEANFILES=$MPCFILES + $target diff --git a/sys/src/libauthsrv/msqrt.mp b/sys/src/libauthsrv/msqrt.mp new file mode 100644 index 000000000..c34d5e480 --- /dev/null +++ b/sys/src/libauthsrv/msqrt.mp @@ -0,0 +1,100 @@ +# derived from: http://eli.thegreenplace.net/2009/03/07/computing-square-roots-in-python + +# Compute the Legendre symbol a|p using Euler's criterion. +# p is a prime, a is relatively prime to p (if p divides a, +# then a|p = 0) +legendresymbol(a, p, r) { + pm1 = p-1; + mod(p) r = a^(pm1>>1); + if(r == pm1) + r = -1; +} + +# Find a quadratic residue (mod p) of 'a'. p must be an +# odd prime. +# +# Solve the congruence of the form: +# x^2 = a (mod p) +# And returns x. Node that p - x is also a root. +# +# 0 is returned if no square root exists for these +# a and p. +# +# The Tonelli-Shanks algorithm is used (except +# for some simple cases in which the solution is known +# from an identity). +msqrt(a, p, r) { + if(legendresymbol(a, p) != 1) + r = 0; + else if(a == 0) + r = 0; + else if(p == 2) + r = a; + else if(p%4 == 3){ + e = p+1 >> 2; + mod(p) r = a^e; + } else { + # Partition p-1 to s * 2^e for an odd s (i.e. + # reduce all the powers of 2 from p-1) + s = p-1; + e = 0; + while(s%2 == 0){ + s = s >> 1; + e = e + 1; + } + + # Find some 'n' with a legendre symbol n|p = -1. + # Shouldn't take long. + n = 2; + while(legendresymbol(n, p) != -1) + n = n + 1; + + # x is a guess of the square root that gets better + # with each iteration. + # b is the "fudge factor" - by now much we're off + # with the guess. The invariant x^2 == a*b (mod p) + # is maintained throughout the loop. + # g is used for successive powers of n to update + # both a and b + # e is the exponent - decreases with each update + mod(p){ + x = a^(s+1 >> 1); + b = a^s; + g = n^s; + } + while(1==1){ + t = b; + m = 0; + while(m < e){ + if(t == 1) + break; + t = t*t % p; + m = m + 1; + } + if(m == 0){ + r = x; + break; + } + t = 2^(e-m-1); + mod(p){ + gs = g^t; + g = gs*gs; + x = x*gs; + b = b*g; + } + e = m; + } + } +} + +# modular inverse square-root +misqrt(a, p, r) { + if((p % 4) == 3){ + e = ((p-3)>>2); + mod(p) r = a^e; + } else { + r = msqrt(a, p); + if(r != 0) + mod(p) r = 1/r; + } +} diff --git a/sys/src/libauthsrv/passtokey.c b/sys/src/libauthsrv/passtokey.c index d2867da87..60742c49c 100644 --- a/sys/src/libauthsrv/passtokey.c +++ b/sys/src/libauthsrv/passtokey.c @@ -3,8 +3,8 @@ #include #include -static void -passtodeskey(char *key, char *p) +void +passtodeskey(char key[DESKEYLEN], char *p) { uchar buf[ANAMELEN], *t; int i, n; @@ -32,17 +32,17 @@ passtodeskey(char *key, char *p) } } -static void -passtoaeskey(uchar *key, char *p) +void +passtoaeskey(uchar key[AESKEYLEN], char *p) { static char salt[] = "Plan 9 key derivation"; pbkdf2_x((uchar*)p, strlen(p), (uchar*)salt, sizeof(salt)-1, 9001, key, AESKEYLEN, hmac_sha1, SHA1dlen); } void -passtokey(Authkey *key, char *p) +passtokey(Authkey *key, char *pw) { memset(key, 0, sizeof(Authkey)); - passtodeskey(key->des, p); - passtoaeskey(key->aes, p); + passtodeskey(key->des, pw); + passtoaeskey(key->aes, pw); } diff --git a/sys/src/libauthsrv/spake2ee.mp b/sys/src/libauthsrv/spake2ee.mp new file mode 100644 index 000000000..dd8786988 --- /dev/null +++ b/sys/src/libauthsrv/spake2ee.mp @@ -0,0 +1,35 @@ +# +# this implements a variant of SPAKE2 Elligator edition described in: +# https://www.mail-archive.com/curves@moderncrypto.org/msg00412.html +# + +# derive points PM or PN from a (password) hash +spake2ee_h2P(p,a,d, h, PX,PY,PZ,PT){ + # find a small non-square for elligator + n = 2; + while(legendresymbol(n, p) != -1) + n = n + 1; + PX,PY,PZ,PT = elligator2(p,a,d, n, h%p); +} + +# Ya = xa*G+PM, Yb = xb*G+PN +spake2ee_1(p,a,d, x, GX,GY, PX,PY,PZ,PT, y){ + mod(p) X,Y,Z,T = edwards_scale(p,a,d, x, GX,GY,1,GX*GY); + X,Y,Z,T = edwards_add(p,a,d, X,Y,Z,T, PX,PY,PZ,PT); + y = decaf_encode(p,a,d, X,Y,Z,T); +} + +# Z = xa*(Yb-PN) +# = xa*(xb*G+PN-PN) +# = xa*xb*G +# = xb*xa*G +# = xb*(xa*G+PM-PM) +# = xb*(Ya-PM) +spake2ee_2(p,a,d, PX,PY,PZ,PT, x, y, ok, z){ + ok, X,Y,Z,T = decaf_decode(p,a,d, y); + if(ok != 0){ + mod(p) X,Y,Z,T = edwards_add(p,a,d, X,Y,Z,T, -PX,PY,PZ,-PT); + X,Y,Z,T = edwards_scale(p,a,d, x, X,Y,Z,T); + z = decaf_encode(p,a,d, X,Y,Z,T); + } +}