/* * Copyright 2009 Paul Blokus * Copyright 2013 Michael Drake * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** \file * SSL Certificate verification UI (implementation) */ #include #include #include "content/fetch.h" #include "content/urldb.h" #include "content/hlcache.h" #include "desktop/sslcert_viewer.h" #include "desktop/treeview.h" #include "utils/messages.h" #include "utils/log.h" #include "utils/utils.h" enum sslcert_viewer_field { SSLCERT_V_SUBJECT, SSLCERT_V_SERIAL, SSLCERT_V_TYPE, SSLCERT_V_VALID_UNTIL, SSLCERT_V_VALID_FROM, SSLCERT_V_VERSION, SSLCERT_V_ISSUER, SSLCERT_V_CERTIFICATES, SSLCERT_V_N_FIELDS }; /** ssl certificate verification context. */ struct sslcert_session_data { struct ssl_cert_info *certs; /**< Certificates */ unsigned long num; /**< Number of certificates in chain */ nsurl *url; /**< The url of the certificate */ llcache_query_response cb; /**< Cert accept/reject callback */ void *cbpw; /**< Context passed to callback */ treeview *tree; /**< The treeview object */ struct treeview_field_desc fields[SSLCERT_V_N_FIELDS]; }; struct sslcert_entry { treeview_node *entry; char version[24]; char serial[24]; char type[24]; struct treeview_field_data data[SSLCERT_V_N_FIELDS - 1]; }; /** * Free a sll certificate viewer entry's treeview field data. * * \param e Entry to free data from */ static void sslcert_viewer_free_treeview_field_data( struct sslcert_entry *e) { } /** * Build a sslcert viewer treeview field from given text * * \param field SSL certificate treeview field to build * \param data SSL certificate entry field data to set * \param value Text to set in field, ownership yielded * \param ssl_d SSL certificate session data * \return NSERROR_OK on success, appropriate error otherwise */ static inline nserror sslcert_viewer_field_builder( enum sslcert_viewer_field field, struct treeview_field_data *data, const char *value, struct sslcert_session_data *ssl_d) { data->field = ssl_d->fields[field].field; data->value = value; data->value_len = (value != NULL) ? strlen(value) : 0; return NSERROR_OK; } /** * Set a sslcert viewer entry's data from the certificate. * * \param e Entry to set up * \param cert Data associated with entry's certificate * \param ssl_d SSL certificate session data * \return NSERROR_OK on success, appropriate error otherwise */ static nserror sslcert_viewer_set_treeview_field_data( struct sslcert_entry *e, const struct ssl_cert_info *cert, struct sslcert_session_data *ssl_d) { unsigned int written; assert(e != NULL); assert(cert != NULL); assert(ssl_d != NULL); /* Set the fields up */ sslcert_viewer_field_builder(SSLCERT_V_SUBJECT, &e->data[SSLCERT_V_SUBJECT], cert->subject, ssl_d); written = snprintf(e->serial, sizeof(e->serial), "%li", cert->serial); assert(written < sizeof(e->serial)); sslcert_viewer_field_builder(SSLCERT_V_SERIAL, &e->data[SSLCERT_V_SERIAL], e->serial, ssl_d); written = snprintf(e->type, sizeof(e->type), "%i", cert->cert_type); assert(written < sizeof(e->type)); sslcert_viewer_field_builder(SSLCERT_V_TYPE, &e->data[SSLCERT_V_TYPE], e->type, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_VALID_UNTIL, &e->data[SSLCERT_V_VALID_UNTIL], cert->not_after, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_VALID_FROM, &e->data[SSLCERT_V_VALID_FROM], cert->not_before, ssl_d); written = snprintf(e->version, sizeof(e->version), "%li", cert->version); assert(written < sizeof(e->version)); sslcert_viewer_field_builder(SSLCERT_V_VERSION, &e->data[SSLCERT_V_VERSION], e->version, ssl_d); sslcert_viewer_field_builder(SSLCERT_V_ISSUER, &e->data[SSLCERT_V_ISSUER], cert->issuer, ssl_d); return NSERROR_OK; } /** * Create a treeview node for a certificate * * \param ssl_d SSL certificate session data * \param n Number of SSL certificate in chain, to make node for * \return true on success, false on memory exhaustion */ static nserror sslcert_viewer_create_node( struct sslcert_session_data *ssl_d, int n) { struct sslcert_entry *e; const struct ssl_cert_info *cert = &(ssl_d->certs[n]); nserror err; /* Create new certificate viewer entry */ e = malloc(sizeof(struct sslcert_entry)); if (e == NULL) { return NSERROR_NOMEM; } err = sslcert_viewer_set_treeview_field_data(e, cert, ssl_d); if (err != NSERROR_OK) { free(e); return err; } /* Create the new treeview node */ err = treeview_create_node_entry(ssl_d->tree, &(e->entry), NULL, TREE_REL_FIRST_CHILD, e->data, e, TREE_OPTION_NONE); if (err != NSERROR_OK) { sslcert_viewer_free_treeview_field_data(e); free(e); return err; } return NSERROR_OK; } /** * Initialise the treeview entry feilds * * \param ssl_d SSL certificate session data * \return true on success, false on memory exhaustion */ static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) { int i; const char *label; for (i = 0; i < SSLCERT_V_N_FIELDS; i++) ssl_d->fields[i].field = NULL; ssl_d->fields[SSLCERT_V_SUBJECT].flags = TREE_FLAG_DEFAULT; label = "TreeviewLabelSubject"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_SUBJECT].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_SERIAL].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelSerial"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_SERIAL].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_TYPE].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelType"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_TYPE].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_VALID_UNTIL].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelValidUntil"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_VALID_UNTIL].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_VALID_FROM].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelValidFrom"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_VALID_FROM].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_VERSION].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelVersion"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_VERSION].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_ISSUER].flags = TREE_FLAG_SHOW_NAME; label = "TreeviewLabelIssuer"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_ISSUER].field) != lwc_error_ok) { goto error; } ssl_d->fields[SSLCERT_V_CERTIFICATES].flags = TREE_FLAG_DEFAULT; label = "TreeviewLabelCertificates"; label = messages_get(label); if (lwc_intern_string(label, strlen(label), &ssl_d->fields[SSLCERT_V_CERTIFICATES].field) != lwc_error_ok) { return false; } return NSERROR_OK; error: for (i = 0; i < SSLCERT_V_N_FIELDS; i++) if (ssl_d->fields[i].field != NULL) lwc_string_unref(ssl_d->fields[i].field); return NSERROR_UNKNOWN; } /** * Delete ssl certificate viewer entries * * \param e Entry to delete. */ static void sslcert_viewer_delete_entry(struct sslcert_entry *e) { sslcert_viewer_free_treeview_field_data(e); free(e); } static nserror sslcert_viewer_tree_node_folder_cb( struct treeview_node_msg msg, void *data) { switch (msg.msg) { case TREE_MSG_NODE_DELETE: case TREE_MSG_NODE_EDIT: case TREE_MSG_NODE_LAUNCH: break; } return NSERROR_OK; } static nserror sslcert_viewer_tree_node_entry_cb( struct treeview_node_msg msg, void *data) { struct sslcert_entry *e = data; switch (msg.msg) { case TREE_MSG_NODE_DELETE: e->entry = NULL; sslcert_viewer_delete_entry(e); break; case TREE_MSG_NODE_EDIT: case TREE_MSG_NODE_LAUNCH: break; } return NSERROR_OK; } struct treeview_callback_table sslv_tree_cb_t = { .folder = sslcert_viewer_tree_node_folder_cb, .entry = sslcert_viewer_tree_node_entry_cb }; /* Exported interface, documented in sslcert_viewer.h */ nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, void *core_window_handle, struct sslcert_session_data *ssl_d) { nserror err; int cert_loop; assert(ssl_d != NULL); LOG(("Building certificate viewer")); /* Init. certificate chain treeview entry fields */ err = sslcert_init_entry_fields(ssl_d); if (err != NSERROR_OK) { ssl_d->tree = NULL; return err; } /* Create the certificate treeview */ err = treeview_create(&ssl_d->tree, &sslv_tree_cb_t, SSLCERT_V_N_FIELDS, ssl_d->fields, cw_t, core_window_handle, TREEVIEW_READ_ONLY); if (err != NSERROR_OK) { ssl_d->tree = NULL; return err; } /* Build treeview nodes from certificate chain */ for (cert_loop = ssl_d->num - 1; cert_loop >= 0; cert_loop--) { err = sslcert_viewer_create_node(ssl_d, cert_loop); if (err != NSERROR_OK) { return err; } } LOG(("Built certificate viewer")); return NSERROR_OK; } /** * Free SSL certificate session data * * \param ssl_d SSL certificate session data */ static void sslcert_cleanup_session(struct sslcert_session_data *ssl_d) { assert(ssl_d != NULL); if (ssl_d->url) { nsurl_unref(ssl_d->url); ssl_d->url = NULL; } if (ssl_d->certs) { free(ssl_d->certs); ssl_d->certs = NULL; } free(ssl_d); } /* Exported interface, documented in sslcert_viewer.h */ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d) { int i; nserror err; LOG(("Finalising ssl certificate viewer")); /* Destroy the treeview */ err = treeview_destroy(ssl_d->tree); /* Free treeview entry fields */ for (i = 0; i < SSLCERT_V_N_FIELDS; i++) if (ssl_d->fields[i].field != NULL) lwc_string_unref(ssl_d->fields[i].field); /* Destroy the sslcert_session_data */ sslcert_cleanup_session(ssl_d); LOG(("Finalised ssl certificate viewer")); return err; } /* Exported interface, documented in sslcert_viewer.h */ nserror sslcert_viewer_create_session_data(unsigned long num, nsurl *url, llcache_query_response cb, void *cbpw, const struct ssl_cert_info *certs, struct sslcert_session_data **ssl_d) { struct sslcert_session_data *data; assert(url != NULL); assert(certs != NULL); data = malloc(sizeof(struct sslcert_session_data)); if (data == NULL) { *ssl_d = NULL; return NSERROR_NOMEM; } /* copy certificate data */ data->certs = malloc(num * sizeof(struct ssl_cert_info)); if (data->certs == NULL) { free(data); *ssl_d = NULL; return NSERROR_NOMEM; } memcpy(data->certs, certs, num * sizeof(struct ssl_cert_info)); data->url = nsurl_ref(url); data->num = num; data->cb = cb; data->cbpw = cbpw; data->tree = NULL; *ssl_d = data; return NSERROR_OK; } /* Exported interface, documented in sslcert_viewer.h */ nserror sslcert_viewer_reject(struct sslcert_session_data *ssl_d) { assert(ssl_d != NULL); ssl_d->cb(false, ssl_d->cbpw); return NSERROR_OK; } /* Exported interface, documented in sslcert_viewer.h */ nserror sslcert_viewer_accept(struct sslcert_session_data *ssl_d) { assert(ssl_d != NULL); urldb_set_cert_permissions(ssl_d->url, true); ssl_d->cb(true, ssl_d->cbpw); return NSERROR_OK; } /* Exported interface, documented in sslcert_viewer.h */ void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, int x, int y, struct rect *clip, const struct redraw_context *ctx) { assert(ssl_d != NULL && "sslcert_viewer_redraw() given bad session data"); treeview_redraw(ssl_d->tree, x, y, clip, ctx); } /* Exported interface, documented in sslcert_viewer.h */ void sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, browser_mouse_state mouse, int x, int y) { treeview_mouse_action(ssl_d->tree, mouse, x, y); } /* Exported interface, documented in sslcert_viewer.h */ void sslcert_viewer_keypress(struct sslcert_session_data *ssl_d, uint32_t key) { treeview_keypress(ssl_d->tree, key); }