commit - 1c454de92a2b6867a9ce9c3dd5a14d8a43edfdef
commit + f9d09706c26867a700fc666e9809f029520f5848
blob - c1c7d625060f9c5984c837f36bb35b671ad96ee9
blob + a8a7c9466451b8e9def914e038d5915e00df8585
--- gotwebd/auth.c
+++ gotwebd/auth.c
/*
* 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
#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)
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;
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);
}
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)