45#include "../../dlib/dlib.h"
46#include "../dialog.hh"
52#include <openssl/ssl.h>
53#include <openssl/rand.h>
54#include <openssl/err.h>
55#include <openssl/x509v3.h>
56#include <openssl/crypto.h>
58#define CERT_STATUS_NONE 0
59#define CERT_STATUS_RECEIVING 1
60#define CERT_STATUS_CLEAN 2
61#define CERT_STATUS_BAD 3
62#define CERT_STATUS_USER_ACCEPTED 4
105 const FdMapEntry_t *e = v1;
107 return (fd != e->fd);
112 FdMapEntry_t *e =
dNew0(FdMapEntry_t, 1);
114 e->connkey = connkey;
117 MSG_ERR(
"TLS FD ENTRY ALREADY FOUND FOR %d\n", e->fd);
137 MSG(
"TLS FD ENTRY NOT FOUND FOR %d\n", fd);
166 Conn_t *conn =
dNew0(Conn_t, 1);
170 conn->connecting =
TRUE;
171 conn->in_connect =
FALSE;
172 conn->do_shutdown =
TRUE;
186 if (where & SSL_CB_ALERT) {
187 const char *str = SSL_alert_desc_string_long(ret);
189 if (strcmp(str,
"close notify"))
190 MSG(
"TLS ALERT on %s: %s\n", (where & SSL_CB_READ) ?
"read" :
"write",
212 static const char *
const ca_files[] = {
213 "/etc/ssl/certs/ca-certificates.crt",
214 "/etc/pki/tls/certs/ca-bundle.crt",
215 "/usr/share/ssl/certs/ca-bundle.crt",
216 "/usr/local/share/certs/ca-root.crt",
218 "/etc/openssl/certs/ca-certificates.crt",
222 static const char *
const ca_paths[] = {
227 X509_STORE *store = SSL_CTX_get_cert_store(
ssl_context);
228 X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
230 for (u = 0; u <
sizeof(ca_files) /
sizeof(ca_files[0]); u++) {
232 X509_LOOKUP_load_file(lookup, ca_files[u], X509_FILETYPE_PEM);
235 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
236 for (u = 0; u <
sizeof(ca_paths)/
sizeof(ca_paths[0]); u++) {
238 X509_LOOKUP_add_dir(lookup, ca_paths[u], X509_FILETYPE_PEM);
242 X509_LOOKUP_add_dir(lookup, userpath, X509_FILETYPE_PEM);
246 while(ERR_get_error())
254 const char *ver = OpenSSL_version(OPENSSL_VERSION);
255 if (snprintf(buf, n,
"%s", ver) >= n)
259 char *sp1 = strchr(ossl,
' ');
262 char *sp2 = strchr(ossl,
' ');
276 MSG(
"TLS library: %s\n", OpenSSL_version(OPENSSL_VERSION));
278 SSL_load_error_strings();
279 if (RAND_status() != 1) {
283 MSG_ERR(
"Disabling HTTPS: Insufficient entropy for openssl.\n");
290 MSG_ERR(
"Disabling HTTPS: Error creating SSL context.\n");
302 "ALL:!aNULL:!eNULL:!LOW:!EXPORT40:!RC4");
308 SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
311 SSL_CTX_set_verify(
ssl_context, SSL_VERIFY_NONE, NULL);
334 snprintf(buf, 4096,
"%s/.dillo/certs/",
dGethomedir());
338 snprintf(buf, 4096,
"%s/.dillo/certs/%lx.%u",
346 MSG(
"Unable to open cert save file in home dir\n");
349 PEM_write_X509(fp, cert);
351 MSG(
"Wrote certificate\n");
370 const Server_t *s1 = (
const Server_t *)v1, *s2 = (
const Server_t *)v2;
374 cmp = s1->port - s2->port;
382 const Server_t *s = (
const Server_t *)v1;
416 s =
dNew(Server_t, 1);
467 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
470 const uint_t buflen = 4096;
472 int rc, i, n = sk_X509_num(sk);
474 EVP_PKEY *public_key;
475 int key_type, key_bits;
476 const char *type_str;
479 for (i = 0; i < n; i++) {
480 cert = sk_X509_value(sk, i);
481 public_key = X509_get_pubkey(cert);
488 b = BIO_new(BIO_s_mem());
489 const ASN1_OBJECT *algorithm;
490 X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(cert));
491 rc = i2a_ASN1_OBJECT(b, algorithm);
494 rc = BIO_gets(b, buf, buflen);
497 strcpy(buf,
"(unknown)");
498 buf[buflen-1] =
'\0';
500 char *s = strstr(buf,
"With");
504 if (!strcmp(buf,
"sha1")) {
506 MSG_WARN(
"In 2015, browsers have begun to deprecate SHA1 "
508 }
else if (!strncmp(buf,
"md", 2) && success ==
TRUE) {
509 const char *msg =
"A certificate in the chain uses the MD5 "
510 "signature algorithm, which is too weak "
513 "Continue",
"Cancel", NULL);
524#if OPENSSL_VERSION_NUMBER < 0x30000000L
525 key_type = EVP_PKEY_type(EVP_PKEY_id(public_key));
527 key_type = EVP_PKEY_type(EVP_PKEY_get_id(public_key));
529 type_str = key_type == EVP_PKEY_RSA ?
"RSA" :
530 key_type == EVP_PKEY_DSA ?
"DSA" :
531 key_type == EVP_PKEY_DH ?
"DH" :
532 key_type == EVP_PKEY_EC ?
"EC" :
"???";
533 key_bits = EVP_PKEY_bits(public_key);
534 X509_NAME_oneline(X509_get_subject_name(cert), buf, buflen);
535 buf[buflen-1] =
'\0';
537 MSG(
"%d-bit %s: %s\n", key_bits, type_str, buf);
538 EVP_PKEY_free(public_key);
540 if (key_type == EVP_PKEY_RSA && key_bits <= 1024) {
542 MSG_WARN(
"In 2014/5, browsers have been deprecating 1024-bit "
548 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
549 buf[buflen-1] =
'\0';
551 MSG(
"root: %s\n", buf);
559#define ASTERISK_EXCLUDES_DOT
578 const char *p = pattern, *n = string;
580 for (; (c = tolower (*p++)) !=
'\0'; n++)
583 for (c = tolower (*p); c ==
'*'; c = tolower (*++p))
585 for (; *n !=
'\0'; n++)
588#ifdef ASTERISK_EXCLUDES_DOT
596 if (c != tolower (*n))
611 if (cert == NULL || host == NULL)
615 GENERAL_NAMES *subjectAltNames;
617 char common_name[256];
629 subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
636 dStr_sprintf(err,
"Hostname %s does not match any of certificate's "
637 "Subject Alternative Names: ", host);
641 ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);
643 int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
645 for (i=0; i < numaltnames; i++)
647 const GENERAL_NAME *name =
648 sk_GENERAL_NAME_value (subjectAltNames, i);
651 if (host_in_octet_string)
653 if (name->type == GEN_IPADD)
658 alt_name_checked =
TRUE;
659 if (!ASN1_STRING_cmp (host_in_octet_string,
665 else if (name->type == GEN_DNS)
669 unsigned char *name_in_utf8 = NULL;
672 alt_name_checked =
TRUE;
674 if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
678 (strlen ((
char *)name_in_utf8) ==
679 (
size_t)ASN1_STRING_length (name->d.dNSName)))
681 OPENSSL_free (name_in_utf8);
685 OPENSSL_free (name_in_utf8);
690 sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
691 if (host_in_octet_string)
692 ASN1_OCTET_STRING_free(host_in_octet_string);
694 if (alt_name_checked ==
TRUE && i >= numaltnames)
698 err->
str,
"Continue",
"Cancel", NULL);
713 if (alt_name_checked ==
FALSE)
716 X509_NAME *xname = X509_get_subject_name(cert);
717 common_name[0] =
'\0';
718 X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
719 sizeof (common_name));
724 msg =
dStrconcat(
"Certificate common name ", common_name,
725 " doesn't match requested host name ", host, NULL);
727 msg,
"Continue",
"Cancel", NULL);
751 X509_NAME_ENTRY *xentry;
757 j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
763 xentry = X509_NAME_get_entry(xname,i);
764 sdata = X509_NAME_ENTRY_get_data(xentry);
765 if (strlen (common_name) != (
size_t)ASN1_STRING_length (sdata))
768 msg =
dStrconcat(
"Certificate common name is invalid (contains a NUL "
769 "character). This may be an indication that the "
770 "host is not who it claims to be -- that is, not "
771 "the real ", host, NULL);
773 msg,
"Continue",
"Cancel", NULL);
802 STACK_OF(X509) *sk = SSL_get_peer_cert_chain(ssl);
804 return sk ? sk_X509_value(sk, sk_X509_num(sk) - 1) : NULL;
810 X509_NAME_oneline(X509_get_issuer_name(cert), buf, buflen);
812 strcpy(buf,
"(unknown)");
813 buf[buflen-1] =
'\0';
819 ASN1_TIME *exp_date = X509_get_notAfter(cert);
820 BIO *b = BIO_new(BIO_s_mem());
821 int rc = ASN1_TIME_print(b, exp_date);
824 rc = BIO_gets(b, buf, buflen);
827 strcpy(buf,
"(unknown)");
828 buf[buflen-1] =
'\0';
842 const uint_t buflen = 4096;
843 char buf[buflen], *cn, *msg;
844 int choice = -1, ret = -1;
845 char *title =
dStrconcat(
"Dillo TLS security warning: ",srv->hostname,NULL);
847#if OPENSSL_VERSION_NUMBER < 0x30000000L
848 remote_cert = SSL_get_peer_certificate(ssl);
851 remote_cert = SSL_get1_peer_certificate(ssl);
853 if (remote_cert == NULL){
856 "The remote system is not presenting a certificate. "
857 "This site cannot be trusted. Sending data is not safe.",
858 "Continue",
"Cancel", NULL);
867 st = SSL_get_verify_result(ssl);
872 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
876 X509_NAME *subject_name = X509_get_subject_name(remote_cert);
877 char *subj = X509_NAME_oneline(subject_name, NULL, 0);
878 if ((cn = strstr(subj,
"/CN=")) == NULL) {
879 strcpy(buf,
"(no CN given)");
885 if ((cn_end = strstr(cn,
"/")) == NULL )
886 cn_end = cn + strlen(cn);
888 strncpy(buf, cn, (
size_t) (cn_end - cn));
889 buf[cn_end - cn] =
'\0';
892 msg =
dStrconcat(
"The remote certificate is self-signed and "
893 "untrusted. For address: ", buf, NULL);
895 msg,
"Continue",
"Cancel",
"Save Certificate", NULL);
913 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
914 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
916 "The issuer for the remote certificate cannot be found. "
917 "The authenticity of the remote certificate cannot be trusted.",
918 "Continue",
"Cancel", NULL);
925 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
926 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
927 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
928 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
930 "The remote certificate signature could not be read "
931 "or is invalid and should not be trusted",
932 "Continue",
"Cancel", NULL);
938 case X509_V_ERR_CERT_NOT_YET_VALID:
939 case X509_V_ERR_CRL_NOT_YET_VALID:
941 "Part of the remote certificate is not yet valid. "
942 "Certificates usually have a range of dates over which "
943 "they are to be considered valid, and the certificate "
944 "presented has a starting validity after today's date "
945 "You should be cautious about using this site",
946 "Continue",
"Cancel", NULL);
952 case X509_V_ERR_CERT_HAS_EXPIRED:
953 case X509_V_ERR_CRL_HAS_EXPIRED:
955 msg =
dStrconcat(
"The remote certificate expired on: ", buf,
956 ". This site can no longer be trusted.", NULL);
964 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
965 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
966 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
967 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
969 "There was an error in the certificate presented. "
970 "Some of the certificate data was improperly formatted "
971 "making it impossible to determine if the certificate "
972 "is valid. You should not trust this certificate.",
973 "Continue",
"Cancel", NULL);
978 case X509_V_ERR_INVALID_CA:
979 case X509_V_ERR_INVALID_PURPOSE:
980 case X509_V_ERR_CERT_UNTRUSTED:
981 case X509_V_ERR_CERT_REJECTED:
982 case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
984 "One of the certificates in the chain is being used "
985 "incorrectly (possibly due to configuration problems "
986 "with the remote system. The connection should not "
988 "Continue",
"Cancel", NULL);
993 case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
994 case X509_V_ERR_AKID_SKID_MISMATCH:
995 case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
997 "Some of the information presented by the remote system "
998 "does not match other information presented. "
999 "This may be an attempt to eavesdrop on communications",
1000 "Continue",
"Cancel", NULL);
1005 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1007 msg =
dStrconcat(
"Certificate chain led to a self-signed certificate "
1008 "instead of a trusted root. Name: ", buf , NULL);
1015 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1017 msg =
dStrconcat(
"The issuer certificate of an untrusted certificate "
1018 "cannot be found. Issuer: ", buf, NULL);
1027 "The remote certificate cannot be verified (code %ld)", st);
1029 buf,
"Continue",
"Cancel", NULL);
1035 X509_free(remote_cert);
1042 }
else if (choice == 1) {
1077 if (c->connecting) {
1081 if (c->do_shutdown && !SSL_in_init(c->ssl)) {
1085 SSL_shutdown(c->ssl);
1087 MSG(
"Tls_close_by_key: Avoiding SSL shutdown for: %s\n",
URL_STR(c->url));
1109 MSG(
"Tls_connect: conn for fd %d not valid\n", fd);
1113 if (conn->in_connect) {
1114 MSG(
"Tls_connect: nested call to Tls_connect(), aborting\n");
1117 conn->in_connect =
TRUE;
1120 if (ERR_peek_error()) {
1122 while ((err = ERR_get_error())) {
1123 MSG(
"Tls_connect: queued error: %s\n",
1124 ERR_error_string(err, NULL));
1129 ret = SSL_connect(conn->ssl);
1134 int err1_ret = SSL_get_error(conn->ssl, ret);
1135 if (err1_ret == SSL_ERROR_WANT_READ ||
1136 err1_ret == SSL_ERROR_WANT_WRITE) {
1139 _MSG(
"iowatching fd %d for tls -- want %s\n", fd,
1140 err1_ret == SSL_ERROR_WANT_READ ?
"read" :
"write");
1144 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1149 conn->do_shutdown =
FALSE;
1151 unsigned long err2_ret = ERR_get_error();
1155 MSG(
"SSL_connect() failed: %s\n",
1156 ERR_error_string(err2_ret, NULL));
1157 }
while ((err2_ret = ERR_get_error()));
1161 MSG(
"TLS connect error: \"an EOF was observed that violates "
1162 "the protocol\"\n");
1167 }
else if (ret == -1) {
1173 assert(errno != EAGAIN && errno != EINTR);
1175 MSG_ERR(
"According to the man page for SSL_get_error(), this "
1176 "was not a possibility (ret %d).\n", ret);
1180 MSG(
"SSL_get_error() returned %d on a connect.\n", err1_ret);
1188 SSL *ssl = conn->ssl;
1189 const char *version = SSL_get_version(ssl);
1190 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1193 SSL_CIPHER_get_name(cipher));
1209 conn->connecting =
FALSE;
1211 conn->in_connect =
FALSE;
1219 MSG(
"Connection disappeared. Too long with a popup popped up?\n");
1224 conn->in_connect =
FALSE;
1248 if (ERR_peek_error()) {
1250 while ((err = ERR_get_error())) {
1251 MSG(
"a_Tls_openssl_connect: queued error: %s\n",
1252 ERR_error_string(err, NULL));
1258 unsigned long err_ret = ERR_get_error();
1260 MSG(
"SSL_new() failed: %s\n", ERR_error_string(err_ret, NULL));
1261 }
while ((err_ret = ERR_get_error()));
1266 if (success && !SSL_set_fd(ssl, fd)) {
1267 unsigned long err_ret = ERR_get_error();
1269 MSG(
"SSL_set_fd() failed: %s\n", ERR_error_string(err_ret, NULL));
1270 }
while ((err_ret = ERR_get_error()));
1277#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1282 SSL_set_tlsext_host_name(ssl,
URL_HOST(url));
1307 SSL *ssl = conn->ssl;
1308 int err1_ret = SSL_get_error(ssl, ret);
1309 if (err1_ret == SSL_ERROR_NONE) {
1312 }
else if (err1_ret == SSL_ERROR_ZERO_RETURN) {
1315 }
else if (err1_ret == SSL_ERROR_WANT_READ || err1_ret == SSL_ERROR_WANT_WRITE) {
1318 }
else if (err1_ret == SSL_ERROR_SYSCALL || err1_ret == SSL_ERROR_SSL) {
1319 conn->do_shutdown =
FALSE;
1320 unsigned long err2_ret = ERR_get_error();
1323 MSG(
"%s failed: %s\n", where, ERR_error_string(err2_ret, NULL));
1324 }
while ((err2_ret = ERR_get_error()));
1327 const char *which = err1_ret == SSL_ERROR_SYSCALL ?
"SYSCALL" :
"SSL";
1328 MSG(
"%s failed: SSL_ERROR_%s\n", where, which);
1334 MSG(
"%s failed: SSL_get_error() returned %d\n", where, err1_ret);
1344 Conn_t *c = (Conn_t*)conn;
1353 Conn_t *c = (Conn_t*)conn;
1373 for (i = 0; i < n; i++) {
1388 for (i = 0; i < n; i++) {
int a_Dialog_choice(const char *title, const char *msg,...)
Make a question-dialog with a question and alternatives.
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
int dStrAsciiCasecmp(const char *s1, const char *s2)
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
char * dStrdup(const char *s)
Dlist * dList_new(int size)
Create a new empty list.
int dList_length(Dlist *lp)
For completing the ADT.
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
void dStr_free(Dstr *ds, int all)
Free a dillo string.
int dClose(int fd)
Close a FD handling EINTR.
void dStr_sprintf(Dstr *ds, const char *format,...)
Printf-like function.
Dstr * dStr_new(const char *s)
Create a new string.
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
void dList_free(Dlist *lp)
Free a list (not its elements)
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
char * dGethomedir(void)
Return the home directory in a static string (don't free)
#define dNew0(type, count)
#define dNew(type, count)
void a_Http_connect_done(int fd, bool_t success)
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
void a_Klist_remove(Klist_t *Klist, int Key)
Remove data by Key.
void * a_Klist_get_data(Klist_t *Klist, int Key)
Return the data pointer for a given Key (or NULL if not found)
int a_Klist_insert(Klist_t **Klist, void *Data)
Insert a data pointer and return a key for it.
#define TLS_CONNECT_NEVER
#define TLS_CONNECT_READY
#define TLS_CONNECT_NOT_YET
void a_Tls_openssl_reset_server_state(const DilloUrl *url)
static int Tls_examine_certificate(SSL *ssl, Server_t *srv)
static void Tls_fd_map_remove_all(void)
static void Tls_fd_map_remove_entry(int fd)
static void Tls_get_issuer_name(X509 *cert, char *buf, uint_t buflen)
static void Tls_servers_freeall(void)
int a_Tls_openssl_read(void *conn, void *buf, size_t len)
static X509 * Tls_get_end_of_chain(SSL *ssl)
static int Tls_servers_cmp(const void *v1, const void *v2)
static void Tls_info_cb(const SSL *ssl, int where, int ret)
void * a_Tls_openssl_connection(int fd)
static int Tls_handle_error(Conn_t *conn, int ret, const char *where)
int a_Tls_openssl_connect_ready(const DilloUrl *url)
#define CERT_STATUS_USER_ACCEPTED
static void Tls_fd_map_add_entry(int fd, int connkey)
static int Tls_fd_map_cmp(const void *v1, const void *v2)
static int Tls_user_said_no(const DilloUrl *url)
static int Tls_conn_new(int fd, const DilloUrl *url, SSL *ssl)
static void Tls_close_by_key(int connkey)
#define CERT_STATUS_RECEIVING
void a_Tls_openssl_init(void)
static void Tls_get_expiration_str(X509 *cert, char *buf, uint_t buflen)
#define CERT_STATUS_CLEAN
static SSL_CTX * ssl_context
static int Tls_servers_by_url_cmp(const void *v1, const void *v2)
static void Tls_connect_cb(int fd, void *vconnkey)
static void Tls_connect(int fd, int connkey)
void a_Tls_openssl_close_by_fd(int fd)
const char * a_Tls_openssl_version(char *buf, int n)
static bool_t Tls_check_cert_hostname(X509 *cert, const char *host, int *choice)
void a_Tls_openssl_connect(int fd, const DilloUrl *url)
static int Tls_save_certificate_home(X509 *cert)
static int Tls_cert_status(const DilloUrl *url)
static void Tls_load_certificates(void)
static Klist_t * conn_list
static bool_t Tls_check_cert_strength(SSL *ssl, Server_t *srv, int *choice)
int a_Tls_openssl_certificate_is_clean(const DilloUrl *url)
void a_Tls_openssl_freeall(void)
static bool_t pattern_match(const char *pattern, const char *string)
int a_Tls_openssl_write(void *conn, void *buf, size_t len)
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
int a_Url_host_type(const char *host)
What type of host is this?
DilloUrl * a_Url_dup(const DilloUrl *ori)
Duplicate a Url structure.