"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/hitch.c" between
hitch-1.5.2.tar.gz and hitch-1.6.0.tar.gz

About: Hitch is a libev-based high performance SSL/TLS proxy that terminates TLS/SSL connections and forwards the unencrypted traffic to some backend.

hitch.c  (hitch-1.5.2):hitch.c  (hitch-1.6.0)
skipping to change at line 40 skipping to change at line 40
* *
* The views and conclusions contained in the software and * The views and conclusions contained in the software and
* documentation are those of the authors and should not be * documentation are those of the authors and should not be
* interpreted as representing official policies, either expressed or * interpreted as representing official policies, either expressed or
* implied, of Bump Technologies, Inc. * implied, of Bump Technologies, Inc.
* *
*/ */
#include "config.h" #include "config.h"
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/wait.h> /* WAIT_PID */ #include <sys/wait.h> /* WAIT_PID */
#ifdef __linux__ #ifdef __linux__
# include <sys/prctl.h> # include <sys/prctl.h>
#endif #endif
skipping to change at line 329 skipping to change at line 331
LOG("{core} Using DH parameters from %s\n", cert); LOG("{core} Using DH parameters from %s\n", cert);
if (!SSL_CTX_set_tmp_dh(ctx, dh)) { if (!SSL_CTX_set_tmp_dh(ctx, dh)) {
log_ssl_error(NULL, "{core} Error setting temp DH params"); log_ssl_error(NULL, "{core} Error setting temp DH params");
return (-1); return (-1);
} }
LOG("{core} DH initialized with %d bit key\n", 8*DH_size(dh)); LOG("{core} DH initialized with %d bit key\n", 8*DH_size(dh));
DH_free(dh); DH_free(dh);
return (0); return (0);
} }
static int init_ecdh(SSL_CTX *ctx) { static int init_ecdh(SSL_CTX *ctx, const char *ecdh_curve) {
#ifndef OPENSSL_NO_EC #ifndef OPENSSL_NO_EC
#ifdef NID_X9_62_prime256v1 if (ecdh_curve == NULL || strcmp(ecdh_curve, "auto") == 0) {
EC_KEY *ecdh = NULL; /* For openssl >= 1.1 'auto' is default on and this is
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); * just a NOOP macro. */
SSL_CTX_set_tmp_ecdh(ctx, ecdh); AN(SSL_CTX_set_ecdh_auto(ctx, 1));
EC_KEY_free(ecdh); } else {
LOG("{core} ECDH Initialized with NIST P-256\n"); /* For openssl >= 1.1 this should really be "groups"
#endif /* NID_X9_62_prime256v1 */ * instead of "curves", but we use the old name to
* support openssl 1.0 as well */
if (SSL_CTX_set1_curves_list(ctx,
ecdh_curve) == 0) {
log_ssl_error(NULL,
"{core} Configuring ecdh curves '%s' failed\n",
ecdh_curve);
}
}
LOG("{core} ECDH Initialized\n");
#else
(void) ctx;
(void) ecdh_curve;
#endif /* OPENSSL_NO_EC */ #endif /* OPENSSL_NO_EC */
return (0); return (0);
} }
#endif /* OPENSSL_NO_DH */ #endif /* OPENSSL_NO_DH */
/* This callback function is executed while OpenSSL processes the SSL /* This callback function is executed while OpenSSL processes the SSL
* handshake and does SSL record layer stuff. It's used to trap * handshake and does SSL record layer stuff. It's used to trap
* client-initiated renegotiations. * client-initiated renegotiations.
*/ */
static void static void
info_callback(const SSL *ssl, int where, int ret) info_callback(const SSL *ssl, int where, int ret)
skipping to change at line 891 skipping to change at line 904
x = sk_X509_value(chain, i); x = sk_X509_value(chain, i);
if (X509_check_issued(x, subj) == X509_V_OK) if (X509_check_issued(x, subj) == X509_V_OK)
return (x); return (x);
} }
/* todo: look in cert store? */ /* todo: look in cert store? */
return (NULL); return (NULL);
} }
static int
client_vfy_cb(int preverify_ok, X509_STORE_CTX *storectx)
{
proxystate *ps;
SSL *ssl;
ssl = X509_STORE_CTX_get_ex_data(storectx,
SSL_get_ex_data_X509_STORE_CTX_idx());
CAST_OBJ_NOTNULL(ps, SSL_get_app_data(ssl), PROXYSTATE_MAGIC);
if (preverify_ok)
ps->client_cert_conn = 1;
return (preverify_ok);
}
static int
client_vfy_init(SSL_CTX *ctx, int flags, const char *cafile)
{
X509_STORE *vfy;
STACK_OF(X509_OBJECT) *objs;
X509_OBJECT *o;
X509 *crt;
int i;
AN(cafile);
assert(flags != SSL_VERIFY_NONE);
AN(flags & SSL_VERIFY_PEER);
vfy = X509_STORE_new();
if (!vfy) {
log_ssl_error(NULL, "X509_STORE_new: allocation failed");
return (1);
}
if (X509_STORE_load_locations(vfy, cafile, NULL) == 0) {
log_ssl_error(NULL, "client_verify_ca: unable to "
"load file '%s'",
cafile);
X509_STORE_free(vfy);
return (1);
}
SSL_CTX_set1_verify_cert_store(ctx, vfy);
#ifdef HAVE_X509_STORE_GET0_OBJECTS
objs = X509_STORE_get0_objects(vfy);
#else
objs = vfy->objs;
#endif
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
o = sk_X509_OBJECT_value(objs, i);
#ifdef HAVE_X509_OBJECT_GET0_X509
crt = X509_OBJECT_get0_X509(o);
#else
crt = o->data.x509;
#endif
if (crt != NULL) {
/* SSL_CTX_add_client_CA makes a copy of the
* subject name, so the X509_STORE_free below
* is safe. */
SSL_CTX_add_client_CA(ctx, crt);
}
}
SSL_CTX_set_verify(ctx, flags, client_vfy_cb);
X509_STORE_free(vfy);
return (0);
}
/* Initialize an SSL context */ /* Initialize an SSL context */
static sslctx * static sslctx *
make_ctx_fr(const struct cfg_cert_file *cf, const struct frontend *fr, make_ctx_fr(const struct cfg_cert_file *cf, const struct frontend *fr,
const struct front_arg *fa) const struct front_arg *fa)
{ {
SSL_CTX *ctx; SSL_CTX *ctx;
sslctx *sc; sslctx *sc;
EVP_PKEY *pkey; EVP_PKEY *pkey;
int selected_protos = CONFIG->SELECTED_TLS_PROTOS; int selected_protos = CONFIG->SELECTED_TLS_PROTOS;
char *ciphers = CONFIG->CIPHER_SUITE; char *ciphers = CONFIG->CIPHERS_TLSv12;
char *ciphersuites = CONFIG->CIPHERSUITES_TLSv13;
int pref_srv_ciphers = CONFIG->PREFER_SERVER_CIPHERS; int pref_srv_ciphers = CONFIG->PREFER_SERVER_CIPHERS;
int client_verify = CONFIG->CLIENT_VERIFY;
if (fa != NULL) { if (fa != NULL) {
CHECK_OBJ_NOTNULL(fa, FRONT_ARG_MAGIC); CHECK_OBJ_NOTNULL(fa, FRONT_ARG_MAGIC);
if (fa->selected_protos != 0) if (fa->selected_protos != 0)
selected_protos = fa->selected_protos; selected_protos = fa->selected_protos;
if (fa->ciphers != NULL) if (fa->ciphers_tlsv12 != NULL)
ciphers = fa->ciphers; ciphers = fa->ciphers_tlsv12;
if (fa->prefer_server_ciphers != -1) if (fa->prefer_server_ciphers != -1)
pref_srv_ciphers = fa->prefer_server_ciphers; pref_srv_ciphers = fa->prefer_server_ciphers;
if (fa->ciphersuites_tlsv13)
ciphersuites = fa->ciphersuites_tlsv13;
if (fa->client_verify != -1)
client_verify = fa->client_verify;
} }
long ssloptions = SSL_OP_NO_SSLv2 | SSL_OP_ALL | long ssloptions = SSL_OP_NO_SSLv2 | SSL_OP_ALL |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
#ifdef SSL_OP_NO_COMPRESSION #ifdef SSL_OP_NO_COMPRESSION
ssloptions |= SSL_OP_NO_COMPRESSION; ssloptions |= SSL_OP_NO_COMPRESSION;
#endif #endif
#ifdef SSL_OP_SINGLE_DH_USE #ifdef SSL_OP_SINGLE_DH_USE
ssloptions |= SSL_OP_SINGLE_DH_USE; ssloptions |= SSL_OP_SINGLE_DH_USE;
skipping to change at line 958 skipping to change at line 1046
SSL_CTX_set_next_protos_advertised_cb(ctx, npn_select_cb, NULL); SSL_CTX_set_next_protos_advertised_cb(ctx, npn_select_cb, NULL);
#endif #endif
if (ciphers != NULL) { if (ciphers != NULL) {
if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) { if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) {
log_ssl_error(NULL, "{core} SSL_CTX_set_cipher_list"); log_ssl_error(NULL, "{core} SSL_CTX_set_cipher_list");
return (NULL); return (NULL);
} }
} }
#if HAVE_TLS_1_3
if (ciphersuites != NULL) {
if (SSL_CTX_set_ciphersuites(ctx, ciphersuites) != 1) {
log_ssl_error(NULL, "{core} SSL_CTX_set_ciphersuites");
return (NULL);
}
}
#else
(void) ciphersuites;
#endif
if (client_verify != SSL_VERIFY_NONE) {
const char *ca = CONFIG->CLIENT_VERIFY_CA;
if (fa && fa->client_verify_ca)
ca = fa->client_verify_ca;
AN(ca);
if (client_vfy_init(ctx, client_verify, ca))
return (NULL);
}
if (pref_srv_ciphers) if (pref_srv_ciphers)
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
AN(SSL_CTX_set_session_id_context(ctx, (const unsigned char *) "hitch",
strlen("hitch")));
ALLOC_OBJ(sc, SSLCTX_MAGIC); ALLOC_OBJ(sc, SSLCTX_MAGIC);
AN(sc); AN(sc);
sc->filename = strdup(cf->filename); sc->filename = strdup(cf->filename);
sc->mtim = cf->mtim; sc->mtim = cf->mtim;
sc->ctx = ctx; sc->ctx = ctx;
sc->staple_vfy = cf->ocsp_vfy; sc->staple_vfy = cf->ocsp_vfy;
VTAILQ_INIT(&sc->sni_list); VTAILQ_INIT(&sc->sni_list);
if (sc->staple_vfy > 0 || if (sc->staple_vfy > 0 ||
(sc-> staple_vfy < 0 && CONFIG->OCSP_VFY)) (sc-> staple_vfy < 0 && CONFIG->OCSP_VFY))
skipping to change at line 1013 skipping to change at line 1123
if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) { if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) {
log_ssl_error(NULL, "SSL_CTX_use_PrivateKey: %s", log_ssl_error(NULL, "SSL_CTX_use_PrivateKey: %s",
cf->filename); cf->filename);
EVP_PKEY_free(pkey); EVP_PKEY_free(pkey);
sctx_free(sc, NULL); sctx_free(sc, NULL);
return (NULL); return (NULL);
} }
#ifndef OPENSSL_NO_DH #ifndef OPENSSL_NO_DH
init_dh(ctx, cf->filename); init_dh(ctx, cf->filename);
init_ecdh(ctx); init_ecdh(ctx, CONFIG->ECDH_CURVE);
#endif /* OPENSSL_NO_DH */ #endif /* OPENSSL_NO_DH */
#ifndef OPENSSL_NO_TLSEXT #ifndef OPENSSL_NO_TLSEXT
if (!SSL_CTX_set_tlsext_servername_callback(ctx, sni_switch_ctx)) { if (!SSL_CTX_set_tlsext_servername_callback(ctx, sni_switch_ctx)) {
ERR("Error setting up SNI support.\n"); ERR("Error setting up SNI support.\n");
} }
CHECK_OBJ_ORNULL(fr, FRONTEND_MAGIC); CHECK_OBJ_ORNULL(fr, FRONTEND_MAGIC);
if (!SSL_CTX_set_tlsext_servername_arg(ctx, (void *)fr)) { if (!SSL_CTX_set_tlsext_servername_arg(ctx, (void *)fr)) {
ERR("Error setting SNI servername arg.\n"); ERR("Error setting SNI servername arg.\n");
} }
if (load_cert_ctx(sc) != 0) { if (load_cert_ctx(sc) != 0) {
EVP_PKEY_free(pkey); EVP_PKEY_free(pkey);
sctx_free(sc, NULL); sctx_free(sc, NULL);
return (NULL); return (NULL);
} }
if (CONFIG->OCSP_DIR) { if (CONFIG->OCSP_DIR) {
char *fn = HOCSP_fn(sc->filename); char *fn = HOCSP_fn(sc->filename);
/* attempt loading of cached ocsp staple */ /* attempt loading of cached ocsp staple */
if (fn != NULL && HOCSP_init_file(fn, sc, 1) == 0) { if (fn != NULL) {
LOG("{core} Loaded cached OCSP staple for cert '%s'\n", if (HOCSP_init_file(fn, sc, 1) == 0) {
sc->filename); LOG("{core} Loaded cached OCSP staple "
sc->staple_fn = fn; "for cert '%s'\n", sc->filename);
sc->staple_fn = fn;
}
else {
free(fn);
}
} }
} }
if (sc->staple == NULL && cf->ocspfn != NULL) { if (sc->staple == NULL && cf->ocspfn != NULL) {
if (HOCSP_init_file(cf->ocspfn, sc, 0) != 0) { if (HOCSP_init_file(cf->ocspfn, sc, 0) != 0) {
ERR("Error loading OCSP response %s for stapling.\n", ERR("Error loading OCSP response %s for stapling.\n",
cf->ocspfn); cf->ocspfn);
EVP_PKEY_free(pkey); EVP_PKEY_free(pkey);
sctx_free(sc, NULL); sctx_free(sc, NULL);
return (NULL); return (NULL);
skipping to change at line 1979 skipping to change at line 2094
tlv_tok = SSL_get_servername(ps->ssl, tlv_tok = SSL_get_servername(ps->ssl,
TLSEXT_NAMETYPE_host_name); TLSEXT_NAMETYPE_host_name);
if (tlv_tok != NULL) { if (tlv_tok != NULL) {
tlv_len = strlen(tlv_tok); tlv_len = strlen(tlv_tok);
i = proxy_tlv_append(base + len, maxlen - len, i = proxy_tlv_append(base + len, maxlen - len,
PP2_TYPE_AUTHORITY, tlv_tok, tlv_len); PP2_TYPE_AUTHORITY, tlv_tok, tlv_len);
len += i; len += i;
} }
} }
if (CONFIG->PROXY_TLV) { if (CONFIG->PROXY_TLV) {
char *tlvp = base + len; X509 *crt;
ssize_t sz = 0; ssize_t sz = 0;
struct pp2_tlv_ssl *tlv;
char *tlvp = base + len;
const char *tmp; const char *tmp;
tlvp[0] = PP2_TYPE_SSL; tlvp[0] = PP2_TYPE_SSL;
/* tlvp[1..2] to be updated with payload length later */ /* tlvp[1..2] to be updated with payload length later */
len += 3; len += 3;
tlv = (struct pp2_tlv_ssl *) (&base[len]);
tlv->client = PP2_CLIENT_SSL;
tlv->verify = 1;
/* PP2_CLIENT_CERT_SESS */
crt = SSL_get_peer_certificate(ps->ssl);
if (crt) {
tlv->client |= PP2_CLIENT_CERT_SESS;
tlv->verify = htonl(SSL_get_verify_result(ps->ssl));
X509_free(crt);
}
base[len] = PP2_CLIENT_SSL; /* PP2_CLIENT_CERT_CONN */
base[len + 1] = 1; if (ps->client_cert_conn)
tlv->client |= PP2_CLIENT_CERT_CONN;
len += 5; len += 5;
sz += 5; sz += 5;
tmp = SSL_get_version(ps->ssl); tmp = SSL_get_version(ps->ssl);
AN(tmp); AN(tmp);
i = proxy_tlv_append(base + len, maxlen - len, i = proxy_tlv_append(base + len, maxlen - len,
PP2_SUBTYPE_SSL_VERSION, tmp, -1); PP2_SUBTYPE_SSL_VERSION, tmp, -1);
len += i; len += i;
sz += i; sz += i;
skipping to change at line 3323 skipping to change at line 3452
act.sa_handler = sighup_handler; act.sa_handler = sighup_handler;
if (sigaction(SIGHUP, &act, NULL) != 0) { if (sigaction(SIGHUP, &act, NULL) != 0) {
ERR("Unable to register SIGHUP signal handler: %s\n", ERR("Unable to register SIGHUP signal handler: %s\n",
strerror(errno)); strerror(errno));
exit(1); exit(1);
} }
} }
#define NULL_DEV "/dev/null"
static void
daemonize()
{
/* logging.c */
if (logfile == stdout || logfile == stderr) {
logfile = NULL;
}
/* go to root directory */
if (chdir("/") != 0) {
ERR("Unable change directory to /: %s\n", strerror(errno));
exit(1);
}
/* let's make some children, baby :) */
pid_t pid = fork();
if (pid < 0) {
ERR("Unable to daemonize: fork failed: %s\n", strerror(errno));
exit(1);
}
/* am i the parent? */
if (pid != 0) {
LOGL("{core} Daemonized as pid %d.\n", pid);
exit(0);
}
/* reopen standard streams to null device */
if (freopen(NULL_DEV, "r", stdin) == NULL) {
ERR("Unable to reopen stdin to %s: %s\n",
NULL_DEV, strerror(errno));
exit(1);
}
if (freopen(NULL_DEV, "w", stdout) == NULL) {
ERR("Unable to reopen stdout to %s: %s\n",
NULL_DEV, strerror(errno));
exit(1);
}
if (freopen(NULL_DEV, "w", stderr) == NULL) {
ERR("Unable to reopen stderr to %s: %s\n",
NULL_DEV, strerror(errno));
exit(1);
}
/* this is child, the new master */
pid_t s = setsid();
if (s < 0) {
ERR("Unable to create new session, setsid(2) failed: "
"%s :: %d\n", strerror(errno), s);
exit(1);
}
LOG("Successfully daemonized as pid %d.\n", getpid());
}
static void static void
openssl_check_version() openssl_check_version()
{ {
/* detect OpenSSL version in runtime */ /* detect OpenSSL version in runtime */
long openssl_version = SSLeay(); long openssl_version = SSLeay();
/* check if we're running the same openssl that we were */ /* check if we're running the same openssl that we were */
/* compiled with */ /* compiled with */
if ((openssl_version ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) { if ((openssl_version ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) {
ERR( ERR(
skipping to change at line 3911 skipping to change at line 3984
struct front_arg *fa, *ftmp; struct front_arg *fa, *ftmp;
CONFIG = config_new(); CONFIG = config_new();
// parse command line // parse command line
if (config_parse_cli(argc, argv, CONFIG) != 0) { if (config_parse_cli(argc, argv, CONFIG) != 0) {
fprintf(stderr, "%s\n", config_error_get()); fprintf(stderr, "%s\n", config_error_get());
return (1); return (1);
} }
if (CONFIG->TEST) {
fprintf(stderr, "Trying to initialize SSL contexts with your"
" certificates\n");
init_globals();
init_openssl();
init_certs();
fprintf(stderr, "%s configuration looks ok.\n",
basename(argv[0]));
return (0);
}
if (CONFIG->LOG_FILENAME) { if (CONFIG->LOG_FILENAME) {
FILE* f; FILE* f;
if ((f = fopen(CONFIG->LOG_FILENAME, "a")) == NULL) { if ((f = fopen(CONFIG->LOG_FILENAME, "a")) == NULL) {
/* logging.c */ /* logging.c */
logfile = stderr; logfile = stderr;
ERR("FATAL: Unable to open log file: %s: %s\n", ERR("FATAL: Unable to open log file: %s: %s\n",
CONFIG->LOG_FILENAME, strerror(errno)); CONFIG->LOG_FILENAME, strerror(errno));
exit(2); exit(2);
} }
logfile = f; logfile = f;
if (CONFIG->UID >=0 || CONFIG->GID >= 0) { if (CONFIG->UID >=0 || CONFIG->GID >= 0) {
AZ(fchown(fileno(logfile), CONFIG->UID, CONFIG->GID)); AZ(fchown(fileno(logfile), CONFIG->UID, CONFIG->GID));
} }
AZ(fstat(fileno(logfile), &logf_st)); AZ(fstat(fileno(logfile), &logf_st));
logf_check_t = time(NULL); logf_check_t = time(NULL);
} else { } else {
logfile = stderr; logfile = stderr;
} }
AZ(setvbuf(logfile, NULL, _IONBF, BUFSIZ)); AZ(setvbuf(logfile, NULL, _IONBF, BUFSIZ));
if (CONFIG->DAEMONIZE && (logfile == stdout || logfile == stderr)) if (CONFIG->TEST) {
logfile = NULL; /* Override log level for config test */
CONFIG->LOG_LEVEL = 3;
fprintf(stderr, "Trying to initialize SSL contexts with your"
" certificates\n");
init_globals();
init_openssl();
init_certs();
fprintf(stderr, "%s configuration looks ok.\n",
basename(argv[0]));
return (0);
}
LOGL("{core} %s starting\n", PACKAGE_STRING); LOGL("{core} %s starting\n", PACKAGE_STRING);
create_workers = 1; create_workers = 1;
openssl_check_version(); openssl_check_version();
init_signals(); init_signals();
init_globals(); init_globals();
init_openssl(); init_openssl();
skipping to change at line 3985 skipping to change at line 4057
ERR("{core} ERROR: chroot requires hitch to be" ERR("{core} ERROR: chroot requires hitch to be"
" started as root.\n"); " started as root.\n");
exit(1); exit(1);
} }
if (geteuid() == 0 && CONFIG->UID < 0) { if (geteuid() == 0 && CONFIG->UID < 0) {
ERR("{core} ERROR: Refusing to run workers as root.\n"); ERR("{core} ERROR: Refusing to run workers as root.\n");
exit(1); exit(1);
} }
if (CONFIG->DAEMONIZE) if (CONFIG->DAEMONIZE) {
daemonize(); if (!CONFIG->SYSLOG && !CONFIG->LOG_FILENAME) {
LOG("{core} Warning: daemonizing with neither "
"'syslog' nor 'log-filename' configured: "
"Hitch will not produce log messages.\n");
}
if (logfile == stdout || logfile == stderr)
logfile = NULL;
if (daemon(0, 0) == -1) {
ERR("Unable to daemonize: %s\n", strerror(errno));
exit(1);
}
}
master_pid = getpid(); master_pid = getpid();
if (CONFIG->PIDFILE) { if (CONFIG->PIDFILE) {
pfh = VPF_Open(CONFIG->PIDFILE, 0644, NULL); pfh = VPF_Open(CONFIG->PIDFILE, 0644, NULL);
if (pfh == NULL) { if (pfh == NULL) {
ERR("FATAL: Could not open pid (-p) file (%s): %s\n", ERR("FATAL: Could not open pid (-p) file (%s): %s\n",
CONFIG->PIDFILE, strerror(errno)); CONFIG->PIDFILE, strerror(errno));
exit(1); exit(1);
} }
 End of changes. 21 change blocks. 
91 lines changed or deleted 174 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)