auth: release dp9ik implementation and reentrant factotum

This commit is contained in:
cinap_lenrek 2016-01-06 03:09:00 +01:00
parent e064752dd4
commit 2dae1ed53a
44 changed files with 2034 additions and 732 deletions

View file

@ -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]);

View file

@ -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 <authsrv.h> ,
@ -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

View file

@ -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);

View file

@ -8,11 +8,21 @@
#include <authsrv.h>
#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; i<MSresplen; i++)
p += sprint(p, "%.2ux ", resp[i]);
syslog(0, AUTHLOG, "resp = %s", buf);
syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, 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

View file

@ -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");

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 */

View file

@ -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);

View file

@ -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;

View file

@ -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; i<nelem(negotiable); i++)
if(strncmp(negotiable[i]->name, 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); i<m; i++){
for(i=0; i<m; i++){
p = findneg(token[i]);
if(p == nil)
continue;

View file

@ -24,6 +24,7 @@ struct State
Key *key;
int astype;
int asfd;
Authkey k;
Ticket t;
Ticketreq tr;
char chal[Maxchal];
@ -165,18 +166,14 @@ p9crread(Fsstate *fss, void *va, uint *n)
static int
p9response(Fsstate *fss, State *s)
{
Authkey key;
Authkey *akey;
uchar buf[8];
ulong chal;
char *pw;
pw = _strfindattr(s->key->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){

View file

@ -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?"
};

View file

@ -429,15 +429,20 @@ ctlwrite(char *a, int atzero)
return -1;
}
}
for(i=0; i<ring->nkey; ){
if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){
Again:
qlock(ring);
for(i=0; i<ring->nkey; 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");

View file

@ -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; i<ring->nkey; 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; i<ring->nkey; 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;
}

View file

@ -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);

View file

@ -6,6 +6,7 @@ OFILES=\
keyfmt.$O\
netcheck.$O\
okpasswd.$O\
private.$O\
readwrite.$O\
readarg.$O\
readln.$O\

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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");

View file

@ -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);

View file

@ -0,0 +1,26 @@
#include <u.h>
#include <libc.h>
#include <authsrv.h>
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);
}

View file

@ -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;
}

View file

@ -2,16 +2,36 @@
#include <libc.h>
#include <authsrv.h>
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;
}

View file

@ -0,0 +1,214 @@
#include <u.h>
#include <libc.h>
#include <mp.h>
#include <libsec.h>
#include <authsrv.h>
#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;
}

View file

@ -2,26 +2,35 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -2,30 +2,35 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -2,35 +2,38 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -2,34 +2,50 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -2,12 +2,6 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -2,29 +2,33 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -1,29 +1,39 @@
#include <u.h>
#include <libc.h>
#include <authsrv.h>
#include <libsec.h>
#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;
}

View file

@ -2,12 +2,6 @@
#include <libc.h>
#include <authsrv.h>
#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;
}

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -0,0 +1,90 @@
#include <u.h>
#include <libc.h>
#include <authsrv.h>
#include <libsec.h>
/*
* 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<nelem(form1sig); n++)
if(memcmp(form1sig[n].sig, ap, 8) == 0)
return form1sig[n].num;
return -1;
}
int
form1B2M(char *ap, int n, uchar key[32])
{
static u32int counter;
Chachastate s;
uchar *p;
int i;
for(i=nelem(form1sig)-1; i>=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;
}

View file

@ -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
</sys/src/cmd/mksyslib
authpak.$O: $MPCFILES
%.mpc: %.mp
mpc $stem.mp > $target

100
sys/src/libauthsrv/msqrt.mp Normal file
View file

@ -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;
}
}

View file

@ -3,8 +3,8 @@
#include <authsrv.h>
#include <libsec.h>
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);
}

View file

@ -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);
}
}