Commit Diff


commit - 06efacffbc9efa9ab5a6f1337e8c7acbffa0e6ee
commit + d35d2167be33e815e3cd3e27cfbabc900e53d254
blob - bde278404461202ccc124ec456b9c0009d3e7807
blob + e7ad89121d041135c012b97ce014e7fbc805ba10
--- gotwebd/auth.c
+++ gotwebd/auth.c
@@ -29,7 +29,9 @@
 #include <unistd.h>
 
 #include <openssl/bio.h>
+#include <openssl/buffer.h>
 #include <openssl/evp.h>
+#include <openssl/hmac.h>
 
 #include "got_error.h"
 #include "got_reference.h"
@@ -49,10 +51,20 @@ struct gotwebd_auth_client {
 static volatile int client_cnt;
 static int inflight;
 
+static char token_secret[32];
+
+#if 0
+int
+auth_init(void)
+{
+	arc4random_buf(token_secret);
+}
+#endif
+
 /*
  * The token format is:
  *
- *    "v1"[issued at/64bit][expire/64bit][username]"\0"[host]
+ *    "v1"[issued at/64bit][expire/64bit][username]"\0"[host]"\0"
  *
  * followed by the HMAC-SHA256 of it, all encoded in base64.
  */
@@ -75,6 +87,93 @@ auth_check_token(const char *token)
 	return !strcmp(token, "42");
 }
 
+/*  */
+static char *
+auth_gen_token(const char *username, const char *hostname)
+{
+	BIO		*bmem, *b64;
+	BUF_MEM		*bufm;
+	char		 hmac[EVP_MAX_MD_SIZE];
+	char		*enc;
+	FILE		*fp;
+	char		*tok;
+	uint64_t	 issued, expire; /* assume size_t(time_t) == 8 */
+	size_t		 siz, ulen, hlen;
+	unsigned int	 hmaclen;	/* openssl... */
+
+	issued = time(NULL);
+	expire = issued + (24 * 60 * 60); /* now + 1 day */
+
+	fp = open_memstream(&tok, &siz);
+	if (fp == NULL)
+		return NULL;
+
+	/* include NUL */
+	ulen = strlen(username) + 1;
+	hlen = strlen(hostname) + 1;
+
+	if (fwrite("v1", 1, 3, fp) != 3 ||
+	    fwrite(&issued, 1, 8, fp) != 8 ||
+	    fwrite(&expire, 1, 8, fp) != 8 ||
+	    fwrite(username, 1, ulen, fp) != ulen ||
+	    fwrite(hostname, 1, hlen, fp) != hlen) {
+		fclose(fp);
+		free(tok);
+		return NULL;
+	}
+
+	if (fclose(fp) == EOF) {
+		free(tok);
+		return NULL;
+	}
+
+	if (siz > INT_MAX) {
+		/*
+		 * can't really happen, isn't it?  yet, openssl
+		 * sometimes likes to take ints so I'd prefer to
+		 * assert.
+		 */
+		free(tok);
+		return NULL;
+	}
+
+	if (HMAC(EVP_sha256(), token_secret, sizeof(token_secret), tok, siz,
+	    hmac, &hmaclen) == NULL) {
+		free(tok);
+		return NULL;
+	}
+
+	bmem = BIO_new(BIO_s_mem());
+	if (bmem == NULL) {
+		free(tok);
+		return NULL;
+	}
+
+	b64 = BIO_new(BIO_f_base64());
+	if (b64 == NULL) {
+		free(tok);
+		return NULL;
+	}
+
+	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+	b64 = BIO_push(b64, bmem);
+
+	if (BIO_write(b64, tok, siz) != (int)siz ||
+	    BIO_write(b64, hmac, hmaclen) != hmaclen ||
+	    BIO_flush(b64) <= 0) {
+		free(tok);
+		BIO_free_all(b64);
+		return NULL;
+	}
+
+	BIO_get_mem_ptr(b64, &bufm);
+	enc = strndup(bufm->data, bufm->length);
+
+	free(tok);
+	BIO_free_all(b64);
+	return enc;
+}
+
 static int
 auth_socket_listen(struct gotwebd *env, struct socket *sock,
     uid_t uid, gid_t gid)
@@ -234,7 +333,7 @@ client_read(struct bufferevent *bev, void *d)
 	struct gotwebd_auth_client *client = d;
 	struct evbuffer *in = EVBUFFER_INPUT(bev);
 	struct evbuffer *out = EVBUFFER_OUTPUT(bev);
-	char *line, *cmd;
+	char *line, *cmd, *code;
 	size_t linelen;
 	const char *hostname;
 
@@ -262,16 +361,22 @@ client_read(struct bufferevent *bev, void *d)
 		cmd += strspn(cmd, " \t");
 		hostname = cmd;
 
-		/* do something with hostname */
-		/* xxx */
-		const char *code = "42";
+		/* XXX */
+		code = auth_gen_token("op", hostname);
+		if (code == NULL) {
+			log_warn("%s: auth_gen_token failed", __func__);
+			client_err(bev, EVBUFFER_READ, client);
+			return;
+		}
 
 		if (evbuffer_add_printf(out, "ok https://%s/?login=%s\n",
 		    hostname, code) == -1) {
 			log_warnx("%s: evbuffer_add_printf failed", __func__);
 			client_err(bev, EVBUFFER_READ, client);
+			free(code);
 			return;
 		}
+		free(code);
 
 		client->cmd_done = 1;
 		return;