commit dad80d9b37d085f1908b0c4f6249968feea0b03d from: Omar Polo date: Wed Apr 30 05:29:47 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 - 4ac134683fe90d6fdd19ff29cfaab8f7efd42e94 commit + dad80d9b37d085f1908b0c4f6249968feea0b03d blob - c1c7d625060f9c5984c837f36bb35b671ad96ee9 blob + 5e76053cdcca8f0739e73a698fbcaf1e4a458472 --- gotwebd/auth.c +++ gotwebd/auth.c @@ -36,73 +36,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 +182,91 @@ 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 */ + if (evbuffer_add_printf(out, "ok https://%s/done\n", hostname) == -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 +324,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 +473,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)