Compare commits

..

5 commits

Author SHA1 Message Date
Ori Bernstein
21731dd45e auth/factotum: add support for TOTP code generation 2024-12-25 02:18:43 +00:00
cinap_lenrek
1a91932731 ip/dhcpd: impvoe validip(), make validipmask() reject ipv6 masks
Because isv4() is never true for IPnoaddr,
we can skip the check.

Make validipmask() ensure that the top IPv4off
bytes are all 0xff, then check that the low
4 bytes are a valid mask (must be a power
of two minus one).
2024-12-23 16:12:35 +00:00
Jacob Moody
248634879a git: bikeshed git/walk relative path calculation and add tests 2024-12-22 22:41:02 +00:00
Jacob Moody
7814ec46c3 git: make git/diff -s print relative file paths
This makes the output of git/diff -s plumbable
when the user is not within the root of the git repo.
This is implemented through adding a flag to git/walk
and having git/walk work out how many '..'s are needed.

Also includes a small piece of documentation regarding
the use of $editor in git/commit.
2024-12-22 21:06:53 +00:00
cinap_lenrek
35cab9b816 ip/dhcpd: don't get confused by ipv6 addresses for bootp Info
When looking up tftp/bootp information from ndb,
filter out ipv6 addresses. This can happen
when for example the tftp= attribute points to
a domain or sys-name that resolves to both
ipv4 and ipv6 addresses.

Make validip() do the isv4() check, and provide
separate validipmask() function.
2024-12-22 20:06:18 +00:00
17 changed files with 365 additions and 29 deletions

View file

@ -183,6 +183,10 @@ git/pull, git/rm, git/serve \- Manage git repositories.
.I filters
]
[
.B -r
.I rel
]
[
.I file...
]
@ -331,7 +335,14 @@ is an alias for
.PP
.B Git/commit
creates a new commit consisting of all changes to the specified files.
By default, an editor is opened to prepare the commit message.
By default,
.I $editor
is opened to prepare the commit message.
If
.I $editor
is undefined
.IR hold (1)
is used.
The
.B -m
flag supplies the commit message directly.
@ -532,6 +543,10 @@ The
.B -q
option suppresses all output.
The
.B -r
option causes paths to be printed relative to the supplied directory
.IR rel .
The
.B -f
option filters files by status, and only matching items are printed.
By default, the filters are

View file

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

View file

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

View file

@ -43,6 +43,7 @@ prototab[] =
&vnc,
&ecdsa,
&wpapsk,
&totp,
nil,
};

View file

@ -14,6 +14,7 @@ PROTO=\
rsa.$O\
ecdsa.$O\
wpapsk.$O\
totp.$O\
FOFILES=\
$PROTO\

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

View file

@ -35,6 +35,7 @@ TARG=\
rsafill\
rsagen\
ssh2rsa\
totp\
uniq\
userpasswd\
warning\

48
sys/src/cmd/auth/totp.c Normal file
View 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);
}

View file

@ -21,7 +21,7 @@ if(! ~ $#* 0)
branch=`{git/query -p $commit}
if(~ $summarize 1 || ~ $uncommitted 1){
git/walk -f$filt $cparam $files
git/walk -r$gitrel -f$filt $cparam $files
exit
}

46
sys/src/cmd/git/test/diff.rc Executable file
View file

@ -0,0 +1,46 @@
#!/bin/rc
. util.rc
nl='
'
rm -fr scratch
mkdir -p scratch/subdir/subdir2
mkdir -p scratch/subdir3
echo @@git diff -s relative@@
@{
cd scratch
q git/init
echo hello > file.txt
echo hello1 > subdir/file1.txt
echo hello2 > subdir/subdir2/file2.txt
echo hello3 > subdir3/file3.txt
q git/add file.txt subdir/file1.txt subdir/subdir2/file2.txt subdir3/file3.txt
q git/commit -m initial .
echo >file.txt
echo >subdir/file1.txt
echo >subdir/subdir2/file2.txt
echo >subdir3/file3.txt
out=`$nl{git/diff -s . | awk '{ print $2 }'}
~ $out(1) file.txt && ~ $out(2) subdir/file1.txt && ~ $out(3) subdir/subdir2/file2.txt \
~ $out(4) subdir3/file3.txt || die 'base level fail'
cd subdir
out=`$nl{git/diff -s .. | awk '{ print $2 }'}
~ $out(1) ../file.txt && ~ $out(2) file1.txt && ~ $out(3) subdir2/file2.txt \
~ $out(4) ../subdir3/file3.txt || die 'subdir1 level fail'
cd subdir2
out=`$nl{git/diff -s ../.. | awk '{ print $2 }'}
~ $out(1) ../../file.txt && ~ $out(2) ../file1.txt && ~ $out(3) file2.txt \
~ $out(4) ../../subdir3/file3.txt || die 'subdir2 level fail'
cd ../../subdir3
out=`$nl{git/diff -s .. | awk '{ print $2 }'}
~ $out(1) ../file.txt && ~ $out(2) ../subdir/file1.txt && ~ $out(3) ../subdir/subdir2/file2.txt \
~ $out(4) file3.txt || die 'subdir3 level fail'
! git/diff -s ../.. >[2]/dev/null
}

View file

@ -3,6 +3,7 @@
TEST=\
add\
basic\
diff\
export\
ftype\
lca\

View file

@ -33,6 +33,8 @@ Seen seentab[NCACHE];
Idxed idxtab[NCACHE];
char repopath[1024];
char wdirpath[1024];
char relapath[1024];
int nslash;
char *rstr = "R ";
char *mstr = "M ";
char *astr = "A ";
@ -347,15 +349,56 @@ reporel(char *s)
void
show(Biobuf *o, int flg, char *str, char *path)
{
char *pa, *pb;
int n;
dirty |= flg;
if(!quiet && (printflg & flg))
Bprint(o, "%s%s\n", str, path);
if(!quiet && (printflg & flg)){
Bprint(o, str);
n = nslash;
if(n){
for(pa = relapath, pb = path; *pa && *pb; pa++, pb++){
if(*pa != *pb)
break;
if(*pa == '/'){
n--;
path = pb+1;
}
}
while(n-- > 0)
Bprint(o, "../");
}
Bprint(o, "%s\n", path);
}
}
void
findslashes(char *path)
{
char *p;
p = cleanname(path);
if(p[0] == '.'){
if(p[1] == '\0')
return;
else if(p[1] == '.' && (p[2] == '/' || p[2] == '\0'))
sysfatal("relative path escapes git root");
}
snprint(relapath, sizeof relapath, "%s/", p);
p = relapath;
if(*p == '/')
p++;
for(; *p; p++)
if(*p == '/')
nslash++;
}
void
usage(void)
{
fprint(2, "usage: %s [-qbc] [-f filt] [-b base] [paths...]\n", argv0);
fprint(2, "usage: %s [-qbc] [-f filt] [-b base] [-r rel] [paths...]\n", argv0);
exits("usage");
}
@ -410,6 +453,9 @@ main(int argc, char **argv)
useidx = 1;
bdir = smprint(".git/fs/object/%H/tree", h);
break;
case 'r':
findslashes(EARGF(usage()));
break;
default:
usage();
}ARGEND;

View file

@ -49,7 +49,6 @@ struct Info
/* from dhcp.c */
extern int validip(uchar*);
extern void fatal(char*, ...);
extern void warning(char*, ...);
#pragma varargck argpos fatal 1
@ -57,6 +56,8 @@ extern void warning(char*, ...);
extern int minlease;
/* from db.c */
extern int validip(uchar*);
extern int validipmask(uchar*);
extern char* toid(uchar*, int);
extern void initbinding(uchar*, int);
extern Binding* iptobinding(uchar*, int);

View file

@ -412,3 +412,22 @@ releasebinding(Binding *b, char *id)
close(fd);
return 0;
}
int
validip(uchar *ip)
{
if(ipcmp(ip, v4prefix) == 0)
return 0;
return isv4(ip);
}
int
validipmask(uchar *mask)
{
unsigned x;
if(memcmp(mask, IPallbits, IPv4off) != 0)
return 0;
x = ~(mask[IPv4off+0] << 24 | mask[IPv4off+1] << 16 | mask[IPv4off+2] << 8 | mask[IPv4off+3]);
return ((x + 1U) & x) == 0;
}

View file

@ -191,7 +191,6 @@ void sendnak(Req*, uchar*, char*);
void sendoffer(Req*, uchar*, int);
void stringopt(Req*, int, char*);
void termopt(Req*);
int validip(uchar*);
void vectoropt(Req*, int, uchar*, int);
void
@ -1134,9 +1133,9 @@ miscoptions(Req *rp, uchar *ip)
addrs[i] = &x[i*IPaddrlen];
/* always supply these */
if(validip(rp->ii.ipmask))
if(validipmask(rp->ii.ipmask))
maskopt(rp, OBmask, rp->ii.ipmask);
else if(validip(rp->gii.ipmask))
else if(validipmask(rp->gii.ipmask))
maskopt(rp, OBmask, rp->gii.ipmask);
else if((lifc = ipremoteonifc(rp->ifc, ip)) != nil)
maskopt(rp, OBmask, lifc->mask);
@ -1389,16 +1388,6 @@ readsysname(void)
return p;
}
extern int
validip(uchar *ip)
{
if(ipcmp(ip, IPnoaddr) == 0)
return 0;
if(ipcmp(ip, v4prefix) == 0)
return 0;
return 1;
}
void
longopt(Req *rp, int t, long v)
{

View file

@ -35,7 +35,9 @@ main(void)
b.lease = b.offer = 0;
now = time(0);
for(i = 0; i < nall; i++){
if(parseip(b.ip, all[i].name) == -1 || syncbinding(&b, 0) < 0)
if(parseip(b.ip, all[i].name) == -1
|| !validip(b.ip)
|| syncbinding(&b, 0) < 0)
continue;
if(b.lease > now)
print("%I leased by %s until %s", b.ip, b.boundto,

View file

@ -57,17 +57,21 @@ localip(uchar *laddr, uchar *raddr, Ipifc *ifc)
}
static void
setipaddr(uchar *addr, char *ip)
setipaddr(uchar *ip, char *s)
{
if(ipcmp(addr, IPnoaddr) == 0)
parseip(addr, ip);
if(ipcmp(ip, IPnoaddr) == 0)
if(parseip(ip, s) == -1
|| !validip(ip))
ipmove(ip, IPnoaddr); /* invalid */
}
static void
setipmask(uchar *mask, char *ip)
setipmask(uchar *mask, char *s)
{
if(ipcmp(mask, IPnoaddr) == 0)
parseipmask(mask, ip, 1);
if(parseipmask(mask, s, 1) == -1
|| !validipmask(mask))
ipmove(mask, IPnoaddr); /* invalid */
}
/*
@ -228,9 +232,7 @@ lookup(Bootp *bp, Info *iip, Info *riip)
for(nt = t; nt != nil; nt = nt->entry){
if(strcmp(nt->attr, "ip") != 0)
continue;
if(parseip(ciaddr, nt->val) == -1 || !isv4(ciaddr))
continue;
if(!validip(ciaddr))
if(parseip(ciaddr, nt->val) == -1 || !validip(ciaddr))
continue;
if(!samenet(ciaddr, riip))
continue;