add secstored; use readcons
This commit is contained in:
parent
d93fca6a7a
commit
096ff3e14a
11 changed files with 1196 additions and 22 deletions
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
35
src/cmd/secstore/secacct.c
Normal file
35
src/cmd/secstore/secacct.c
Normal 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
28
src/cmd/secstore/secchk.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
420
src/cmd/secstore/secstored.c
Normal file
420
src/cmd/secstore/secstored.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
446
src/cmd/secstore/secureidcheck.c
Normal file
446
src/cmd/secstore/secureidcheck.c
Normal 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
244
src/cmd/secstore/secuser.c
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue