mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
auth/factotum: add support for TOTP code generation
This commit is contained in:
parent
1a91932731
commit
21731dd45e
7 changed files with 216 additions and 1 deletions
|
@ -1,6 +1,6 @@
|
|||
.TH FACTOTUM 4
|
||||
.SH NAME
|
||||
factotum, fgui, userpasswd \- authentication agent
|
||||
factotum, fgui, userpasswd, totp \- authentication agent
|
||||
.SH SYNOPSIS
|
||||
.B auth/factotum
|
||||
[
|
||||
|
@ -26,6 +26,14 @@ factotum, fgui, userpasswd \- authentication agent
|
|||
.PP
|
||||
.B auth/userpasswd
|
||||
.I fmt
|
||||
.PP
|
||||
.B auth/totp
|
||||
[
|
||||
.B -k
|
||||
.I pattern
|
||||
] | [
|
||||
.B label
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Factotum
|
||||
is a user-level file system that
|
||||
|
@ -258,6 +266,15 @@ key tuple specified in
|
|||
.IR fmt .
|
||||
This can be used by shell scripts to do cleartext password
|
||||
authentication.
|
||||
.PP
|
||||
.I Totp
|
||||
queries and prints an
|
||||
.B RFC 6238
|
||||
TOTP code
|
||||
for the
|
||||
.B proto=totp
|
||||
key tuple specified.
|
||||
This can be used to authenticate with services that require time based OTP.
|
||||
.SS "Key Tuples
|
||||
.PP
|
||||
A
|
||||
|
|
|
@ -228,3 +228,4 @@ extern Proto rsa; /* rsa.c */
|
|||
extern Proto httpdigest; /* httpdigest.c */
|
||||
extern Proto ecdsa; /* ecdsa.c */
|
||||
extern Proto wpapsk; /* wpapsk.c */
|
||||
extern Proto totp; /* totp */
|
||||
|
|
|
@ -43,6 +43,7 @@ prototab[] =
|
|||
&vnc,
|
||||
&ecdsa,
|
||||
&wpapsk,
|
||||
&totp,
|
||||
nil,
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ PROTO=\
|
|||
rsa.$O\
|
||||
ecdsa.$O\
|
||||
wpapsk.$O\
|
||||
totp.$O\
|
||||
|
||||
FOFILES=\
|
||||
$PROTO\
|
||||
|
|
146
sys/src/cmd/auth/factotum/totp.c
Normal file
146
sys/src/cmd/auth/factotum/totp.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
#include "dat.h"
|
||||
|
||||
typedef struct State State;
|
||||
struct State {
|
||||
Key *key;
|
||||
};
|
||||
|
||||
enum {
|
||||
HaveTotp,
|
||||
Maxphase,
|
||||
};
|
||||
|
||||
enum {
|
||||
Maxdigits = 8,
|
||||
Sec = 1000*1000*1000,
|
||||
};
|
||||
|
||||
static char *phasenames[Maxphase] ={
|
||||
[HaveTotp] "HaveTotp",
|
||||
};
|
||||
|
||||
static int
|
||||
genhotp(uchar *key, int n, uvlong c, int len)
|
||||
{
|
||||
uchar hash[SHA1dlen];
|
||||
uchar data[8];
|
||||
u32int h, m;
|
||||
int o;
|
||||
|
||||
data[0] = (c>>56) & 0xff;
|
||||
data[1] = (c>>48) & 0xff;
|
||||
data[2] = (c>>40) & 0xff;
|
||||
data[3] = (c>>32) & 0xff;
|
||||
data[4] = (c>>24) & 0xff;
|
||||
data[5] = (c>>16) & 0xff;
|
||||
data[6] = (c>> 8) & 0xff;
|
||||
data[7] = (c>> 0) & 0xff;
|
||||
hmac_sha1(data, sizeof(data), key, n, hash, nil);
|
||||
|
||||
o = hash[SHA1dlen - 1] & 0x0F;
|
||||
h = ((hash[o] & 0x7F) << 24)
|
||||
| (hash[o + 1] & 0xFF) << 16
|
||||
| (hash[o + 2] & 0xFF) << 8
|
||||
| hash[o + 3] & 0xFF;
|
||||
m = 1;
|
||||
while(len-- > 0)
|
||||
m *= 10;
|
||||
return h % m;
|
||||
}
|
||||
|
||||
static int
|
||||
gentotp(char *secret, vlong t, int len, vlong period)
|
||||
{
|
||||
uchar key[512];
|
||||
int n;
|
||||
|
||||
n = dec32(key, sizeof(key), secret, strlen(secret));
|
||||
if(n < 0){
|
||||
werrstr("invalid totp secret");
|
||||
return -1;
|
||||
}
|
||||
return genhotp(key, n, t/period, len);
|
||||
}
|
||||
|
||||
static int
|
||||
totpinit(Proto *p, Fsstate *fss)
|
||||
{
|
||||
int ret;
|
||||
Key *k;
|
||||
Keyinfo ki;
|
||||
State *s;
|
||||
|
||||
ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", p->keyprompt);
|
||||
if(ret != RpcOk)
|
||||
return ret;
|
||||
setattrs(fss->attr, k->attr);
|
||||
s = emalloc(sizeof(*s));
|
||||
s->key = k;
|
||||
fss->ps = s;
|
||||
fss->phase = HaveTotp;
|
||||
return RpcOk;
|
||||
}
|
||||
|
||||
static void
|
||||
totpclose(Fsstate *fss)
|
||||
{
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
if(s->key)
|
||||
closekey(s->key);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
totpread(Fsstate *fss, void *va, uint *n)
|
||||
{
|
||||
char *secret, *digits, *period;
|
||||
int len, otp;
|
||||
vlong tdiv;
|
||||
State *s;
|
||||
|
||||
s = fss->ps;
|
||||
len = 6;
|
||||
tdiv = 30ULL*Sec;
|
||||
switch(fss->phase){
|
||||
default:
|
||||
return phaseerror(fss, "read");
|
||||
|
||||
case HaveTotp:
|
||||
digits = _strfindattr(s->key->attr, "digits");
|
||||
secret = _strfindattr(s->key->privattr, "!secret");
|
||||
period = _strfindattr(s->key->attr, "period");
|
||||
if(secret==nil)
|
||||
return failure(fss, "missing totp secret");
|
||||
if(digits != nil)
|
||||
len = atoi(digits);
|
||||
if(period != nil)
|
||||
tdiv = strtoll(period, nil, 0)*Sec;
|
||||
if(*n < len)
|
||||
return toosmall(fss, len);
|
||||
if(len < 1 || len > Maxdigits || tdiv <= 0)
|
||||
return failure(fss, "too many digits");
|
||||
otp = gentotp(secret, nsec(), len, tdiv);
|
||||
if(otp < 0)
|
||||
return failure(fss, "%r");
|
||||
*n = snprint(va, *n, "%.*d", len, otp);
|
||||
return RpcOk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
totpwrite(Fsstate *fss, void*, uint)
|
||||
{
|
||||
return phaseerror(fss, "write");
|
||||
}
|
||||
|
||||
Proto totp = {
|
||||
.name= "totp",
|
||||
.init= totpinit,
|
||||
.write= totpwrite,
|
||||
.read= totpread,
|
||||
.close= totpclose,
|
||||
.addkey= replacekey,
|
||||
.keyprompt= "label? !secret?",
|
||||
};
|
|
@ -35,6 +35,7 @@ TARG=\
|
|||
rsafill\
|
||||
rsagen\
|
||||
ssh2rsa\
|
||||
totp\
|
||||
uniq\
|
||||
userpasswd\
|
||||
warning\
|
||||
|
|
48
sys/src/cmd/auth/totp.c
Normal file
48
sys/src/cmd/auth/totp.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <auth.h>
|
||||
|
||||
char *keypat;
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s fmt\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char params[512];
|
||||
AuthRpc *rpc;
|
||||
int fd;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'k':
|
||||
keypat = EARGF(usage());
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
quotefmtinstall();
|
||||
if(keypat == nil)
|
||||
snprint(params, sizeof(params), "proto=totp label=%q", argv[0]);
|
||||
else
|
||||
snprint(params, sizeof(params), "proto=totp %s", keypat);
|
||||
|
||||
if((fd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) == -1)
|
||||
sysfatal("open /mnt/factotum/rpc: %r");
|
||||
if((rpc = auth_allocrpc(fd)) == nil)
|
||||
sysfatal("allocrpc: %r");
|
||||
if(auth_rpc(rpc, "start", params, strlen(params)) != ARok
|
||||
|| auth_rpc(rpc, "read", nil, 0) != ARok)
|
||||
sysfatal("totp proto: %r");
|
||||
rpc->arg[rpc->narg] = '\0';
|
||||
print("%s\n", rpc->arg);
|
||||
|
||||
close(fd);
|
||||
auth_freerpc(rpc);
|
||||
exits(nil);
|
||||
}
|
Loading…
Reference in a new issue