wm: infra

ref: 95dc8f40e37658bf39746991da74ac6582a62b7f
dir: /chusr.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <authsrv.h>

#define	KEYDB		"/mnt/keys"
#define NETKEYDB	"/mnt/netkeys"
#define KEYDBBUF	(sizeof NETKEYDB)	/* enough for any keydb prefix */
#define AUTHLOG		"auth"

enum{
	MAXNETCHAL	= 100000,		/* max securenet challenge */
	Maxpath		= 256,
};

enum
{
	Nemail		= 10,
	Plan9		= 1,
	Securenet	= 2,
};

typedef struct
{
	char	*user;
	char	*postid;
	char	*name;
	char	*dept;
	char	*email[Nemail];
} Acctbio;

typedef struct {
	char	*keys;
	char	*msg;
	char	*who;
	Biobuf 	*b;
} Fs;

Fs fs =	{ "/mnt/keys", "plan 9 key", "/adm/keys.who", 0 };

static uchar zeros[16];


void    install(char*, char*, Authkey*, long);
int exists(char*, char*);
void	private(void);
int	setkey(char*, char*, Authkey*);
char*	setdeskey(char*, char*, char*);
uchar*	setaeskey(char*, char*, uchar*);
char*	setsecret(char*, char*, char*);
void	wrbio(char*, Acctbio*);
int	writefile(char*, char*, int);
int	deskeyfmt(Fmt*);

void
usage(void)
{
	fprint(2, "usage: %s [-d dept] [-e email] [-i postid] [-s sponsor\'s email] -p pass user\n", argv0);
	exits("usage");
}

void
main(int argc, char *argv[])
{
	char *u, *pass;
	long t;
	Authkey key;
	Acctbio a;
	Fs *f;

	fmtinstall('K', deskeyfmt);

	ARGBEGIN{
    case 'd':
        a.dept = EARGF(usage());
        break;
    case 'i':
        a.postid = EARGF(usage());
        break;
    case 'e':
        a.email[0] = EARGF(usage());
        break;
	case 'p':
		pass = EARGF(usage());
		break;
    /* sponsor's email */
    case 's':
        // a->email[1] = EARGF(usage());
        break;

	default:
		usage();
	}ARGEND

	if(argc < 3)
		usage();
	u = *argv;
	if(memchr(u, '\0', ANAMELEN) == 0)
		sysfatal("bad user name");

	private();
	t = 0;
	a.user = 0;
	memset(&key, 0, sizeof(key));
	f = &fs;

	passtokey(&key, pass);

	install(f->keys, u, &key, t);

	if(setsecret(KEYDB, u, pass) == 0)
		sysfatal("error writing Inferno/POP secret");

	wrbio(f->who, &a);

	print("user %s installed for Plan 9\n", u);
	syslog(0, AUTHLOG, "user %s installed for plan 9", u);

	exits(0);
}

void
install(char *db, char *u, Authkey *key, long t)
{
	char buf[KEYDBBUF+ANAMELEN+20];
	int fd;

	if(!exists(db, u)){
		snprint(buf, sizeof(buf), "%s/%s", db, u);
		fd = create(buf, OREAD, 0777|DMDIR);
		if(fd < 0)
			sysfatal("can't create user %s: %r", u);
		close(fd);
	}

	if(!setkey(db, u, key))
		sysfatal("can't set key: %r");

	if(t == -1)
		return;
	snprint(buf, sizeof(buf), "%s/%s/expire", db, u);
	fd = open(buf, OWRITE);
	if(fd < 0 || fprint(fd, "%ld", t) < 0)
		sysfatal("can't write expiration time");
	close(fd);
}

int
exists(char *db, char *u)
{
	char buf[KEYDBBUF+ANAMELEN+6];

	snprint(buf, sizeof(buf), "%s/%s/expire", db, u);
	if(access(buf, 0) < 0)
		return 0;
	return 1;
}


/* these functions are from /sys/src/cmd/auth/lib/ */

int
deskeyfmt(Fmt *f)
{
	uchar key[8];
	char buf[32];
	uchar *k;
	int i;

	k = va_arg(f->args, uchar*);
	key[0] = 0;
	for(i = 0; i < 7; i++){
		key[i] |= k[i] >> i;
		key[i] &= ~1;
		key[i+1] = k[i] << (7 - i);
	}
	key[7] &= ~1;
	sprint(buf, "%.3uo %.3uo %.3uo %.3uo %.3uo %.3uo %.3uo %.3uo",
		key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
	fmtstrcpy(f, buf);
	return 0;
}

/* don't allow other processes to debug us and steal keys */
void
private(void)
{
	int fd;
	char buf[32];
	static char pmsg[] = "Warning! %s can't protect itself from debugging: %r\n";
	static char smsg[] = "Warning! %s can't turn off swapping: %r\n";

	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
	fd = open(buf, OWRITE|OCEXEC);
	if(fd < 0){
		fprint(2, pmsg, argv0);
		return;
	}
	if(fprint(fd, "private") < 0)
		fprint(2, pmsg, argv0);
	if(fprint(fd, "noswap") < 0)
		fprint(2, smsg, argv0);
	close(fd);
}

int
setkey(char *db, char *user, Authkey *key)
{
	int ret;

	ret = setdeskey(db, user, key->des) != nil;
	if(tsmemcmp(key->aes, zeros, AESKEYLEN) != 0)
		ret |= setaeskey(db, user, key->aes) != nil;
	return ret;
}

char*
setdeskey(char *db, char *user, char *key)
{
	char filename[Maxpath];

	snprint(filename, sizeof filename, "%s/%s/key", db, user);
	if(writefile(filename, key, DESKEYLEN) != DESKEYLEN)
		return nil;
	return key;
}

uchar*
setaeskey(char *db, char *user, uchar *key)
{
	char filename[Maxpath];

	snprint(filename, sizeof filename, "%s/%s/aeskey", db, user);
	if(writefile(filename, (char*)key, AESKEYLEN) != AESKEYLEN)
		return nil;
	return key;
}

char*
setsecret(char *db, char *user, char *secret)
{
	char filename[Maxpath];

	snprint(filename, sizeof filename, "%s/%s/secret", db, user);
	if(writefile(filename, secret, strlen(secret)) != strlen(secret))
		return nil;
	return secret;
}

void
wrbio(char *file, Acctbio *a)
{
	char buf[1024];
	int i, fd, n;

	fd = open(file, OWRITE);
	if(fd < 0){
		fd = create(file, OWRITE, 0660);
		if(fd < 0)
			sysfatal("can't create %s", file);
	}
	if(seek(fd, 0, 2) < 0)
		sysfatal("can't seek %s", file);

	if(a->postid == 0)
		a->postid = "";
	if(a->name == 0)
		a->name = "";
	if(a->dept == 0)
		a->dept = "";
	if(a->email[0] == 0)
		a->email[0] = strdup(a->user);

	n = 0;
	n += snprint(buf+n, sizeof(buf)-n, "%s|%s|%s|%s",
		a->user, a->postid, a->name, a->dept);
	for(i = 0; i < Nemail; i++){
		if(a->email[i] == 0)
			break;
		n += snprint(buf+n, sizeof(buf)-n, "|%s", a->email[i]);
	}
	n += snprint(buf+n, sizeof(buf)-n, "\n");

	write(fd, buf, n);
	close(fd);
}

int
writefile(char *file, char *buf, int n)
{
	int fd;

	fd = open(file, OWRITE);
	if(fd < 0)
		return -1;
	n = write(fd, buf, n);
	close(fd);
	return n;
}