diff options
author | Josh Rickmar <jrick@devio.us> | 2012-06-07 12:54:25 -0400 |
---|---|---|
committer | Josh Rickmar <jrick@devio.us> | 2012-06-07 12:54:25 -0400 |
commit | 5d21e3a3954e947f889f024b8ba9db9e616aa28e (patch) | |
tree | 7e44dd9fae763b256db81361d278ad2830c1bffa /xombrero.c | |
parent | 07616810c81eb3e7073c9ca00e28f451c7bfb091 (diff) | |
download | xombrero-5d21e3a3954e947f889f024b8ba9db9e616aa28e.tar.gz |
Implement a warn_cert_changes setting to warn users when the remote
ssl certificate is different from a previously cached certificate to help prevent against MITM attacks. Prompt the user with an action to take (show remote cert, allow for that session, or cache the new remote cert).
Diffstat (limited to 'xombrero.c')
-rw-r--r-- | xombrero.c | 161 |
1 files changed, 121 insertions, 40 deletions
diff --git a/xombrero.c b/xombrero.c index 20894e8..cb4a4ac 100644 --- a/xombrero.c +++ b/xombrero.c @@ -85,6 +85,7 @@ TAILQ_HEAD(command_list, command_entry); #define XT_DIR (".xombrero") #define XT_CACHE_DIR ("cache") #define XT_CERT_DIR ("certs") +#define XT_CERT_CACHE_DIR ("certs_cache") #define XT_JS_DIR ("js") #define XT_SESSIONS_DIR ("sessions") #define XT_TEMP_DIR ("tmp") @@ -222,6 +223,8 @@ struct command_list chl; struct command_list shl; struct command_entry *history_at; struct command_entry *search_at; +struct secviolation_list svl; +struct sv_ignore_list svil; int undo_count; int cmd_history_count = 0; int search_history_count = 0; @@ -655,6 +658,7 @@ show_oops(struct tab *at, const char *fmt, ...) char work_dir[PATH_MAX]; char certs_dir[PATH_MAX]; +char certs_cache_dir[PATH_MAX]; char js_dir[PATH_MAX]; char cache_dir[PATH_MAX]; char sessions_dir[PATH_MAX]; @@ -697,6 +701,20 @@ download_rb_cmp(struct download *e1, struct download *e2) } RB_GENERATE(download_list, download, entry, download_rb_cmp); +int +secviolation_rb_cmp(struct secviolation *s1, struct secviolation *s2) +{ + return (s1->xtp_arg < s2->xtp_arg ? -1 : s1->xtp_arg > s2->xtp_arg); +} +RB_GENERATE(secviolation_list, secviolation, entry, secviolation_rb_cmp); + +int +sv_ignore_rb_cmp(struct sv_ignore *s1, struct sv_ignore *s2) +{ + return (strcmp(s1->domain, s2->domain)); +} +RB_GENERATE(sv_ignore_list, sv_ignore, entry, sv_ignore_rb_cmp); + struct valid_url_types { char *type; } vut[] = { @@ -704,6 +722,7 @@ struct valid_url_types { { "https://" }, { "ftp://" }, { "file://" }, + { XT_URI_ABOUT }, { XT_XTP_STR }, }; @@ -835,7 +854,8 @@ load_uri(struct tab *t, gchar *uri) if (!strncmp(uri, XT_URI_ABOUT, XT_URI_ABOUT_LEN)) { for (i = 0; i < about_list_size(); i++) - if (!strcmp(&uri[XT_URI_ABOUT_LEN], about_list[i].name)) { + if (!strcmp(&uri[XT_URI_ABOUT_LEN], about_list[i].name) && + about_list[i].func != NULL) { bzero(&args, sizeof args); about_list[i].func(t, &args); gtk_widget_set_sensitive(GTK_WIDGET(t->stop), @@ -1778,18 +1798,17 @@ statusbar_modify_attr(struct tab *t, const char *text, const char *base) void save_certs(struct tab *t, gnutls_x509_crt_t *certs, - size_t cert_count, char *domain) + size_t cert_count, const char *domain, const char *dir) { size_t cert_buf_sz; char cert_buf[64 * 1024], file[PATH_MAX]; int i; FILE *f; - GdkColor color; if (t == NULL || certs == NULL || cert_count <= 0 || domain == NULL) return; - snprintf(file, sizeof file, "%s" PS "%s", certs_dir, domain); + snprintf(file, sizeof file, "%s" PS "%s", dir, domain); if ((f = fopen(file, "w")) == NULL) { show_oops(t, "Can't create cert file %s %s", file, strerror(errno)); @@ -1809,10 +1828,6 @@ save_certs(struct tab *t, gnutls_x509_crt_t *certs, } } - /* not the best spot but oh well */ - gdk_color_parse(XT_COLOR_BLUE, &color); - gtk_widget_modify_base(t->uri_entry, GTK_STATE_NORMAL, &color); - statusbar_modify_attr(t, XT_COLOR_BLACK, XT_COLOR_BLUE); done: fclose(f); } @@ -1825,7 +1840,7 @@ enum cert_trust { }; enum cert_trust -load_compare_cert(const gchar *uri, const gchar **error_str) +load_compare_cert(const gchar *uri, const gchar **error_str, const char *dir) { char domain[8182], file[PATH_MAX]; char cert_buf[64 * 1024], r_cert_buf[64 * 1024]; @@ -1867,7 +1882,7 @@ load_compare_cert(const gchar *uri, const gchar **error_str) goto done; } - snprintf(file, sizeof file, "%s" PS "%s", certs_dir, domain); + snprintf(file, sizeof file, "%s" PS "%s", dir, domain); if ((f = fopen(file, "r")) == NULL) { if (!error) rv = CERT_TRUSTED; @@ -1880,7 +1895,7 @@ load_compare_cert(const gchar *uri, const gchar **error_str) cert_buf, &cert_buf_sz)) { goto freeit; } - if (fread(r_cert_buf, cert_buf_sz, 1, f) != 1) { + if (fread(r_cert_buf, cert_buf_sz, 1, f) != 1 && !feof(f)) { rv = CERT_BAD; /* critical */ goto freeit; } @@ -1901,7 +1916,7 @@ done: close(s); /* only complain if we didn't save it locally */ - if (error && rv != CERT_LOCAL) { + if (strlen(ssl_ca_file) != 0 && error && rv != CERT_LOCAL) { strlcpy(serr, "Certificate exception(s): ", sizeof serr); if (error & GNUTLS_CERT_INVALID) strlcat(serr, "invalid, ", sizeof serr); @@ -1935,7 +1950,6 @@ done: int cert_cmd(struct tab *t, struct karg *args) { - struct stat sb; const gchar *uri, *error_str = NULL; char domain[8182]; int s = -1; @@ -1943,16 +1957,14 @@ cert_cmd(struct tab *t, struct karg *args) gnutls_session_t gsession; gnutls_x509_crt_t *certs; gnutls_certificate_credentials_t xcred; + GdkColor color; if (t == NULL) return (1); - if (stat(ssl_ca_file, &sb)) { - show_oops(t, "Can't open CA file: %s", ssl_ca_file); - return (1); - } - - if ((uri = get_uri(t)) == NULL) { + if (args->s != NULL) + uri = args->s; + else if ((uri = get_uri(t)) == NULL) { show_oops(t, "Invalid URI"); return (1); } @@ -1975,8 +1987,13 @@ cert_cmd(struct tab *t, struct karg *args) if (args->i & XT_SHOW) show_certs(t, certs, cert_count, "Certificate Chain"); - else if (args->i & XT_SAVE) - save_certs(t, certs, cert_count, domain); + else if (args->i & XT_SAVE) { + save_certs(t, certs, cert_count, domain, certs_dir); + gdk_color_parse(XT_COLOR_BLUE, &color); + gtk_widget_modify_base(t->uri_entry, GTK_STATE_NORMAL, &color); + statusbar_modify_attr(t, XT_COLOR_BLACK, XT_COLOR_BLUE); + } else if (args->i & XT_CACHE) + save_certs(t, certs, cert_count, domain, certs_cache_dir); free_connection_certs(certs, cert_count); done: @@ -1989,6 +2006,65 @@ done: return (0); } +/* + * args must be allocated dynamically as the thread that added this function + * to the idle loop no longer exists + */ +gboolean +warn_cert_cache_differs_idle(struct karg *args) +{ + if (args == NULL) { + show_oops(NULL, "%s: invalid parameters", __func__); + /* return 0 to not re-add function to the idle loop */ + return (0); + } + xtp_page_sv((struct tab *)args->ptr, args); + free(args); + return (0); +} + +int +check_cert_changes(struct tab *t, const char *uri) +{ + SoupURI *soupuri = NULL; + struct karg args = {0}; + struct sv_ignore find; + const char *errstr = NULL; + struct karg *argsp; + + if (!(warn_cert_changes && g_str_has_prefix(uri, "https://"))) + return (0); + + switch (load_compare_cert(uri, &errstr, certs_cache_dir)) { + case CERT_LOCAL: + /* The cached certificate is identical */ + break; + case CERT_TRUSTED: /* FALLTHROUGH */ + case CERT_UNTRUSTED: + /* cache new certificate */ + args.i = XT_CACHE; + cert_cmd(t, &args); + break; + case CERT_BAD: + if ((soupuri = soup_uri_new(uri)) == NULL) + break; + find.domain = soupuri->host; + if (RB_FIND(sv_ignore_list, &svil, &find)) + break; + t->xtp_meaning = XT_XTP_TAB_MEANING_SV; + argsp = malloc(sizeof(struct karg)); + bzero(argsp, sizeof(struct karg)); + argsp->s = g_strdup((char *)uri); + argsp->ptr = (void *)t; + g_idle_add((GSourceFunc)warn_cert_cache_differs_idle, argsp); + break; + } + + if (soupuri) + soup_uri_free(soupuri); + return (0); +} + int remove_cookie(int index) { @@ -2704,7 +2780,7 @@ command(struct tab *t, struct karg *args) sp = NULL; } s = sl; - for (i = 0; i < sizeof subs / sizeof (struct prompt_sub); ++i) { + for (i = 0; i < LENGTH(subs); ++i) { sv = g_strsplit(sl, subs[i].s, -1); if (sl) g_free(sl); @@ -3537,15 +3613,17 @@ color_address_bar(gpointer p) #endif col_str = XT_COLOR_YELLOW; - switch (load_compare_cert(u, &error_str)) { + switch (load_compare_cert(u, &error_str, certs_dir)) { case CERT_LOCAL: col_str = XT_COLOR_BLUE; break; case CERT_TRUSTED: - col_str = XT_COLOR_GREEN; + col_str = (strlen(ssl_ca_file) == 0) ? XT_COLOR_RED + : XT_COLOR_GREEN; break; case CERT_UNTRUSTED: - col_str = XT_COLOR_YELLOW; + col_str = (strlen(ssl_ca_file) == 0) ? XT_COLOR_RED + : XT_COLOR_YELLOW; break; case CERT_BAD: col_str = XT_COLOR_RED; @@ -3610,10 +3688,16 @@ done: } void +check_certs(gpointer p) +{ + color_address_bar((struct tab *)p); + check_cert_changes((struct tab *)p, get_uri((struct tab *)p)); +} + +void show_ca_status(struct tab *t, const char *uri) { GdkColor color; - struct stat sb; gchar *col_str = XT_COLOR_WHITE, *text, *base; DNPRINTF(XT_D_URL, "show_ca_status: %d %s %s\n", @@ -3622,20 +3706,10 @@ show_ca_status(struct tab *t, const char *uri) if (t == NULL) return; - if (uri == NULL) - goto done; - if (stat(ssl_ca_file, &sb)) { - if (g_str_has_prefix(uri, "http://")) - goto done; - if (g_str_has_prefix(uri, "https://")) { - col_str = XT_COLOR_RED; - goto done; - } - return; - } - if (g_str_has_prefix(uri, "http://") || + if (uri == NULL || g_str_has_prefix(uri, "http://") || !g_str_has_prefix(uri, "https://")) goto done; + #ifdef USE_THREADS /* * It is not necessary to see if the thread is already running. @@ -3644,9 +3718,9 @@ show_ca_status(struct tab *t, const char *uri) */ /* thread the coloring of the address bar */ - t->thread = g_thread_create((GThreadFunc)color_address_bar, t, TRUE, NULL); + t->thread = g_thread_create((GThreadFunc)check_certs, t, TRUE, NULL); #else - color_address_bar(t); + check_certs(t); #endif return; @@ -7834,6 +7908,8 @@ main(int argc, char **argv) RB_INIT(&pl_wl); RB_INIT(&downloads); RB_INIT(&st_tree); + RB_INIT(&svl); + RB_INIT(&svil); TAILQ_INIT(&sessions); TAILQ_INIT(&tabs); @@ -7981,6 +8057,11 @@ main(int argc, char **argv) snprintf(certs_dir, sizeof certs_dir, "%s" PS "%s", work_dir, XT_CERT_DIR); xxx_dir(certs_dir); + /* cert changes dir */ + snprintf(certs_cache_dir, sizeof certs_cache_dir, "%s" PS "%s", + work_dir, XT_CERT_CACHE_DIR); + xxx_dir(certs_cache_dir); + /* sessions dir */ snprintf(sessions_dir, sizeof sessions_dir, "%s" PS "%s", work_dir, XT_SESSIONS_DIR); |