Commit Diff


commit - 1c454de92a2b6867a9ce9c3dd5a14d8a43edfdef
commit + f9d09706c26867a700fc666e9809f029520f5848
blob - c1c7d625060f9c5984c837f36bb35b671ad96ee9
blob + a8a7c9466451b8e9def914e038d5915e00df8585
--- gotwebd/auth.c
+++ gotwebd/auth.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2025 Stefan Sperling <stsp@openbsd.org>
+ * Copyright (c) 2025 Omar Polo <op@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -36,73 +37,15 @@
 #define AUTH_SOCKET_BACKLOG 4
 
 struct gotwebd_auth_client {
-	TAILQ_ENTRY(gotwebd_auth_client) entry;
-	uint32_t id;
 	int fd;
+	int cmd_done;
 	uid_t euid;
-	struct imsgev iev;
+	struct bufferevent *bev;
 };
-TAILQ_HEAD(gotwebd_auth_clients, gotwebd_auth_client);
 
-static struct gotwebd_auth_clients clients[GOTWEBD_MAXCLIENTS * 4];
-static SIPHASH_KEY clients_hash_key;
 static volatile int client_cnt;
 static int inflight;
 
-static void
-clients_init(void)
-{
-	uint64_t slot;
-
-	arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
-
-	for (slot = 0; slot < nitems(clients); slot++)
-		TAILQ_INIT(&clients[slot]);
-}
-
-static uint64_t
-client_hash(uint32_t client_id)
-{
-	return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
-}
-
-static void
-add_client(struct gotwebd_auth_client *client)
-{
-	uint64_t slot = client_hash(client->id) % nitems(clients);
-	TAILQ_INSERT_HEAD(&clients[slot], client, entry);
-	client_cnt++;
-}
-
-static struct gotwebd_auth_client *
-find_client(uint32_t client_id)
-{
-	uint64_t slot;
-	struct gotwebd_auth_client *c;
-
-	slot = client_hash(client_id) % nitems(clients);
-	TAILQ_FOREACH(c, &clients[slot], entry) {
-		if (c->id == client_id)
-			return c;
-	}
-
-	return NULL;
-}
-
-static uint32_t
-get_client_id(void)
-{
-	int duplicate = 0;
-	uint32_t id;
-
-	do {
-		id = arc4random();
-		duplicate = (find_client(id) != NULL);
-	} while (duplicate || id == 0);
-
-	return id;
-}
-
 static int
 auth_socket_listen(struct gotwebd *env, struct socket *sock,
     uid_t uid, gid_t gid)
@@ -240,37 +183,94 @@ accept_reserve(int fd, struct sockaddr *addr, socklen_
 	return ret;
 }
 
-static const struct got_error *
-disconnect(struct gotwebd_auth_client *client)
+static void
+client_err(struct bufferevent *bev, short error, void *d)
 {
-	uint64_t slot;
-	int client_fd;
+	struct gotwebd_auth_client *client = d;
 
-	log_debug("client on fd %d disconnecting", client->fd);
+	log_debug("closing connection with client fd=%d; error=%d",
+	    client->fd, error);
 
-	slot = client_hash(client->id) % nitems(clients);
-	TAILQ_REMOVE(&clients[slot], client, entry);
-
-	if (client->iev.ibuf.fd != -1)
-		imsgbuf_clear(&client->iev.ibuf);
-
-	client_fd = client->fd;
+	bufferevent_free(client->bev);
+	close(client->fd);
 	free(client);
+
 	inflight--;
 	client_cnt--;
-	if (close(client_fd) == -1)
-		return got_error_from_errno("close");
-
-	return NULL;
 }
 
 static void
-auth_request(int fd, short event, void *arg)
+client_read(struct bufferevent *bev, void *d)
 {
-	log_warnx("%s", __func__);
+	struct gotwebd_auth_client *client = d;
+	struct evbuffer *in = EVBUFFER_INPUT(bev);
+	struct evbuffer *out = EVBUFFER_OUTPUT(bev);
+	char *line, *cmd;
+	size_t linelen;
+	const char *hostname;
+
+	if (client->cmd_done) {
+		log_warnx("%s: attempt to send another command", __func__);
+		client_err(bev, EVBUFFER_READ, client);
+		return;
+	}
+
+	line = evbuffer_readln(in, &linelen, EVBUFFER_EOL_LF);
+	if (line == NULL) {
+		/*
+		 * there is no line yet to read.  however, error if we
+		 * have too much data buffered without a newline
+		 * character.
+		 */
+		if (EVBUFFER_LENGTH(in) > LINE_MAX)
+			client_err(bev, EVBUFFER_READ, client);
+		return;
+	}
+
+	cmd = line;
+	if (strncmp(cmd, "login", 5) == 0) {
+		cmd += 5;
+		cmd += strspn(cmd, " \t");
+		hostname = cmd;
+
+		/* do something with hostname */
+		/* xxx */
+		const char *code = "42";
+
+		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);
+			return;
+		}
+
+		client->cmd_done = 1;
+		return;
+	}
+
+	if (evbuffer_add_printf(out, "err unknown command\n") == -1) {
+		log_warnx("%s: evbuffer_add_printf failed", __func__);
+		client_err(bev, EVBUFFER_READ, client);
+		return;
+	}
+	client->cmd_done = 1;
+	return;
 }
 
 static void
+client_write(struct bufferevent *bev, void *d)
+{
+	struct gotwebd_auth_client *client = d;
+	struct evbuffer *out = EVBUFFER_OUTPUT(bev);
+
+	if (EVBUFFER_LENGTH(out) == 0) {
+		/* reply sent */
+		client_err(bev, EVBUFFER_WRITE, client);
+		return;
+	}
+}
+
+static void
 auth_accept(int fd, short event, void *arg)
 {
 	struct imsgev *iev = arg;
@@ -328,31 +328,35 @@ auth_accept(int fd, short event, void *arg)
 		log_warn("%s: calloc", __func__);
 		goto err;
 	}
-	client->iev.ibuf.fd = -1;
-	client->id = get_client_id();
 	client->fd = s;
 	client->euid = euid;
 	s = -1;
 
-	if (imsgbuf_init(&client->iev.ibuf, client->fd) == -1) {
-		log_warn("imsgbuf_init");
+	client->bev = bufferevent_new(client->fd, client_read, client_write,
+	    client_err, client);
+	if (client->bev == NULL) {
+		log_warn("%s: bufferevent_new failed", __func__);
 		goto err;
 	}
+	bufferevent_enable(client->bev, EV_READ|EV_WRITE);
 
-	client->iev.handler = auth_request;;
-	client->iev.data = client;
-	event_set(&client->iev.ev, client->fd, EV_READ, auth_request, client);
-	event_add(&client->iev.ev, NULL);
+	/*
+	 * undocumented; but these are seconds.  10s should be plenty
+	 * for both receiving a request and sending the reply.
+	 */
+	bufferevent_settimeout(client->bev, 10, 10);
 
-	add_client(client);
-
 	log_debug("%s: new client connected on fd %d uid %d gid %d", __func__,
 	    client->fd, euid, egid);
 	return;
 err:
 	inflight--;
-	if (client)
-		disconnect(client);
+	if (client) {
+		if (client->bev != NULL)
+			bufferevent_free(client->bev);
+		close(client->fd);
+		free(client);
+	}
 	if (s != -1)
 		close(s);
 }
@@ -473,7 +477,6 @@ gotwebd_auth(struct gotwebd *env, int fd)
 	struct event	 sighup, sigint, sigusr1, sigchld, sigterm;
 	struct event_base *evb;
 
-	clients_init();
 	evb = event_init();
 
 	if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL)