shithub: totp

Download patch

ref: 8e3d8120a945bad722e218963699f0a17a853d86
author: sirjofri <sirjofri@sirjofri.de>
date: Fri Sep 8 09:43:43 EDT 2023

first version, with time, snarf and update

--- /dev/null
+++ b/mkfile
@@ -1,0 +1,6 @@
+</$objtype/mkfile
+
+TARG=totp
+OFILES=totp.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/totp.c
@@ -1,0 +1,435 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <auth.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-D] [-t]\n", argv0);
+	exits(nil);
+}
+
+int chatty;
+
+typedef struct Account Account;
+struct Account {
+	char *user;
+	char code[12];
+	int remaining;
+	int expanded;
+	int hitstart;
+	int hitend;
+	Account *next;
+};
+
+Account *accounts;
+int numaccounts;
+
+// return seconds, write buf with OTP
+int
+readtotp(char *buf, char *user)
+{
+	AuthRpc *rpc;
+	char *s;
+	int n, fd, rem;
+	char *toks[2];
+	
+	fd = open("/mnt/factotum/rpc", ORDWR);
+	if (fd < 0)
+		sysfatal("error accessing factotum: %r");
+	
+	rpc = auth_allocrpc(fd);
+	if (!rpc)
+		sysfatal("error: %r");
+	
+	s = smprint("proto=totp user=%q role=client", user);
+	n = strlen(s);
+	
+	if (chatty)
+		print("rpc: %s\n", s);
+	
+	if (auth_rpc(rpc, "start", s, n) != ARok)
+		goto Err;
+	
+	free(s);
+	
+	auth_rpc(rpc, "read", nil, 0);
+	
+	if (tokenize(rpc->arg, toks, 2) != 2)
+		goto Err1;
+	
+	snprint(buf, 10, "%s", toks[0]);
+	rem = atoi(toks[1]);
+	
+	if (chatty)
+		print("code: %s, remaining: %d\n", buf, rem);
+	
+	auth_freerpc(rpc);
+	close(fd);
+	return rem;
+	
+Err:
+	close(fd);
+	free(s);
+Err1:
+	auth_freerpc(rpc);
+	*buf = 0;
+	return -1;
+}
+
+void
+readaccount(Account *acc)
+{
+	int rem = readtotp(acc->code, acc->user);
+	
+	if (rem < 0) {
+		print("error reading data for account '%s'", acc->user);
+		return;
+	}
+	
+	if (chatty)
+		print("readaccount: %s\n", acc->user);
+	
+	acc->remaining = rem;
+}
+
+void
+foraccount(void (*fn)(Account*))
+{
+	Account *a = accounts;
+	
+	if (a == nil)
+		return;
+	
+	while (a != nil) {
+		fn(a);
+		a = a->next;
+	}
+}
+
+void
+printaccount(Account *a)
+{
+	if (chatty)
+		print("print account '%s'\n", a->user);
+	
+	print("%s: %s (%d)\n", a->user, a->code, a->remaining);
+}
+
+char*
+parseuser(char **toks, int ntoks)
+{
+	int i = 2, n;
+	char *s;
+	
+	while (i < ntoks && strncmp("user=", toks[i], 5))
+		i++;
+	
+	s = toks[i] + 5; // get rid of "user="
+	
+	// unquote, if needed
+	if (*s == '\'') {
+		s += 1;
+		n = strlen(s);
+		s[n-1] = 0;
+	}
+	
+	return strdup(s);
+}
+
+int
+addaccount(char *s)
+{
+	Account *a;
+	char *toks[16];
+	int ntoks;
+	
+	ntoks = tokenize(s, toks, 16);
+	
+	if (ntoks < 3) {
+		sysfatal("bad response from factotum!");
+	}
+	
+	if (strcmp("key", toks[0]) != 0 || strcmp("proto=totp", toks[1]) != 0)
+		return 0;
+	
+	
+	int valid = 0;
+	for (int i = 0; i < ntoks; i++) {
+		if (strcmp("role=client", toks[i]) == 0) {
+			valid = 1;
+			break;
+		}
+	}
+	if (!valid)
+		return 0;
+	
+	
+	if (!accounts) {
+		accounts = malloc(sizeof(Account));
+		if (!accounts)
+			sysfatal("err: %r");
+		a = accounts;
+	} else {
+		a = accounts;
+		while (a->next != nil)
+			a = a->next;
+		
+		a->next = malloc(sizeof(Account));
+		if (!a->next)
+			sysfatal("err: %r");
+		a = a->next;
+	}
+	a->hitstart = a->hitend = -1;
+	*(a->code) = 0;
+	a->next = nil;
+	a->expanded = 0;
+	
+	a->user = parseuser(toks, ntoks);
+	
+	return 1;
+}
+
+void
+freeaccount(Account* acc)
+{
+	Account* c = acc->next;
+	
+	if (acc->user)
+		free(acc->user);
+	
+	free(acc);
+	if (c)
+		freeaccount(c);
+}
+
+int
+readaccounts(void)
+{
+	Biobuf *bfd;
+	char *s;
+	int num = 0;
+	
+	if (accounts)
+		freeaccount(accounts);
+	
+	bfd = Bopen("/mnt/factotum/ctl", OREAD);
+	if (!bfd)
+		sysfatal("error reading factotum: %r");
+	
+	while (s = Brdstr(bfd, '\n', 1)) {
+		num += addaccount(s);
+		free(s);
+	}
+	
+	Bterm(bfd);
+	
+	if (chatty)
+		print("%d accounts added\n", num);
+	return num;
+}
+
+/* images */
+Image *back;
+Image *linecol;
+
+int lineheight;
+
+/* draw params */
+Image *dp_screen;
+int dp_num; /* number of accounts */
+int dp_id; /* ID of this account */
+int dp_numexpanded; /* number of expanded elements to consider */
+
+void
+drawaccount(Account *account)
+{
+	Point start, end, l1, l2;
+	int numnormal;
+	int margin_l = 10;
+	char rem[16];
+	
+	numnormal = dp_id - dp_numexpanded;
+	
+	l1.x = margin_l;
+	l1.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20) + 5;
+	l1 = addpt(l1, dp_screen->r.min);
+	string(dp_screen, l1, linecol, ZP, font, account->user);
+	
+	account->hitstart = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20);
+	
+	if (account->expanded) {
+		l2.x = margin_l;
+		l2.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20) + 5 + lineheight + 5;
+		l2 = addpt(l2, dp_screen->r.min);
+		string(dp_screen, l2, linecol, ZP, font, account->code);
+		
+		snprint(rem, 16, "%d", account->remaining);
+		l2.x = screen->r.max.x - margin_l - stringwidth(font, rem);
+		string(dp_screen, l2, linecol, ZP, font, rem);
+		
+		dp_numexpanded++;
+	} else {
+		numnormal++;
+	}
+	
+	account->hitend = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20);
+	
+	start.x = 0;
+	start.y = numnormal*(lineheight+15) + dp_numexpanded*(lineheight*2+20);
+	end.x = dp_screen->r.max.x - dp_screen->r.min.x;
+	end.y = start.y;
+	
+	start = addpt(start, dp_screen->r.min);
+	end = addpt(end, dp_screen->r.min);
+	
+	line(dp_screen, start, end, 0, 0, 0, linecol, ZP);
+	
+	dp_id++;
+}
+
+/* check hit account params */
+Point ch_point;
+Account *ch_clicked;
+
+void
+checkhitaccount(Account *account)
+{
+	int ref;
+	
+	if (account->hitend < 0 || account->hitstart < 0)
+		return;
+	
+	ref = ch_point.y;
+	if (ref < account->hitend && ref > account->hitstart) {
+		ch_clicked = account;
+		if (chatty)
+			print("clicked element %s\n", account->user);
+	}
+}
+
+Account*
+accounthit(Point p)
+{
+	ch_point = subpt(p, screen->r.min);
+	ch_clicked = nil;
+	foraccount(checkhitaccount);
+	
+	return ch_clicked;
+}
+
+void
+snarf(Account *account)
+{
+	int fd = open("/dev/snarf", OWRITE);
+	if (fd < 0)
+		return;
+	
+	fprint(fd, "%s", account->code);
+	close(fd);
+}
+
+void
+redraw(Image *screen)
+{
+	draw(screen, screen->r, back, nil, ZP);
+	dp_screen = screen;
+	dp_num = numaccounts;
+	dp_id = 0;
+	dp_numexpanded = 0;
+	foraccount(drawaccount);
+}
+
+void
+eresized(int new)
+{
+	if (new && getwindow(display, Refnone) < 0)
+		fprint(2, "can't reattach to window");
+	redraw(screen);
+}
+
+void
+main(int argc, char **argv)
+{
+	int textbased = 0;
+	chatty = 0;
+	accounts = nil;
+	
+	Event e;
+	Mouse m;
+	Menu menu;
+	char *mstr[] = {" update ", "exit", 0};
+	int key, timer, menuhit;
+	
+	Account *selected;
+	
+	ARGBEGIN {
+	case 't':
+		textbased++;
+		break;
+	case 'D':
+		chatty++;
+		break;
+	} ARGEND;
+	
+	quotefmtinstall();
+	
+	numaccounts = readaccounts();
+	
+	if (textbased) {
+		foraccount(readaccount);
+		foraccount(printaccount);
+		exits(nil);
+	}
+	
+	if (initdraw(0, 0, "totp") < 0)
+		sysfatal("initdraw failed: %r");
+	
+	back = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DWhite);
+	linecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlack);
+	lineheight = stringsize(font, "M").y;
+	
+	redraw(screen);
+	
+	einit(Emouse);
+	timer = etimer(0, 1000);
+	
+	menu.item = mstr;
+	menu.lasthit = 0;
+	for (;;) {
+		key = event(&e);
+		if (key == Emouse) {
+			m = e.mouse;
+			if (m.buttons & 4) {
+				menuhit = emenuhit(3, &m, &menu);
+				if (menuhit == 0) {
+					numaccounts = readaccounts();
+					foraccount(readaccount);
+				}
+				if (menuhit == 1)
+					exits(nil);
+			}
+			if (m.buttons & 1) {
+				selected = accounthit(m.xy);
+				if (selected) {
+					selected->expanded = !selected->expanded;
+					redraw(screen);
+				}
+			}
+			if (m.buttons & 2) {
+				selected = accounthit(m.xy);
+				if (selected) {
+					snarf(selected);
+				}
+			}
+		}
+		if (key == timer) {
+			foraccount(readaccount);
+			redraw(screen);
+		}
+	}
+}