commit 288fd238ab2466e7fc9d8d11e7dd92363326a66f from: Omar Polo date: Sun Apr 25 19:22:42 2021 UTC allow saving the new certificate after mismatch commit - fef9de8ff7030ff5eecba8cbb6a6def8b5bce9c8 commit + 288fd238ab2466e7fc9d8d11e7dd92363326a66f blob - f1d91bbbe7efedd85f36c3a3127776239806b287 blob + 82e0f91bff2cf8e134c77bcea58c00500c6e79e6 --- ChangeLog +++ ChangeLog @@ -1,5 +1,7 @@ 2021-04-25 Omar Polo + * telescope.c (handle_check_cert_user_choice): allow saving the new certificate after mismatch + * fs.c (load_certs): don't crash on invalid lines in known_hosts * hash.c (telescope_lookup_tofu): save certificates per (host, port) tuple, not only per-host blob - 3959ee825b9e66fc3f65c56fdece058323e8080d blob + 2b96509fdd0337da41b701583d79bb641267d74e --- fs.c +++ fs.c @@ -37,6 +37,7 @@ static void handle_get(struct imsg*, size_t); static void handle_quit(struct imsg*, size_t); static void handle_bookmark_page(struct imsg*, size_t); static void handle_save_cert(struct imsg*, size_t); +static void handle_update_cert(struct imsg*, size_t); static void handle_session_start(struct imsg*, size_t); static void handle_session_tab(struct imsg*, size_t); static void handle_session_end(struct imsg*, size_t); @@ -48,7 +49,7 @@ static struct imsgbuf *ibuf; static FILE *session; static char bookmark_file[PATH_MAX]; -static char known_hosts_file[PATH_MAX]; +static char known_hosts_file[PATH_MAX], known_hosts_tmp[PATH_MAX]; static char session_file[PATH_MAX]; static imsg_handlerfn *handlers[] = { @@ -56,6 +57,7 @@ static imsg_handlerfn *handlers[] = { [IMSG_QUIT] = handle_quit, [IMSG_BOOKMARK_PAGE] = handle_bookmark_page, [IMSG_SAVE_CERT] = handle_save_cert, + [IMSG_UPDATE_CERT] = handle_update_cert, [IMSG_SESSION_START] = handle_session_start, [IMSG_SESSION_TAB] = handle_session_tab, [IMSG_SESSION_END] = handle_session_end, @@ -178,6 +180,68 @@ handle_save_cert(struct imsg *imsg, size_t datalen) res = 0; end: imsg_compose(ibuf, IMSG_SAVE_CERT_OK, imsg->hdr.peerid, 0, -1, + &res, sizeof(res)); + imsg_flush(ibuf); +} + +static void +handle_update_cert(struct imsg *imsg, size_t datalen) +{ + FILE *tmp, *f; + struct tofu_entry entry; + char sfn[PATH_MAX], *line = NULL, *t; + size_t l, linesize = 0; + ssize_t linelen; + int fd, e, res = 0; + + if (datalen != sizeof(entry)) + die(); + memcpy(&entry, imsg->data, datalen); + + strlcpy(sfn, known_hosts_tmp, sizeof(sfn)); + if ((fd = mkstemp(sfn)) == -1 || + (tmp = fdopen(fd, "w")) == NULL) { + if (fd != -1) { + unlink(sfn); + close(fd); + } + res = 0; + goto end; + } + + if ((f = fopen(known_hosts_file, "r")) == NULL) { + unlink(sfn); + fclose(tmp); + res = 0; + goto end; + } + + l = strlen(entry.domain); + while ((linelen = getline(&line, &linesize, f)) != -1) { + if ((t = strstr(line, entry.domain)) != NULL && + (line[l] == ' ' || line[l] == '\t')) + continue; + /* line has a trailing \n */ + fprintf(tmp, "%s", line); + } + fprintf(tmp, "%s %s %d\n", entry.domain, entry.hash, entry.verified); + + free(line); + e = ferror(tmp); + + fclose(tmp); + fclose(f); + + if (e) { + unlink(sfn); + res = 0; + goto end; + } + + res = rename(sfn, known_hosts_file) != -1; + +end: + imsg_compose(ibuf, IMSG_UPDATE_CERT_OK, imsg->hdr.peerid, 0, -1, &res, sizeof(res)); imsg_flush(ibuf); } @@ -238,6 +302,10 @@ fs_init(void) strlcpy(known_hosts_file, getenv("HOME"), sizeof(known_hosts_file)); strlcat(known_hosts_file, "/.telescope/known_hosts", sizeof(known_hosts_file)); + strlcpy(known_hosts_tmp, getenv("HOME"), sizeof(known_hosts_tmp)); + strlcat(known_hosts_tmp, "/.telescope/known_hosts.tmp.XXXXXXXXXX", + sizeof(known_hosts_file)); + strlcpy(session_file, getenv("HOME"), sizeof(session_file)); strlcat(session_file, "/.telescope/session", sizeof(session_file)); blob - 1dc54d50a6a4ae71a414eb217d9d8799d0b34fde blob + 01b9fe34f05109df256fc7d27e1a1b48d74f00bb --- telescope.c +++ telescope.c @@ -26,12 +26,14 @@ static struct tab *tab_by_id(uint32_t); static void handle_imsg_err(struct imsg*, size_t); static void handle_imsg_check_cert(struct imsg*, size_t); static void handle_check_cert_user_choice(int, unsigned int); +static void handle_maybe_save_new_cert(int, unsigned int); static void handle_imsg_got_code(struct imsg*, size_t); static void handle_imsg_got_meta(struct imsg*, size_t); static void handle_imsg_buf(struct imsg*, size_t); static void handle_imsg_eof(struct imsg*, size_t); static void handle_imsg_bookmark_ok(struct imsg*, size_t); static void handle_imsg_save_cert_ok(struct imsg*, size_t); +static void handle_imsg_update_cert_ok(struct imsg *, size_t); static void handle_dispatch_imsg(int, short, void*); static void load_page_from_str(struct tab*, const char*); static void do_load_url(struct tab*, const char*); @@ -45,6 +47,7 @@ static imsg_handlerfn *handlers[] = { [IMSG_EOF] = handle_imsg_eof, [IMSG_BOOKMARK_OK] = handle_imsg_bookmark_ok, [IMSG_SAVE_CERT_OK] = handle_imsg_save_cert_ok, + [IMSG_UPDATE_CERT_OK] = handle_imsg_update_cert_ok, }; static struct ohash certs; @@ -131,6 +134,8 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal } else { tab->trust = TS_UNTRUSTED; load_page_from_str(tab, "# Certificate mismatch\n"); + if ((tab->cert = strdup(hash)) == NULL) + die(); ui_yornp("Certificate mismatch. Proceed?", handle_check_cert_user_choice, tab->id); } @@ -139,11 +144,55 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal static void handle_check_cert_user_choice(int accept, unsigned int tabid) { + struct tab *tab; + + tab = tab_by_id(tabid); + imsg_compose(netibuf, IMSG_CERT_STATUS, tabid, 0, -1, &accept, sizeof(accept)); imsg_flush(netibuf); + + if (accept) + ui_yornp("Save the new certificate?", + handle_maybe_save_new_cert, tabid); + else { + free(tab->cert); + tab->cert = NULL; + } } +static void +handle_maybe_save_new_cert(int accept, unsigned int tabid) +{ + struct tab *tab; + struct tofu_entry *e; + + tab = tab_by_id(tabid); + + if (!accept) + goto end; + + if ((e = calloc(1, sizeof(e))) == NULL) + die(); + + strlcpy(e->domain, tab->uri.host, sizeof(e->domain)); + if (*tab->uri.port != '\0' && strcmp(tab->uri.port, "1965")) { + strlcat(e->domain, ":", sizeof(e->domain)); + strlcat(e->domain, tab->uri.port, sizeof(e->domain)); + } + strlcpy(e->hash, tab->cert, sizeof(e->hash)); + imsg_compose(fsibuf, IMSG_UPDATE_CERT, 0, 0, -1, e, sizeof(*e)); + imsg_flush(fsibuf); + + tofu_update(&certs, e); + + tab->trust = TS_TRUSTED; + +end: + free(tab->cert); + tab->cert = NULL; +} + static inline int normalize_code(int n) { @@ -280,6 +329,18 @@ handle_imsg_save_cert_ok(struct imsg *imsg, size_t dat if (res != 0) ui_notify("Failed to save the cert for: %s", strerror(res)); +} + +static void +handle_imsg_update_cert_ok(struct imsg *imsg, size_t datalen) +{ + int res; + + if (datalen != sizeof(res)) + die(); + memcpy(&res, imsg->data, datalen); + if (!res) + ui_notify("Failed to update the certificate"); } static void blob - d972e44d1820f75c484a8f39a511b7e3e3997bf0 blob + 3a5b568ee0673fb93a01be0ae82ff2ec4f6b75af --- telescope.h +++ telescope.h @@ -46,6 +46,8 @@ enum imsg_type { IMSG_BOOKMARK_OK, IMSG_SAVE_CERT, IMSG_SAVE_CERT_OK, + IMSG_UPDATE_CERT, + IMSG_UPDATE_CERT_OK, IMSG_SESSION_START, IMSG_SESSION_TAB, @@ -148,6 +150,7 @@ struct tab { uint32_t id; uint32_t flags; + char *cert; enum trust_state trust; struct phos_uri uri; struct histhead hist; @@ -249,6 +252,7 @@ void textplain_initparser(struct parser*); void tofu_init(struct ohash*, unsigned int, ptrdiff_t); struct tofu_entry *tofu_lookup(struct ohash*, const char*, const char*); void tofu_add(struct ohash*, struct tofu_entry*); +void tofu_update(struct ohash*, struct tofu_entry*); /* ui.c */ unsigned int tab_new_id(void); blob - 1814b4ea5d3302c0cbd2f818d003bc8c72d64710 blob + b474a99d2639bb8390a3b0ec64e782783a93d344 --- tofu.c +++ tofu.c @@ -82,3 +82,17 @@ tofu_add(struct ohash *h, struct tofu_entry *e) slot = ohash_qlookup(h, e->domain); ohash_insert(h, slot, e); } + +void +tofu_update(struct ohash *h, struct tofu_entry *e) +{ + struct tofu_entry *t; + + if ((t = tofu_lookup(h, e->domain, NULL)) == NULL) + tofu_add(h, e); + else { + strlcpy(t->hash, e->hash, sizeof(t->hash)); + t->verified = e->verified; + free(e); + } +}