commit f9d09706c26867a700fc666e9809f029520f5848 from: Omar Polo date: Sat May 17 16:12:45 2025 UTC gotwebd: start to implement the auth feature rearranged the code a bit; let's use a bufferevent since we're not going to use imsg for this anyway. for now it just replies back with a dummy URL. commit - 1c454de92a2b6867a9ce9c3dd5a14d8a43edfdef commit + f9d09706c26867a700fc666e9809f029520f5848 blob - c1c7d625060f9c5984c837f36bb35b671ad96ee9 blob + a8a7c9466451b8e9def914e038d5915e00df8585 --- gotwebd/auth.c +++ gotwebd/auth.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2025 Stefan Sperling + * Copyright (c) 2025 Omar Polo * * 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)