commit - d2734f084af031fafd9d269c6f91b70460772e68
commit + d35e18b31b0e05c6178a6bfa891dd2e2dadf3db1
blob - cc75de3d7d482e933ac57faa09aec2859f9180a8
blob + c33b2d778d3419cdbf24d4f84a62c70d46dd615b
--- certs.c
+++ certs.c
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+/*
+ * The routines to generate a certificate were derived from acme-client.
+ */
+
#include "compat.h"
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <openssl/x509v3.h>
#include "certs.h"
+#include "fs.h"
+#include "iri.h"
+/* client certificate */
+struct ccert {
+ char *line; /* fields below points inside here */
+ char *host;
+ char *port;
+ char *path;
+ char *cert;
+};
+
+static struct cert_store {
+ struct ccert *certs;
+ size_t len;
+ size_t cap;
+} cert_store;
+
+char **identities;
+static size_t id_len, id_cap;
+
/*
* Default number of bits when creating a new RSA key.
*/
#define KBITS 4096
+
+static int
+identities_cmp(const void *a, const void *b)
+{
+ return (strcmp(a, b));
+}
+
+static inline int
+push_identity(char *name)
+{
+ void *t;
+ size_t newcap, i;
+
+ for (i = 0; i < id_len; ++i) {
+ if (!strcmp(identities[i], name))
+ return (0);
+ }
+
+ /* id_cap is initilized to 8 in certs_init() */
+ if (id_len >= id_cap - 1) {
+ newcap = id_cap + 8;
+ t = recallocarray(identities, id_cap, newcap,
+ sizeof(*identities));
+ if (t == NULL)
+ return (-1);
+ identities = t;
+ id_cap = newcap;
+ }
+
+ identities[id_len++] = name;
+ qsort(identities, id_len, sizeof(*identities), identities_cmp);
+
+ return (0);
+}
+
+static int
+certs_cmp(const void *a, const void *b)
+{
+ const struct ccert *ca = a, *cb = b;
+ int r;
+
+ if ((r = strcmp(ca->host, cb->host)) != 0)
+ return (r);
+ if ((r = strcmp(ca->port, cb->port)) != 0)
+ return (r);
+ if ((r = strcmp(ca->path, cb->path)) != 0)
+ return (r);
+ return (strcmp(ca->cert, cb->cert));
+}
+
+static int
+certs_store_add(const char *l)
+{
+ size_t newcap;
+ void *t;
+ char *line, *host, *port, *path, *cert;
+
+ if ((line = strdup(l)) == NULL)
+ return (-1);
+
+ host = line;
+ while (isspace((unsigned char)*host))
+ ++host;
+
+ if (*host == '#') {
+ free(line);
+ return (0);
+ }
+
+ port = host + strcspn(host, " \t");
+ if (*port == '\0')
+ goto err;
+ *port++ = '\0';
+ while (isspace((unsigned char)*port))
+ ++port;
+
+ path = port + strcspn(port, " \t");
+ if (*path == '\0')
+ goto err;
+ *path++ = '\0';
+ while (isspace((unsigned char)*path))
+ ++path;
+
+ cert = path + strcspn(path, " \t");
+ if (*cert == '\0')
+ goto err;
+ *cert++ = '\0';
+ while (isspace((unsigned char)*cert))
+ ++cert;
+
+ if (*cert == '\0')
+ goto err;
+
+ if (cert_store.len == cert_store.cap) {
+ newcap = cert_store.cap + 8;
+ t = reallocarray(cert_store.certs, newcap,
+ sizeof(*cert_store.certs));
+ if (t == NULL)
+ goto err;
+ cert_store.certs = t;
+ cert_store.cap = newcap;
+ }
+ cert_store.certs[cert_store.len].line = line;
+ cert_store.certs[cert_store.len].host = host;
+ cert_store.certs[cert_store.len].port = port;
+ cert_store.certs[cert_store.len].path = path;
+ cert_store.certs[cert_store.len].cert = cert;
+ cert_store.len++;
+
+ return (push_identity(cert));
+
+ err:
+ free(line);
+ return (-1);
+}
+
+int
+certs_init(const char *certfile)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
+
+ id_cap = 8;
+ if ((identities = calloc(id_cap, sizeof(*identities))) == NULL)
+ return (-1);
+
+ if ((fp = fopen(certfile, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+ return (-1);
+ }
+
+ while ((linelen = getline(&line, &linesize, fp)) != -1) {
+ if (line[linelen - 1] == '\n')
+ line[--linelen] = '\0';
+
+ if (certs_store_add(line) == -1) {
+ fclose(fp);
+ free(line);
+ return (-1);
+ }
+ }
+
+ if (ferror(fp)) {
+ fclose(fp);
+ free(line);
+ return (-1);
+ }
+
+ /*
+ * Data should already be in order, so mergesort should be
+ * faster. If it fails (memory scarcity), fall back to qsort()
+ * which is in place.
+ */
+ if (mergesort(cert_store.certs, cert_store.len,
+ sizeof(*cert_store.certs), certs_cmp) == -1)
+ qsort(cert_store.certs, cert_store.len,
+ sizeof(*cert_store.certs), certs_cmp);
+
+ fclose(fp);
+ free(line);
+ return (0);
+}
+
+const char *
+ccert(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < id_len; ++i) {
+ if (!strcmp(name, identities[i]))
+ return (identities[i]);
+ }
+
+ return (NULL);
+}
+
+const char *
+cert_for(struct iri *iri)
+{
+ struct ccert *c;
+ size_t i;
+
+ for (i = 0; i < cert_store.len; ++i) {
+ c = &cert_store.certs[i];
+
+ if (!strcmp(c->host, iri->iri_host) &&
+ !strcmp(c->port, iri->iri_portstr) &&
+ !strncmp(c->path, iri->iri_path, strlen(c->path)))
+ return (c->cert);
+ }
+
+ return (NULL);
+}
+
+int
+cert_open(const char *cert)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ int fd;
+
+ strlcpy(path, cert_dir, sizeof(path));
+ strlcat(path, "/", sizeof(path));
+ strlcat(path, cert, sizeof(path));
+
+ if ((fd = open(path, O_RDONLY)) == -1)
+ return (-1);
+
+ if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
static EVP_PKEY *
rsa_key_create(FILE *f, const char *fname)
{
blob - c50f46afdb218208b3c062887aca4274c3d8fec9
blob + 02ed3f170e25607b1fa02036f749359c51db5996
--- certs.h
+++ certs.h
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-int cert_new(const char *, const char *, const char *, int);
+struct iri;
+
+extern char **identities;
+
+int certs_init(const char *);
+const char *ccert(const char *);
+const char *cert_for(struct iri *);
+int cert_open(const char *);
+int cert_new(const char *, const char *, const char *, int);
blob - 7758bf12a823fbfb255a8158a88bddba18e2b018
blob + ceff70ecc8ba50abc5c4395df2443de8e6bfc829
--- fs.c
+++ fs.c
char crashed_file[PATH_MAX];
char session_file[PATH_MAX], session_file_tmp[PATH_MAX];
char history_file[PATH_MAX], history_file_tmp[PATH_MAX];
+char cert_dir[PATH_MAX];
+char certs_file[PATH_MAX], certs_file_tmp[PATH_MAX];
char cwd[PATH_MAX];
sizeof(history_file));
join_path(history_file_tmp, cache_path_base, "/history.XXXXXXXXXX",
sizeof(history_file_tmp));
+ join_path(cert_dir, data_path_base, "/certs/",
+ sizeof(cert_dir));
+ join_path(certs_file, data_path_base, "/certs/certs",
+ sizeof(certs_file));
+ join_path(certs_file_tmp, data_path_base, "/certs/certs.XXXXXXXXXX",
+ sizeof(certs_file_tmp));
join_path(crashed_file, cache_path_base, "/crashed",
sizeof(crashed_file));
+ mkdirs(cert_dir, S_IRWXU);
+
return 1;
}
blob - c18c4d95757e523512b6cf129160fa9189ceb0cb
blob + 2d642755e7c9fcc3e659e4ff7f6a99dd6dbf3282
--- fs.h
+++ fs.h
/*
- * Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2021, 2022, 2024 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
extern char crashed_file[PATH_MAX];
extern char session_file[PATH_MAX], session_file_tmp[PATH_MAX];
extern char history_file[PATH_MAX], history_file_tmp[PATH_MAX];
+extern char cert_dir[PATH_MAX];
+extern char certs_file[PATH_MAX], paths_file_tmp[PATH_MAX];
extern char cwd[PATH_MAX];
blob - 48bb3e9ad2ce05481bcf18a40d6b024a8f2b7149
blob + 29378bfc1ba17d17a75d8ca398c17d206e6c407c
--- net.c
+++ net.c
#include "compat.h"
+#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <netinet/in.h>
char *port;
char *req;
size_t len;
+ void *ccert;
+ size_t ccert_len;
+ int ccert_fd;
int done_header;
struct bufferevent *bev;
tls_config_insecure_noverifycert(conf);
tls_config_insecure_noverifyname(conf);
+
+ if (req->ccert && tls_config_set_keypair_mem(conf,
+ req->ccert, req->ccert_len, req->ccert, req->ccert_len)
+ == -1) {
+ close_with_errf(req, "failed to load keypair: %s",
+ tls_config_error(conf));
+ tls_config_free(conf);
+ return;
+ }
/* prepare tls */
if ((req->ctx = tls_client()) == NULL) {
close_with_errf(req, "tls_client: %s",
strerror(errno));
+ tls_config_free(conf);
return;
}
if (tls_configure(req->ctx, conf) == -1) {
close_with_errf(req, "tls_configure: %s",
tls_error(req->ctx));
+ tls_config_free(conf);
return;
}
tls_config_free(conf);
tls_free(req->ctx);
req->ctx = NULL;
+ }
+
+ if (req->ccert != NULL) {
+ munmap(req->ccert, req->ccert_len);
+ close(req->ccert_fd);
}
free(req->host);
}
close_with_errf(req, "unknown event error %x", error);
+}
+
+static int
+load_cert(struct imsg *imsg, struct req *req)
+{
+ struct stat sb;
+ int fd;
+
+ if ((fd = imsg_get_fd(imsg)) == -1)
+ return (0);
+
+ if (fstat(fd, &sb) == -1)
+ return (-1);
+
+#if 0
+ if (sb.st_size >= (off_t)SIZE_MAX) {
+ close(fd);
+ return (-1);
+ }
+#endif
+
+ req->ccert = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (req->ccert == MAP_FAILED) {
+ req->ccert = NULL;
+ close(fd);
+ return (-1);
+ }
+
+ req->ccert_len = sb.st_size;
+ req->ccert_fd = fd;
+
+ return (0);
}
static void
if ((req = calloc(1, sizeof(*req))) == NULL)
die();
+ req->ccert_fd = -1;
req->id = imsg_get_id(&imsg);
TAILQ_INSERT_HEAD(&reqhead, req, reqs);
die();
if ((req->req = strdup(r.req)) == NULL)
die();
+ if (load_cert(&imsg, req) == -1)
+ die();
req->len = strlen(req->req);
blob - 503a74eafeedeff94d7961e9fce4d014934399bc
blob + d85f1fb9da735aeff95a101a3e527eba316a0285
--- sandbox.c
+++ sandbox.c
void
sandbox_net_process(void)
{
- if (pledge("stdio inet dns", NULL) == -1)
+ if (pledge("stdio inet dns recvfd", NULL) == -1)
err(1, "pledge");
}
if (unveil(cache_path_base, "rwc") == -1)
err(1, "unveil(%s)", cache_path_base);
- if (pledge("stdio rpath wpath cpath unix tty", NULL) == -1)
+ if (pledge("stdio rpath wpath cpath unix sendfd tty", NULL) == -1)
err(1, "pledge");
}
blob - c17f3b88353b860490d35ebc7d8832291d82c0fb
blob + 55ec2474b63d18f22c3f76662ac98092bef60623
--- session.c
+++ session.c
void
stop_tab(struct tab *tab)
{
- ui_send_net(IMSG_STOP, tab->id, NULL, 0);
+ ui_send_net(IMSG_STOP, tab->id, -1, NULL, 0);
}
static inline void
blob - d90ada56333478b10894e8e96d5344f09128009a
blob + c50a70155a36fb4151dc2ecd9f8e6e49345412d1
--- telescope.c
+++ telescope.c
#include <string.h>
#include <unistd.h>
+#include "certs.h"
#include "control.h"
#include "defaults.h"
#include "fs.h"
static void load_via_proxy(struct tab *, const char *,
struct proxy *);
static void make_request(struct tab *, struct get_req *, int,
- const char *);
+ const char *, int);
static void do_load_url(struct tab *, const char *, const char *, int);
static pid_t start_child(enum telescope_process, const char *, int);
static void send_url(const char *);
else
tab->trust = TS_TRUSTED;
- ui_send_net(IMSG_CERT_STATUS, imsg->hdr.peerid,
+ ui_send_net(IMSG_CERT_STATUS, imsg->hdr.peerid, -1,
&tofu_res, sizeof(tofu_res));
} else {
tab->trust = TS_UNTRUSTED;
static void
handle_check_cert_user_choice(int accept, struct tab *tab)
{
- ui_send_net(IMSG_CERT_STATUS, tab->id, &accept,
+ ui_send_net(IMSG_CERT_STATUS, tab->id, -1, &accept,
sizeof(accept));
if (accept) {
} else if (tab->code == 20) {
history_add(hist_cur(tab->hist));
if (setup_parser_for(tab)) {
- ui_send_net(IMSG_PROCEED, tab->id, NULL, 0);
+ ui_send_net(IMSG_PROCEED, tab->id, -1, NULL, 0);
} else if (safe_mode) {
load_page_from_str(tab,
err_pages[UNKNOWN_TYPE_OR_CSET]);
ui_show_downloads_pane();
d = enqueue_download(tab->id, path);
d->fd = fd;
- ui_send_net(IMSG_PROCEED, d->id, NULL, 0);
+ ui_send_net(IMSG_PROCEED, d->id, -1, NULL, 0);
/*
* Change this tab id, the old one is associated with the
strlcat(req.req, "\r\n", sizeof(req.req));
parser_init(tab, textplain_initparser);
- make_request(tab, &req, PROTO_FINGER, NULL);
+ make_request(tab, &req, PROTO_FINGER, NULL, 0);
}
static void
load_gemini_url(struct tab *tab, const char *url)
{
struct get_req req;
+ int use_cert = 0;
+ if ((tab->client_cert = cert_for(&tab->iri)) != NULL)
+ use_cert = 1;
+
memset(&req, 0, sizeof(req));
strlcpy(req.host, tab->iri.iri_host, sizeof(req.host));
strlcpy(req.port, tab->iri.iri_portstr, sizeof(req.port));
- make_request(tab, &req, PROTO_GEMINI, hist_cur(tab->hist));
+ make_request(tab, &req, PROTO_GEMINI, hist_cur(tab->hist), use_cert);
}
static inline const char *
}
strlcat(req.req, "\r\n", sizeof(req.req));
- make_request(tab, &req, PROTO_GOPHER, NULL);
+ make_request(tab, &req, PROTO_GOPHER, NULL, 0);
}
static void
tab->proxy = p;
- make_request(tab, &req, p->proto, hist_cur(tab->hist));
+ make_request(tab, &req, p->proto, hist_cur(tab->hist), 0);
}
static void
-make_request(struct tab *tab, struct get_req *req, int proto, const char *r)
+make_request(struct tab *tab, struct get_req *req, int proto, const char *r,
+ int use_cert)
{
+ int fd = -1;
+
stop_tab(tab);
tab->id = tab_new_id();
req->proto = proto;
}
start_loading_anim(tab);
- ui_send_net(IMSG_GET, tab->id, req, sizeof(*req));
+
+ if (!use_cert)
+ tab->client_cert = NULL;
+ if (use_cert && (fd = cert_open(tab->client_cert)) == -1) {
+ tab->client_cert = NULL;
+ message("failed to open certificate: %s", strerror(errno));
+ }
+
+ ui_send_net(IMSG_GET, tab->id, fd, req, sizeof(*req));
}
void
erase_buffer(&tab->buffer);
parser_init(tab, gophermap_initparser);
- make_request(tab, &req, PROTO_GOPHER, NULL);
+ make_request(tab, &req, PROTO_GOPHER, NULL, 0);
}
void
}
int
-ui_send_net(int type, uint32_t peerid, const void *data,
+ui_send_net(int type, uint32_t peerid, int fd, const void *data,
uint16_t datalen)
{
- return imsg_compose_event(iev_net, type, peerid, 0, -1, data,
+ return imsg_compose_event(iev_net, type, peerid, 0, fd, data,
datalen);
}
TAILQ_INIT(&minibuffer_map.m);
fs_init();
+ certs_init(certs_file);
config_init();
parseconfig(config_path, fail);
if (configtest) {
ui_end();
}
- ui_send_net(IMSG_QUIT, 0, NULL, 0);
+ ui_send_net(IMSG_QUIT, 0, -1, NULL, 0);
imsg_flush(&iev_net->ibuf);
/* wait for children to terminate */
blob - c077f15294e3d9cac921621ca5d65ee35d53d4e3
blob + 471779df8f10998d8404cc3f546f80621e82ddd9
--- telescope.h
+++ telescope.h
char *cert;
enum trust_state trust;
+ const char *client_cert;
struct proxy *proxy;
struct iri iri;
struct hist *hist;
void write_buffer(const char *, struct tab *);
void humanify_url(const char *, const char *, char *, size_t);
int bookmark_page(const char *);
-int ui_send_net(int, uint32_t, const void *, uint16_t);
+int ui_send_net(int, uint32_t, int, const void *, uint16_t);
/* tofu.c */
void tofu_init(struct ohash*, unsigned int, ptrdiff_t);
blob - 2c16045881aaa24551c4373ab324470e474c29c0
blob + 55e20314797f9773fe0430a7448c59bdf1374329
--- ui.c
+++ ui.c
/*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
wattr_on(modeline, modeline_face.background, NULL);
wmove(modeline, 0, 0);
- wprintw(modeline, "-%c%c- %s ",
+ wprintw(modeline, "-%c%c%c %s ",
spin[tab->loading_anim_step],
trust_status_char(tab->trust),
+ tab->client_cert ? 'C' : '-',
mode == NULL ? "(none)" : mode);
pct = (buffer->line_off + buffer->curs_y) * 100.0