add secstored; use readcons

This commit is contained in:
rsc 2005-02-11 19:39:51 +00:00
parent d93fca6a7a
commit 096ff3e14a
11 changed files with 1196 additions and 22 deletions

View file

@ -75,7 +75,7 @@ main(int argc, char **argv)
while(buf[n-1] == '\n')
buf[--n] = 0;
}else{
pass = getpassm("aescbc key:");
pass = readcons("aescbc key", nil, 1);
n = strlen(pass);
if(n >= BUF)
exits("key too long");

View file

@ -64,7 +64,7 @@ dirls(char *path)
if(path==nil || (ndir = ls(path, &dirbuf)) < 0)
return nil;
qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void *, void *))compare);
qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void *, const void *))compare);
for(nmwid=lenwid=i=0; i<ndir; i++){
if((m = strlen(dirbuf[i].name)) > nmwid)
nmwid = m;

View file

@ -13,10 +13,15 @@ OFILES =\
util.$O\
TARG=aescbc secstore
TARG=aescbc secstore secstored secuser
<$PLAN9/src/mkmany
$O.aescbc: aescbc.$O util.$O $LIB ${SHORTLIB:%=$LIBDIR/lib%.a}
$O.aescbc: aescbc.$O util.$O
$LD -o $target $prereq $LDFLAGS
$O.secstored: secstored.$O dirls.$O secureidcheck.$O $OFILES
$LD -o $target $prereq
$O.secuser: secuser.$O $OFILES
$LD -o $target $prereq

View file

@ -0,0 +1,35 @@
#include <u.h>
#include <libc.h>
#include <ip.h>
int verbose = 1;
static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n";
void
main(int argc, char **argv)
{
int n, m, fd;
uchar buf[500];
if(argc != 2)
exits("usage: secacct userid");
n = snprint((char*)buf, sizeof buf, testmess, argv[1]);
hnputs(buf, 0x8000+n-2);
fd = dial("tcp!ruble.cs.bell-labs.com!5356", 0, 0, 0);
if(fd < 0)
exits("cannot dial ruble");
if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2)
exits("cannot exchange first round");
n = ((buf[0]&0x7f)<<8) + buf[1];
if(n+1 > sizeof buf)
exits("implausibly large count");
m = readn(fd, buf, n);
close(fd);
if(m != n)
fprint(2,"short read from secstore\n");
buf[m] = 0;
print("%s\n", (char*)buf);
exits(0);
}

28
src/cmd/secstore/secchk.c Normal file
View file

@ -0,0 +1,28 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
extern char* secureidcheck(char *user, char *response);
Ndb *db;
void
main(int argc, char **argv)
{
Ndb *db2;
if(argc!=2){
fprint(2,"usage %s pinsecurid\n", argv[0]);
exits("usage");
}
db = ndbopen("/lib/ndb/auth");
if(db == 0)
syslog(0, "secstore", "no /lib/ndb/auth");
db2 = ndbopen(0);
if(db2 == 0)
syslog(0, "secstore", "no /lib/ndb/local");
db = ndbcat(db, db2);
print("user=%s\n", getenv("user"));
print("%s\n", secureidcheck(getenv("user"), argv[1]));
exits(0);
}

View file

@ -16,6 +16,7 @@ typedef struct AuthConn{
int verbose;
Nvrsafe nvr;
char *SECSTORE_DIR;
void
usage(void)
@ -311,7 +312,7 @@ chpasswd(AuthConn *c, char *id)
// changing our password is vulnerable to connection failure
for(;;){
snprint(prompt, sizeof(prompt), "new password for %s: ", id);
newpass = getpassm(prompt);
newpass = readcons(prompt, nil, 1);
if(newpass == nil)
goto Out;
if(strlen(newpass) >= 7)
@ -324,9 +325,9 @@ chpasswd(AuthConn *c, char *id)
}
newpasslen = strlen(newpass);
snprint(prompt, sizeof(prompt), "retype password: ");
passck = getpassm(prompt);
passck = readcons(prompt, nil, 1);
if(passck == nil){
fprint(2, "getpassmwd failed\n");
fprint(2, "readcons failed\n");
goto Out;
}
if(strcmp(passck, newpass) != 0){
@ -419,7 +420,9 @@ login(char *id, char *dest, int pass_stdin, int pass_nvram)
}
ntry++;
if(!pass_stdin && !pass_nvram){
pass = getpassm("secstore password: ");
pass = readcons("secstore password", nil, 1);
if(pass == nil)
pass = estrdup("");
if(strlen(pass) >= sizeof c->pass){
fprint(2, "password too long, skipping secstore login\n");
exits("password too long");
@ -444,7 +447,7 @@ login(char *id, char *dest, int pass_stdin, int pass_nvram)
fprint(2, "Enter an empty password to quit.\n");
}
c->passlen = strlen(c->pass);
fprint(2, "%s\n", S);
fprint(2, "server: %s\n", S);
free(S);
if(readstr(c->conn, s) < 0){
c->conn->free(c->conn);
@ -460,7 +463,9 @@ login(char *id, char *dest, int pass_stdin, int pass_nvram)
exits("missing PIN+SecureID on standard input");
free(PINSTA);
}else{
pass = getpassm("STA PIN+SecureID: ");
pass = readcons("STA PIN+SecureID", nil, 1);
if(pass == nil)
pass = estrdup("");
strncpy(s+3, pass, (sizeof s)-4);
memset(pass, 0, strlen(pass));
free(pass);

View file

@ -17,7 +17,6 @@ typedef struct PW {
PW *getPW(char *, int);
int putPW(PW *);
void freePW(PW *);
char* getpassm(const char*);
// *client: SConn, client name, passphrase
// *server: SConn, (partial) 1st msg, PW entry
@ -27,4 +26,6 @@ int PAKserver(SConn *, char *, char *, PW **);
char *PAK_Hi(char *, char *, mpint *, mpint *);
#define LOG "secstore"
#define SECSTORE_DIR "/adm/secstore"
extern char *SECSTORE_DIR;

View file

@ -0,0 +1,420 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <mp.h>
#include <libsec.h>
#include "SConn.h"
#include "secstore.h"
char *SECSTORE_DIR;
char* secureidcheck(char *, char *); // from /sys/src/cmd/auth/
extern char* dirls(char *path);
int verbose;
Ndb *db;
static void
usage(void)
{
fprint(2, "usage: secstored [-R] [-S servername] [-s tcp!*!5356] [-v] [-x netmtpt]\n");
exits("usage");
}
static int
getdir(SConn *conn, char *id)
{
char *ls, *s;
uchar *msg;
int n, len;
s = emalloc(Maxmsg);
snprint(s, Maxmsg, "%s/store/%s", SECSTORE_DIR, id);
if((ls = dirls(s)) == nil)
len = 0;
else
len = strlen(ls);
/* send file size */
snprint(s, Maxmsg, "%d", len);
conn->write(conn, (uchar*)s, strlen(s));
/* send directory listing in Maxmsg chunks */
n = Maxmsg;
msg = (uchar*)ls;
while(len > 0){
if(len < Maxmsg)
n = len;
conn->write(conn, msg, n);
msg += n;
len -= n;
}
free(s);
free(ls);
return 0;
}
char *
validatefile(char *f)
{
char *nl;
if(f==nil || *f==0)
return nil;
if(nl = strchr(f, '\n'))
*nl = 0;
if(strchr(f,'/') != nil || strcmp(f,"..")==0 || strlen(f) >= 300){
syslog(0, LOG, "no slashes allowed: %s\n", f);
return nil;
}
return f;
}
static int
getfile(SConn *conn, char *id, char *gf)
{
int n, gd, len;
ulong mode;
char *s;
Dir *st;
if(strcmp(gf,".")==0)
return getdir(conn, id);
/* send file size */
s = emalloc(Maxmsg);
snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, gf);
gd = open(s, OREAD);
if(gd < 0){
syslog(0, LOG, "can't open %s: %r\n", s);
free(s);
conn->write(conn, (uchar*)"-1", 2);
return -1;
}
st = dirfstat(gd);
if(st == nil){
syslog(0, LOG, "can't stat %s: %r\n", s);
free(s);
conn->write(conn, (uchar*)"-1", 2);
return -1;
}
mode = st->mode;
len = st->length;
free(st);
if(mode & DMDIR) {
syslog(0, LOG, "%s should be a plain file, not a directory\n", s);
free(s);
conn->write(conn, (uchar*)"-1", 2);
return -1;
}
if(len < 0 || len > MAXFILESIZE){
syslog(0, LOG, "implausible filesize %d for %s\n", len, gf);
free(s);
conn->write(conn, (uchar*)"-3", 2);
return -1;
}
snprint(s, Maxmsg, "%d", len);
conn->write(conn, (uchar*)s, strlen(s));
/* send file in Maxmsg chunks */
while(len > 0){
n = read(gd, s, Maxmsg);
if(n <= 0){
syslog(0, LOG, "read error on %s: %r\n", gf);
free(s);
return -1;
}
conn->write(conn, (uchar*)s, n);
len -= n;
}
close(gd);
free(s);
return 0;
}
static int
putfile(SConn *conn, char *id, char *pf)
{
int n, nw, pd;
long len;
char s[Maxmsg+1];
/* get file size */
n = readstr(conn, s);
if(n < 0){
syslog(0, LOG, "remote: %s: %r\n", s);
return -1;
}
len = atoi(s);
if(len == -1){
syslog(0, LOG, "remote file %s does not exist\n", pf);
return -1;
}else if(len < 0 || len > MAXFILESIZE){
syslog(0, LOG, "implausible filesize %ld for %s\n", len, pf);
return -1;
}
/* get file in Maxmsg chunks */
if(strchr(pf,'/') != nil || strcmp(pf,"..")==0){
syslog(0, LOG, "no slashes allowed: %s\n", pf);
return -1;
}
snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, pf);
pd = create(s, OWRITE, 0660);
if(pd < 0){
syslog(0, LOG, "can't open %s: %r\n", s);
return -1;
}
while(len > 0){
n = conn->read(conn, (uchar*)s, Maxmsg);
if(n <= 0){
syslog(0, LOG, "empty file chunk\n");
return -1;
}
nw = write(pd, s, n);
if(nw != n){
syslog(0, LOG, "write error on %s: %r", pf);
return -1;
}
len -= n;
}
close(pd);
return 0;
}
static int
removefile(SConn *conn, char *id, char *f)
{
Dir *d;
char buf[Maxmsg];
snprint(buf, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, f);
if((d = dirstat(buf)) == nil){
snprint(buf, sizeof buf, "remove failed: %r");
writerr(conn, buf);
return -1;
}else if(d->mode & DMDIR){
snprint(buf, sizeof buf, "can't remove a directory");
writerr(conn, buf);
free(d);
return -1;
}
free(d);
if(remove(buf) < 0){
snprint(buf, sizeof buf, "remove failed: %r");
writerr(conn, buf);
return -1;
}
return 0;
}
/* given line directory from accept, returns ipaddr!port */
static char*
remoteIP(char *ldir)
{
int fd, n;
char rp[100], ap[500];
snprint(rp, sizeof rp, "%s/remote", ldir);
fd = open(rp, OREAD);
if(fd < 0)
return strdup("?!?");
n = read(fd, ap, sizeof ap);
if(n <= 0 || n == sizeof ap){
fprint(2, "error %d reading %s: %r\n", n, rp);
return strdup("?!?");
}
close(fd);
ap[n--] = 0;
if(ap[n] == '\n')
ap[n] = 0;
return strdup(ap);
}
static int
dologin(int fd, char *S, int forceSTA)
{
int i, n, rv;
char *file, *mess;
char msg[Maxmsg+1];
PW *pw;
SConn *conn;
pw = nil;
rv = -1;
// collect the first message
if((conn = newSConn(fd)) == nil)
return -1;
if(readstr(conn, msg) < 0){
fprint(2, "remote: %s: %r\n", msg);
writerr(conn, "can't read your first message");
goto Out;
}
// authenticate
if(PAKserver(conn, S, msg, &pw) < 0){
if(pw != nil)
syslog(0, LOG, "secstore denied for %s", pw->id);
goto Out;
}
if((forceSTA || pw->status&STA) != 0){
conn->write(conn, (uchar*)"STA", 3);
if(readstr(conn, msg) < 10 || strncmp(msg, "STA", 3) != 0){
syslog(0, LOG, "no STA from %s", pw->id);
goto Out;
}
mess = secureidcheck(pw->id, msg+3);
if(mess != nil){
syslog(0, LOG, "secureidcheck denied %s because %s", pw->id, mess);
goto Out;
}
}
conn->write(conn, (uchar*)"OK", 2);
syslog(0, LOG, "AUTH %s", pw->id);
// perform operations as asked
while((n = readstr(conn, msg)) > 0){
syslog(0, LOG, "[%s] %s", pw->id, msg);
if(strncmp(msg, "GET ", 4) == 0){
file = validatefile(msg+4);
if(file==nil || getfile(conn, pw->id, file) < 0)
goto Err;
}else if(strncmp(msg, "PUT ", 4) == 0){
file = validatefile(msg+4);
if(file==nil || putfile(conn, pw->id, file) < 0){
syslog(0, LOG, "failed PUT %s/%s", pw->id, file);
goto Err;
}
}else if(strncmp(msg, "RM ", 3) == 0){
file = validatefile(msg+3);
if(file==nil || removefile(conn, pw->id, file) < 0){
syslog(0, LOG, "failed RM %s/%s", pw->id, file);
goto Err;
}
}else if(strncmp(msg, "CHPASS", 6) == 0){
if(readstr(conn, msg) < 0){
syslog(0, LOG, "protocol botch CHPASS for %s", pw->id);
writerr(conn, "protocol botch while setting PAK");
goto Out;
}
pw->Hi = strtomp(msg, nil, 64, pw->Hi);
for(i=0; i < 4 && putPW(pw) < 0; i++)
syslog(0, LOG, "password change failed for %s (%d): %r", pw->id, i);
if(i==4)
goto Out;
}else if(strncmp(msg, "BYE", 3) == 0){
rv = 0;
break;
}else{
writerr(conn, "unrecognized operation");
break;
}
}
if(n <= 0)
syslog(0, LOG, "%s closed connection without saying goodbye\n", pw->id);
Out:
freePW(pw);
conn->free(conn);
return rv;
Err:
writerr(conn, "operation failed");
goto Out;
}
void
main(int argc, char **argv)
{
int afd, dfd, lcfd, forceSTA = 0;
char adir[40], ldir[40], *remote;
char *serve = "tcp!*!5356", *p, aserve[128];
char *S = "secstore";
char *dbpath;
Ndb *db2;
S = sysname();
SECSTORE_DIR = unsharp("#9/secstore");
// setnetmtpt(net, sizeof(net), nil);
ARGBEGIN{
case 'R':
forceSTA = 1;
break;
case 's':
serve = EARGF(usage());
break;
case 'S':
S = EARGF(usage());
break;
case 'x':
p = ARGF();
if(p == nil)
usage();
USED(p);
// setnetmtpt(net, sizeof(net), p);
break;
case 'v':
verbose++;
break;
default:
usage();
}ARGEND;
if(!verbose)
switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
case -1:
sysfatal("fork: %r");
case 0:
break;
default:
exits(0);
}
snprint(aserve, sizeof aserve, "%s", serve);
afd = announce(aserve, adir);
if(afd < 0)
sysfatal("%s: %r\n", aserve);
syslog(0, LOG, "ANNOUNCE %s", aserve);
for(;;){
if((lcfd = listen(adir, ldir)) < 0)
exits("can't listen");
switch(fork()){
case -1:
fprint(2, "secstore forking: %r\n");
close(lcfd);
break;
case 0:
// "/lib/ndb/common.radius does not exist" if db set before fork
db = ndbopen(dbpath=unsharp("#9/ndb/auth"));
if(db == 0)
syslog(0, LOG, "no ndb/auth");
db2 = ndbopen(0);
if(db2 == 0)
syslog(0, LOG, "no ndb/local");
db = ndbcat(db, db2);
if((dfd = accept(lcfd, ldir)) < 0)
exits("can't accept");
alarm(30*60*1000); // 30 min
remote = remoteIP(ldir);
syslog(0, LOG, "secstore from %s", remote);
free(remote);
dologin(dfd, S, forceSTA);
exits(nil);
default:
close(lcfd);
break;
}
}
}

View file

@ -0,0 +1,446 @@
/* RFC2138 */
#include <u.h>
#include <libc.h>
#include <ip.h>
#include <ctype.h>
#include <mp.h>
#include <libsec.h>
#include <bio.h>
#include <ndb.h>
#define AUTHLOG "auth"
enum{ R_AccessRequest=1, /* Packet code */
R_AccessAccept=2,
R_AccessReject=3,
R_AccessChallenge=11,
R_UserName=1,
R_UserPassword=2,
R_NASIPAddress=4,
R_ReplyMessage=18,
R_State=24,
R_NASIdentifier=32
};
typedef struct Secret{
uchar *s;
int len;
} Secret;
typedef struct Attribute{
struct Attribute *next;
uchar type;
uchar len; // number of bytes in value
uchar val[256];
} Attribute;
typedef struct Packet{
uchar code, ID;
uchar authenticator[16];
Attribute first;
} Packet;
// assumes pass is at most 16 chars
void
hide(Secret *shared, uchar *auth, Secret *pass, uchar *x)
{
DigestState *M;
int i, n = pass->len;
M = md5(shared->s, shared->len, nil, nil);
md5(auth, 16, x, M);
if(n > 16)
n = 16;
for(i = 0; i < n; i++)
x[i] ^= (pass->s)[i];
}
int
authcmp(Secret *shared, uchar *buf, int m, uchar *auth)
{
DigestState *M;
uchar x[16];
M = md5(buf, 4, nil, nil); // Code+ID+Length
M = md5(auth, 16, nil, M); // RequestAuth
M = md5(buf+20, m-20, nil, M); // Attributes
md5(shared->s, shared->len, x, M);
return memcmp(x, buf+4, 16);
}
Packet*
newRequest(uchar *auth)
{
static uchar ID = 0;
Packet *p;
p = (Packet*)malloc(sizeof(*p));
if(p == nil)
return nil;
p->code = R_AccessRequest;
p->ID = ++ID;
memmove(p->authenticator, auth, 16);
p->first.next = nil;
p->first.type = 0;
return p;
}
void
freePacket(Packet *p)
{
Attribute *a, *x;
if(!p)
return;
a = p->first.next;
while(a){
x = a;
a = a->next;
free(x);
}
free(p);
}
int
ding(void *v, char *msg)
{
USED(v);
/* syslog(0, AUTHLOG, "ding %s", msg); */
if(strstr(msg, "alarm"))
return 1;
return 0;
}
Packet *
rpc(char *dest, Secret *shared, Packet *req)
{
uchar buf[4096], buf2[4096], *b, *e;
Packet *resp;
Attribute *a;
int m, n, fd, try;
// marshal request
e = buf + sizeof buf;
buf[0] = req->code;
buf[1] = req->ID;
memmove(buf+4, req->authenticator, 16);
b = buf+20;
for(a = &req->first; a; a = a->next){
if(b + 2 + a->len > e)
return nil;
*b++ = a->type;
*b++ = 2 + a->len;
memmove(b, a->val, a->len);
b += a->len;
}
n = b-buf;
buf[2] = n>>8;
buf[3] = n;
// send request, wait for reply
fd = dial(dest, 0, 0, 0);
if(fd < 0){
syslog(0, AUTHLOG, "%s: rpc can't get udp channel", dest);
return nil;
}
atnotify(ding, 1);
m = -1;
for(try = 0; try < 2; try++){
alarm(4000);
m = write(fd, buf, n);
if(m != n){
syslog(0, AUTHLOG, "%s: rpc write err %d %d: %r", dest, m, n);
m = -1;
break;
}
m = read(fd, buf2, sizeof buf2);
alarm(0);
if(m < 0){
syslog(0, AUTHLOG, "%s rpc read err %d: %r", dest, m);
break; // failure
}
if(m == 0 || buf2[1] != buf[1]){ // need matching ID
syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m);
continue;
}
if(authcmp(shared, buf2, m, buf+4) == 0)
break;
syslog(0, AUTHLOG, "%s bad rpc chksum", dest);
}
close(fd);
if(m <= 0)
return nil;
// unmarshal reply
b = buf2;
e = buf2+m;
resp = (Packet*)malloc(sizeof(*resp));
if(resp == nil)
return nil;
resp->code = *b++;
resp->ID = *b++;
n = *b++;
n = (n<<8) | *b++;
if(m != n){
syslog(0, AUTHLOG, "rpc got %d bytes, length said %d", m, n);
if(m > n)
e = buf2+n;
}
memmove(resp->authenticator, b, 16);
b += 16;
a = &resp->first;
a->type = 0;
while(1){
if(b >= e){
a->next = nil;
break; // exit loop
}
a->type = *b++;
a->len = (*b++) - 2;
if(b + a->len > e){ // corrupt packet
a->next = nil;
freePacket(resp);
return nil;
}
memmove(a->val, b, a->len);
b += a->len;
if(b < e){ // any more attributes?
a->next = (Attribute*)malloc(sizeof(*a));
if(a->next == nil){
free(req);
return nil;
}
a = a->next;
}
}
return resp;
}
int
setAttribute(Packet *p, uchar type, uchar *s, int n)
{
Attribute *a;
a = &p->first;
if(a->type != 0){
a = (Attribute*)malloc(sizeof(*a));
if(a == nil)
return -1;
a->next = p->first.next;
p->first.next = a;
}
a->type = type;
a->len = n;
if(a->len > 253 ) // RFC2138, section 5
a->len = 253;
memmove(a->val, s, a->len);
return 0;
}
/* return a reply message attribute string */
char*
replymsg(Packet *p)
{
Attribute *a;
static char buf[255];
for(a = &p->first; a; a = a->next){
if(a->type == R_ReplyMessage){
if(a->len >= sizeof buf)
a->len = sizeof(buf)-1;
memmove(buf, a->val, a->len);
buf[a->len] = 0;
}
}
return buf;
}
/* for convenience while debugging */
char *replymess;
Attribute *stateattr;
void
logPacket(Packet *p)
{
Attribute *a;
char buf[255];
char pbuf[4*1024];
uchar *au = p->authenticator;
int i;
char *np, *e;
e = pbuf + sizeof(pbuf);
np = seprint(pbuf, e, "Packet ID=%d auth=%x %x %x... ", p->ID, au[0], au[1], au[2]);
switch(p->code){
case R_AccessRequest:
np = seprint(np, e, "request\n");
break;
case R_AccessAccept:
np = seprint(np, e, "accept\n");
break;
case R_AccessReject:
np = seprint(np, e, "reject\n");
break;
case R_AccessChallenge:
np = seprint(np, e, "challenge\n");
break;
default:
np = seprint(np, e, "code=%d\n", p->code);
break;
}
replymess = "0000000";
for(a = &p->first; a; a = a->next){
if(a->len > 253 )
a->len = 253;
memmove(buf, a->val, a->len);
np = seprint(np, e, " [%d]", a->type);
for(i = 0; i<a->len; i++)
if(isprint(a->val[i]))
np = seprint(np, e, "%c", a->val[i]);
else
np = seprint(np, e, "\\%o", a->val[i]);
np = seprint(np, e, "\n");
buf[a->len] = 0;
if(a->type == R_ReplyMessage)
replymess = strdup(buf);
else if(a->type == R_State)
stateattr = a;
}
syslog(0, AUTHLOG, "%s", pbuf);
}
static uchar*
getipv4addr(void)
{
Ipifc *nifc;
Iplifc *lifc;
static Ipifc *ifc;
ifc = readipifc("/net", ifc, -1);
for(nifc = ifc; nifc; nifc = nifc->next)
for(lifc = nifc->lifc; lifc; lifc = lifc->next)
if(ipcmp(lifc->ip, IPnoaddr) != 0 && ipcmp(lifc->ip, v4prefix) != 0)
return lifc->ip;
return nil;
}
extern Ndb *db;
/* returns 0 on success, error message on failure */
char*
secureidcheck(char *user, char *response)
{
Packet *req = nil, *resp = nil;
ulong u[4];
uchar x[16];
char *radiussecret;
char ruser[ 64];
char dest[3*IPaddrlen+20];
Secret shared, pass;
char *rv = "authentication failed";
Ndbs s;
Ndbtuple *t, *nt, *tt;
uchar *ip;
static Ndb *netdb;
if(netdb == nil)
netdb = ndbopen(0);
/* bad responses make them disable the fob, avoid silly checks */
if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil)
goto out;
/* get radius secret */
radiussecret = ndbgetvalue(db, &s, "radius", "lra-radius", "secret", &t);
if(radiussecret == nil){
syslog(0, AUTHLOG, "secureidcheck: nil radius secret: %r");
goto out;
}
/* translate user name if we have to */
strcpy(ruser, user);
for(nt = t; nt; nt = nt->entry){
if(strcmp(nt->attr, "uid") == 0 && strcmp(nt->val, user) == 0)
for(tt = nt->line; tt != nt; tt = tt->line)
if(strcmp(tt->attr, "rid") == 0){
strcpy(ruser, tt->val);
break;
}
}
ndbfree(t);
u[0] = fastrand();
u[1] = fastrand();
u[2] = fastrand();
u[3] = fastrand();
req = newRequest((uchar*)u);
if(req == nil)
goto out;
shared.s = (uchar*)radiussecret;
shared.len = strlen(radiussecret);
ip = getipv4addr();
if(ip == nil){
syslog(0, AUTHLOG, "no interfaces: %r\n");
goto out;
}
if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0)
goto out;
if(setAttribute(req, R_UserName, (uchar*)ruser, strlen(ruser)) < 0)
goto out;
pass.s = (uchar*)response;
pass.len = strlen(response);
hide(&shared, req->authenticator, &pass, x);
if(setAttribute(req, R_UserPassword, x, 16) < 0)
goto out;
t = ndbsearch(netdb, &s, "sys", "lra-radius");
if(t == nil){
syslog(0, AUTHLOG, "secureidcheck: nil radius sys search: %r\n");
goto out;
}
for(nt = t; nt; nt = nt->entry){
if(strcmp(nt->attr, "ip") != 0)
continue;
snprint(dest,sizeof dest,"udp!%s!oradius", nt->val);
resp = rpc(dest, &shared, req);
if(resp == nil){
syslog(0, AUTHLOG, "%s nil response", dest);
continue;
}
if(resp->ID != req->ID){
syslog(0, AUTHLOG, "%s mismatched ID req=%d resp=%d",
dest, req->ID, resp->ID);
freePacket(resp);
resp = nil;
continue;
}
switch(resp->code){
case R_AccessAccept:
syslog(0, AUTHLOG, "%s accepted ruser=%s", dest, ruser);
rv = nil;
break;
case R_AccessReject:
syslog(0, AUTHLOG, "%s rejected ruser=%s %s", dest, ruser, replymsg(resp));
rv = "secureid failed";
break;
case R_AccessChallenge:
syslog(0, AUTHLOG, "%s challenge ruser=%s %s", dest, ruser, replymsg(resp));
rv = "secureid out of sync";
break;
default:
syslog(0, AUTHLOG, "%s code=%d ruser=%s %s", dest, resp->code, ruser, replymsg(resp));
break;
}
break; // we have a proper reply, no need to ask again
}
ndbfree(t);
free(radiussecret);
out:
freePacket(req);
freePacket(resp);
return rv;
}

244
src/cmd/secstore/secuser.c Normal file
View file

@ -0,0 +1,244 @@
#include <u.h>
#include <libc.h>
#include <mp.h>
#include <libsec.h>
#include "SConn.h"
#include "secstore.h"
int verbose;
static void userinput(char *, int);
char *SECSTORE_DIR;
static void
ensure_exists(char *f, ulong perm)
{
int fd;
if(access(f, AEXIST) >= 0)
return;
if(verbose)
fprint(2,"first time setup for secstore: create %s %lo\n", f, perm);
fd = create(f, OREAD, perm);
if(fd < 0){
fprint(2, "unable to create %s\n", f);
exits("secstored directories");
}
close(fd);
}
int
main(int argc, char **argv)
{
int isnew;
char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
char *pass, *passck;
long expsecs;
mpint *H = mpnew(0), *Hi = mpnew(0);
PW *pw;
Tm *tm;
SECSTORE_DIR = unsharp("#9/secstore");
ARGBEGIN{
case 'v':
verbose++;
break;
}ARGEND;
if(argc!=1){
print("usage: secuser [-v] <user>\n");
exits("usage");
}
ensure_exists(SECSTORE_DIR, DMDIR|0755L);
snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
ensure_exists(home, DMDIR|0755L);
snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
ensure_exists(home, DMDIR|0700L);
id = argv[0];
if(verbose)
fprint(2,"secuser %s\n", id);
if((pw = getPW(id,1)) == nil){
isnew = 1;
print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
pw = emalloc(sizeof(*pw));
pw->id = estrdup(id);
snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
if(access(home, AEXIST) == 0){
print("new user, but directory %s already exists\n", home);
exits(home);
}
}else{
isnew = 0;
}
/* get main password for id */
for(;;){
if(isnew)
snprint(prompt, sizeof(prompt), "%s password", id);
else
snprint(prompt, sizeof(prompt), "%s password [default = don't change]", id);
pass = readcons(prompt, nil, 1);
if(pass == nil){
print("getpass failed\n");
exits("getpass failed");
}
if(verbose)
print("%ld characters\n", strlen(pass));
if(pass[0] == '\0' && isnew == 0)
break;
if(strlen(pass) >= 7)
break;
print("password must be at least 7 characters\n");
}
if(pass[0] != '\0'){
snprint(prompt, sizeof(prompt), "retype password");
if(verbose)
print("confirming...\n");
passck = readcons(prompt, nil, 1);
if(passck == nil){
print("getpass failed\n");
exits("getpass failed");
}
if(strcmp(pass, passck) != 0){
print("passwords didn't match\n");
exits("no match");
}
memset(passck, 0, strlen(passck));
free(passck);
hexHi = PAK_Hi(id, pass, H, Hi);
memset(pass, 0, strlen(pass));
free(pass);
free(hexHi);
mpfree(H);
pw->Hi = Hi;
}
/* get expiration time (midnight of date specified) */
if(isnew)
expsecs = time(0) + 365*24*60*60;
else
expsecs = pw->expire;
for(;;){
tm = localtime(expsecs);
print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
tm->mday, tm->mon, tm->year+1900);
userinput(buf, sizeof(buf));
if(strlen(buf) == 0)
break;
if(strlen(buf) != 8){
print("!bad date format: %s\n", buf);
continue;
}
tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
if(tm->mday > 31 || tm->mday < 1){
print("!bad day of month: %d\n", tm->mday);
continue;
}
tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
if(tm->mon > 11 || tm->mday < 0){
print("!bad month: %d\n", tm->mon + 1);
continue;
}
tm->year = atoi(buf+4) - 1900;
if(tm->year < 70){
print("!bad year: %d\n", tm->year + 1900);
continue;
}
tm->sec = 59;
tm->min = 59;
tm->hour = 23;
tm->yday = 0;
expsecs = tm2sec(tm);
break;
}
pw->expire = expsecs;
/* failed logins */
if(pw->failed != 0 )
print("clearing %d failed login attempts\n", pw->failed);
pw->failed = 0;
/* status bits */
if(isnew)
pw->status = Enabled;
for(;;){
print("Enabled or Disabled [default %s]: ",
(pw->status & Enabled) ? "Enabled" : "Disabled" );
userinput(buf, sizeof(buf));
if(strlen(buf) == 0)
break;
if(buf[0]=='E' || buf[0]=='e'){
pw->status |= Enabled;
break;
}
if(buf[0]=='D' || buf[0]=='d'){
pw->status = pw->status & ~Enabled;
break;
}
}
for(;;){
print("require STA? [default %s]: ",
(pw->status & STA) ? "yes" : "no" );
userinput(buf, sizeof(buf));
if(strlen(buf) == 0)
break;
if(buf[0]=='Y' || buf[0]=='y'){
pw->status |= STA;
break;
}
if(buf[0]=='N' || buf[0]=='n'){
pw->status = pw->status & ~STA;
break;
}
}
/* free form field */
if(isnew)
pw->other = nil;
print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
userinput(buf, 72); /* 72 comes from password.h */
if(buf[0])
if((pw->other = strdup(buf)) == nil)
sysfatal("strdup");
syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
if(putPW(pw) < 0){
print("error writing entry: %r\n");
exits("can't write password file");
}else{
print("change written\n");
if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){
print("unable to create %s: %r\n", home);
exits(home);
}
}
exits("");
return 1; /* keep other compilers happy */
}
static void
userinput(char *buf, int blen)
{
int n;
while(1){
n = read(0, buf, blen);
if(n<=0)
exits("read error");
if(buf[n-1]=='\n'){
buf[n-1] = '\0';
return;
}
buf += n; blen -= n;
if(blen<=0)
exits("input too large");
}
}

View file

@ -26,13 +26,3 @@ estrdup(char *s)
sysfatal("estrdup");
return s;
}
char *
getpassm(char *prompt)
{
char *p = getpass(prompt);
if(p == nil || (p = strdup(p)) == nil)
sysfatal("getpassm");
return p;
}