|
|
|
/*-
|
|
|
|
* SSLsplit - transparent SSL/TLS interception
|
|
|
|
* https://www.roe.ch/SSLsplit
|
|
|
|
*
|
|
|
|
* Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
|
|
|
|
* Copyright (c) 2017-2024, Soner Tari <sonertari@gmail.com>.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "opts.h"
|
|
|
|
#include "filter.h"
|
|
|
|
|
|
|
|
#include "sys.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "defaults.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
#include <openssl/dh.h>
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#include <openssl/x509.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Temporary struct used while configuring proxyspec.
|
|
|
|
* These vars are used while configuring proxyspecs,
|
|
|
|
* and freed right after they are used, not in proxyspec_free().
|
|
|
|
*/
|
|
|
|
typedef struct spec_addrs {
|
|
|
|
int af;
|
|
|
|
char *addr;
|
|
|
|
char *divert_addr;
|
|
|
|
char *target_addr;
|
|
|
|
} spec_addrs_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The topmost caller must exit with EXIT_FAILURE.
|
|
|
|
* Returning -1 instead of calling exit() is necessary for reporting the
|
|
|
|
* include file the error has occurred in.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
oom_return(const char *argv0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: out of memory\n", argv0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
oom_return_null(const char *argv0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: out of memory\n", argv0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
oom_return_na()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
oom_return_na_null()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load a cert/chain/key combo from a single PEM file.
|
|
|
|
* Returns NULL on failure.
|
|
|
|
*/
|
|
|
|
cert_t *
|
|
|
|
opts_load_cert_chain_key(const char *filename)
|
|
|
|
{
|
|
|
|
cert_t *cert;
|
|
|
|
|
|
|
|
cert = cert_new_load(filename);
|
|
|
|
if (!cert) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Failed to load cert and key from PEM file "
|
|
|
|
"'%s'\n", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (X509_check_private_key(cert->crt, cert->key) != 1) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Cert does not match key in PEM file "
|
|
|
|
"'%s':\n", filename);
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
cert_free(cert);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_CERTIFICATE
|
|
|
|
log_dbg_printf("Loaded '%s':\n", filename);
|
|
|
|
log_dbg_print_free(ssl_x509_to_str(cert->crt));
|
|
|
|
log_dbg_print_free(ssl_x509_to_pem(cert->crt));
|
|
|
|
#endif /* DEBUG_CERTIFICATE */
|
|
|
|
return cert;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_opts_t *
|
|
|
|
conn_opts_new(void)
|
|
|
|
{
|
|
|
|
conn_opts_t *conn_opts = malloc(sizeof(conn_opts_t));
|
|
|
|
if (!conn_opts)
|
|
|
|
return oom_return_na_null();
|
|
|
|
memset(conn_opts, 0, sizeof(conn_opts_t));
|
|
|
|
|
|
|
|
conn_opts->sslcomp = 1;
|
|
|
|
conn_opts->chain = sk_X509_new_null();
|
|
|
|
conn_opts->sslmethod = SSLv23_method;
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
conn_opts->minsslversion = TLS1_VERSION;
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
conn_opts->maxsslversion = TLS1_3_VERSION;
|
|
|
|
#else /* !HAVE_TLSV13 */
|
|
|
|
conn_opts->maxsslversion = TLS1_2_VERSION;
|
|
|
|
#endif /* !HAVE_TLSV13 */
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
conn_opts->remove_http_referer = 1;
|
|
|
|
conn_opts->verify_peer = 1;
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
conn_opts->user_timeout = 300;
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
conn_opts->max_http_header_size = 8192;
|
|
|
|
return conn_opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
opts_t *
|
|
|
|
opts_new(void)
|
|
|
|
{
|
|
|
|
opts_t *opts;
|
|
|
|
|
|
|
|
opts = malloc(sizeof(opts_t));
|
|
|
|
if (!opts)
|
|
|
|
return oom_return_na_null();
|
|
|
|
memset(opts, 0, sizeof(opts_t));
|
|
|
|
|
|
|
|
opts->divert = 1;
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
global_t *
|
|
|
|
global_new(void)
|
|
|
|
{
|
|
|
|
global_t *global;
|
|
|
|
|
|
|
|
global = malloc(sizeof(global_t));
|
|
|
|
if (!global)
|
|
|
|
return oom_return_na_null();
|
|
|
|
memset(global, 0, sizeof(global_t));
|
|
|
|
|
|
|
|
global->leafkey_rsabits = DFLT_LEAFKEY_RSABITS;
|
|
|
|
global->conn_idle_timeout = 120;
|
|
|
|
global->expired_conn_check_period = 10;
|
|
|
|
global->stats_period = 1;
|
|
|
|
|
|
|
|
global->conn_opts = conn_opts_new();
|
|
|
|
if (!global->conn_opts)
|
|
|
|
return NULL;
|
|
|
|
global->opts = opts_new();
|
|
|
|
if (!global->opts)
|
|
|
|
return NULL;
|
|
|
|
global->opts->global = global;
|
|
|
|
return global;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
conn_opts_free(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
if (conn_opts->clientcrt) {
|
|
|
|
X509_free(conn_opts->clientcrt);
|
|
|
|
}
|
|
|
|
if (conn_opts->clientkey) {
|
|
|
|
EVP_PKEY_free(conn_opts->clientkey);
|
|
|
|
}
|
|
|
|
if (conn_opts->cacrt) {
|
|
|
|
X509_free(conn_opts->cacrt);
|
|
|
|
}
|
|
|
|
if (conn_opts->cakey) {
|
|
|
|
EVP_PKEY_free(conn_opts->cakey);
|
|
|
|
}
|
|
|
|
if (conn_opts->chain) {
|
|
|
|
sk_X509_pop_free(conn_opts->chain, X509_free);
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (conn_opts->dh) {
|
|
|
|
DH_free(conn_opts->dh);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
if (conn_opts->ecdhcurve) {
|
|
|
|
free(conn_opts->ecdhcurve);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
if (conn_opts->ciphers) {
|
|
|
|
free(conn_opts->ciphers);
|
|
|
|
}
|
|
|
|
if (conn_opts->ciphersuites) {
|
|
|
|
free(conn_opts->ciphersuites);
|
|
|
|
}
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (conn_opts->user_auth_url) {
|
|
|
|
free(conn_opts->user_auth_url);
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
memset(conn_opts, 0, sizeof(conn_opts_t));
|
|
|
|
free(conn_opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_free(opts_t *opts)
|
|
|
|
{
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
filter_userlist_free(opts->divertusers);
|
|
|
|
filter_userlist_free(opts->passusers);
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
// No need to call filter_macro_free() or filter_rules_free() here,
|
|
|
|
// macros and filter rules are freed after setting filter rules during startup
|
|
|
|
filter_free(opts);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
memset(opts, 0, sizeof(opts_t));
|
|
|
|
free(opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
spec_addrs_free(spec_addrs_t *spec_addrs)
|
|
|
|
{
|
|
|
|
if (spec_addrs->addr)
|
|
|
|
free(spec_addrs->addr);
|
|
|
|
if (spec_addrs->divert_addr)
|
|
|
|
free(spec_addrs->divert_addr);
|
|
|
|
if (spec_addrs->target_addr)
|
|
|
|
free(spec_addrs->target_addr);
|
|
|
|
memset(spec_addrs, 0, sizeof(spec_addrs_t));
|
|
|
|
free(spec_addrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear and free a proxy spec.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
proxyspec_free(proxyspec_t *spec)
|
|
|
|
{
|
|
|
|
if (spec->conn_opts)
|
|
|
|
conn_opts_free(spec->conn_opts);
|
|
|
|
if (spec->opts)
|
|
|
|
opts_free(spec->opts);
|
|
|
|
if (spec->natengine)
|
|
|
|
free(spec->natengine);
|
|
|
|
memset(spec, 0, sizeof(proxyspec_t));
|
|
|
|
free(spec);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear and free all proxy specs.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
global_proxyspec_free(proxyspec_t *spec)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
proxyspec_t *next = spec->next;
|
|
|
|
proxyspec_free(spec);
|
|
|
|
spec = next;
|
|
|
|
} while (spec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tmp_opts_free(tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (tmp_opts->cacrt_str) {
|
|
|
|
free(tmp_opts->cacrt_str);
|
|
|
|
tmp_opts->cacrt_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->cakey_str) {
|
|
|
|
free(tmp_opts->cakey_str);
|
|
|
|
tmp_opts->cakey_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->chain_str) {
|
|
|
|
free(tmp_opts->chain_str);
|
|
|
|
tmp_opts->chain_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->clientcrt_str) {
|
|
|
|
free(tmp_opts->clientcrt_str);
|
|
|
|
tmp_opts->clientcrt_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->clientkey_str) {
|
|
|
|
free(tmp_opts->clientkey_str);
|
|
|
|
tmp_opts->clientkey_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->leafcrlurl_str) {
|
|
|
|
free(tmp_opts->leafcrlurl_str);
|
|
|
|
tmp_opts->leafcrlurl_str = NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts->dh_str) {
|
|
|
|
free(tmp_opts->dh_str);
|
|
|
|
tmp_opts->dh_str = NULL;
|
|
|
|
}
|
|
|
|
free(tmp_opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_free(global_t *global)
|
|
|
|
{
|
|
|
|
if (global->spec) {
|
|
|
|
global_proxyspec_free(global->spec);
|
|
|
|
}
|
|
|
|
if (global->leafcertdir) {
|
|
|
|
free(global->leafcertdir);
|
|
|
|
}
|
|
|
|
if (global->defaultleafcert) {
|
|
|
|
cert_free(global->defaultleafcert);
|
|
|
|
}
|
|
|
|
if (global->dropuser) {
|
|
|
|
free(global->dropuser);
|
|
|
|
}
|
|
|
|
if (global->dropgroup) {
|
|
|
|
free(global->dropgroup);
|
|
|
|
}
|
|
|
|
if (global->jaildir) {
|
|
|
|
free(global->jaildir);
|
|
|
|
}
|
|
|
|
if (global->pidfile) {
|
|
|
|
free(global->pidfile);
|
|
|
|
}
|
|
|
|
if (global->conffile) {
|
|
|
|
free(global->conffile);
|
|
|
|
}
|
|
|
|
if (global->connectlog) {
|
|
|
|
free(global->connectlog);
|
|
|
|
}
|
|
|
|
if (global->contentlog) {
|
|
|
|
free(global->contentlog);
|
|
|
|
}
|
|
|
|
if (global->certgendir) {
|
|
|
|
free(global->certgendir);
|
|
|
|
}
|
|
|
|
if (global->contentlog_basedir) {
|
|
|
|
free(global->contentlog_basedir);
|
|
|
|
}
|
|
|
|
if (global->masterkeylog) {
|
|
|
|
free(global->masterkeylog);
|
|
|
|
}
|
|
|
|
if (global->pcaplog) {
|
|
|
|
free(global->pcaplog);
|
|
|
|
}
|
|
|
|
if (global->pcaplog_basedir) {
|
|
|
|
free(global->pcaplog_basedir);
|
|
|
|
}
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
if (global->mirrorif) {
|
|
|
|
free(global->mirrorif);
|
|
|
|
}
|
|
|
|
if (global->mirrortarget) {
|
|
|
|
free(global->mirrortarget);
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (global->userdb_path) {
|
|
|
|
free(global->userdb_path);
|
|
|
|
}
|
|
|
|
if (global->userdb) {
|
|
|
|
// sqlite3.h: "Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op."
|
|
|
|
sqlite3_finalize(global->update_user_atime);
|
|
|
|
sqlite3_close(global->userdb);
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
if (global->conn_opts) {
|
|
|
|
conn_opts_free(global->conn_opts);
|
|
|
|
}
|
|
|
|
if (global->opts) {
|
|
|
|
opts_free(global->opts);
|
|
|
|
}
|
|
|
|
if (global->leafkey) {
|
|
|
|
EVP_PKEY_free(global->leafkey);
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
if (global->openssl_engine) {
|
|
|
|
free(global->openssl_engine);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ENGINE */
|
|
|
|
|
|
|
|
memset(global, 0, sizeof(global_t));
|
|
|
|
free(global);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 1 if global_t contains a proxyspec that (eventually) uses SSL/TLS,
|
|
|
|
* 0 otherwise. When 0, it is safe to assume that no SSL/TLS operations
|
|
|
|
* will take place with this configuration.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
global_has_ssl_spec(global_t *global)
|
|
|
|
{
|
|
|
|
proxyspec_t *p = global->spec;
|
|
|
|
while (p) {
|
|
|
|
if (p->ssl || p->upgrade)
|
|
|
|
return 1;
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 1 if global_t contains a proxyspec with dns, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
global_has_dns_spec(global_t *global)
|
|
|
|
{
|
|
|
|
proxyspec_t *p = global->spec;
|
|
|
|
while (p) {
|
|
|
|
if (p->dns)
|
|
|
|
return 1;
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
/*
|
|
|
|
* Return 1 if global_t contains a proxyspec with user_auth, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
global_has_userauth_spec(global_t *global)
|
|
|
|
{
|
|
|
|
proxyspec_t *p = global->spec;
|
|
|
|
while (p) {
|
|
|
|
if (p->conn_opts->user_auth)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
filter_rule_t *rule = p->opts->filter_rules;
|
|
|
|
while (rule) {
|
|
|
|
if (rule->action.conn_opts)
|
|
|
|
if (rule->action.conn_opts->user_auth)
|
|
|
|
return 1;
|
|
|
|
rule = rule->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 1 if global_t contains a proxyspec with cakey defined, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
global_has_cakey_spec(global_t *global)
|
|
|
|
{
|
|
|
|
proxyspec_t *p = global->spec;
|
|
|
|
while (p) {
|
|
|
|
if (p->conn_opts->cakey)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
filter_rule_t *rule = p->opts->filter_rules;
|
|
|
|
while (rule) {
|
|
|
|
if (rule->action.conn_opts)
|
|
|
|
if (rule->action.conn_opts->cakey)
|
|
|
|
return 1;
|
|
|
|
rule = rule->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
static int WUNRES
|
|
|
|
opts_set_user_auth_url(conn_opts_t *conn_opts, const char * argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (conn_opts->user_auth_url)
|
|
|
|
free(conn_opts->user_auth_url);
|
|
|
|
conn_opts->user_auth_url = strdup(optarg);
|
|
|
|
if (!conn_opts->user_auth_url)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("UserAuthURL: %s\n", conn_opts->user_auth_url);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
tmp_opts_t *
|
|
|
|
tmp_opts_copy(tmp_opts_t *src_tmp_opts)
|
|
|
|
{
|
|
|
|
tmp_opts_t *tmp_opts = malloc(sizeof(tmp_opts_t));
|
|
|
|
if (!tmp_opts)
|
|
|
|
return oom_return_na_null();
|
|
|
|
memset(tmp_opts, 0, sizeof(tmp_opts_t));
|
|
|
|
|
|
|
|
if (src_tmp_opts->cacrt_str)
|
|
|
|
tmp_opts->cacrt_str = strdup(src_tmp_opts->cacrt_str);
|
|
|
|
if (src_tmp_opts->cakey_str)
|
|
|
|
tmp_opts->cakey_str = strdup(src_tmp_opts->cakey_str);
|
|
|
|
if (src_tmp_opts->chain_str)
|
|
|
|
tmp_opts->chain_str = strdup(src_tmp_opts->chain_str);
|
|
|
|
if (src_tmp_opts->clientcrt_str)
|
|
|
|
tmp_opts->clientcrt_str = strdup(src_tmp_opts->clientcrt_str);
|
|
|
|
if (src_tmp_opts->clientkey_str)
|
|
|
|
tmp_opts->clientkey_str = strdup(src_tmp_opts->clientkey_str);
|
|
|
|
if (src_tmp_opts->leafcrlurl_str)
|
|
|
|
tmp_opts->leafcrlurl_str = strdup(src_tmp_opts->leafcrlurl_str);
|
|
|
|
if (src_tmp_opts->dh_str)
|
|
|
|
tmp_opts->dh_str = strdup(src_tmp_opts->dh_str);
|
|
|
|
tmp_opts->split = src_tmp_opts->split;
|
|
|
|
tmp_opts->include = src_tmp_opts->include;
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
tmp_opts->line_num = src_tmp_opts->line_num;
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
|
|
|
|
return tmp_opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_opts_t *
|
|
|
|
conn_opts_copy(conn_opts_t *conn_opts, const char *argv0, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Copy conn_opts\n");
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
|
|
|
|
conn_opts_t *cops = conn_opts_new();
|
|
|
|
if (!cops)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
cops->sslcomp = conn_opts->sslcomp;
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
cops->no_ssl2 = conn_opts->no_ssl2;
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
cops->no_ssl3 = conn_opts->no_ssl3;
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
cops->no_tls10 = conn_opts->no_tls10;
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
cops->no_tls11 = conn_opts->no_tls11;
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
cops->no_tls12 = conn_opts->no_tls12;
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
cops->no_tls13 = conn_opts->no_tls13;
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
cops->passthrough = conn_opts->passthrough;
|
|
|
|
cops->deny_ocsp = conn_opts->deny_ocsp;
|
|
|
|
cops->sslmethod = conn_opts->sslmethod;
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
cops->sslversion = conn_opts->sslversion;
|
|
|
|
cops->minsslversion = conn_opts->minsslversion;
|
|
|
|
cops->maxsslversion = conn_opts->maxsslversion;
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
cops->remove_http_accept_encoding = conn_opts->remove_http_accept_encoding;
|
|
|
|
cops->remove_http_referer = conn_opts->remove_http_referer;
|
|
|
|
cops->verify_peer = conn_opts->verify_peer;
|
|
|
|
cops->allow_wrong_host = conn_opts->allow_wrong_host;
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
cops->user_auth = conn_opts->user_auth;
|
|
|
|
cops->user_timeout = conn_opts->user_timeout;
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
cops->validate_proto = conn_opts->validate_proto;
|
|
|
|
cops->reconnect_ssl = conn_opts->reconnect_ssl;
|
|
|
|
cops->max_http_header_size = conn_opts->max_http_header_size;
|
|
|
|
|
|
|
|
// Pass NULL as tmp_opts param, so we don't reassign the var to itself
|
|
|
|
// That would be harmless but incorrect
|
|
|
|
if (tmp_opts && tmp_opts->chain_str) {
|
|
|
|
if (opts_set_chain(cops, argv0, tmp_opts->chain_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts && tmp_opts->leafcrlurl_str) {
|
|
|
|
if (opts_set_leafcrlurl(cops, argv0, tmp_opts->leafcrlurl_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts && tmp_opts->cacrt_str) {
|
|
|
|
if (opts_set_cacrt(cops, argv0, tmp_opts->cacrt_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts && tmp_opts->cakey_str) {
|
|
|
|
if (opts_set_cakey(cops, argv0, tmp_opts->cakey_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts && tmp_opts->clientcrt_str) {
|
|
|
|
if (opts_set_clientcrt(cops, argv0, tmp_opts->clientcrt_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (tmp_opts && tmp_opts->clientkey_str) {
|
|
|
|
if (opts_set_clientkey(cops, argv0, tmp_opts->clientkey_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (tmp_opts && tmp_opts->dh_str) {
|
|
|
|
if (opts_set_dh(cops, argv0, tmp_opts->dh_str, NULL) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
if (conn_opts->ecdhcurve) {
|
|
|
|
if (opts_set_ecdhcurve(cops, argv0, conn_opts->ecdhcurve) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
if (conn_opts->ciphers) {
|
|
|
|
if (opts_set_ciphers(cops, argv0, conn_opts->ciphers) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (conn_opts->ciphersuites) {
|
|
|
|
if (opts_set_ciphersuites(cops, argv0, conn_opts->ciphersuites) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (conn_opts->user_auth_url) {
|
|
|
|
if (opts_set_user_auth_url(cops, argv0, conn_opts->user_auth_url) == -1)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
return cops;
|
|
|
|
}
|
|
|
|
|
|
|
|
static opts_t * WUNRES
|
|
|
|
global_opts_copy(global_t *global, const char *argv0, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Copy global opts\n");
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
|
|
|
|
opts_t *opts = opts_new();
|
|
|
|
if (!opts)
|
|
|
|
return NULL;
|
|
|
|
opts->global = global;
|
|
|
|
|
|
|
|
opts->divert = global->opts->divert;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (filter_userlist_copy(global->opts->divertusers, argv0, &opts->divertusers) == -1)
|
|
|
|
return oom_return_null(argv0);
|
|
|
|
|
|
|
|
if (filter_userlist_copy(global->opts->passusers, argv0, &opts->passusers) == -1)
|
|
|
|
return oom_return_null(argv0);
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (filter_macro_copy(global->opts->macro, argv0, opts) == -1)
|
|
|
|
return oom_return_null(argv0);
|
|
|
|
|
|
|
|
if (filter_rule_copy(global->opts->filter_rules, argv0, opts, tmp_opts) == -1)
|
|
|
|
return oom_return_null(argv0);
|
|
|
|
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
proxyspec_t *
|
|
|
|
proxyspec_new(global_t *global, const char *argv0, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
proxyspec_t *spec = malloc(sizeof(proxyspec_t));
|
|
|
|
if (!spec)
|
|
|
|
return oom_return_null(argv0);
|
|
|
|
memset(spec, 0, sizeof(proxyspec_t));
|
|
|
|
spec->conn_opts = conn_opts_copy(global->conn_opts, argv0, tmp_opts);
|
|
|
|
if (!spec->conn_opts)
|
|
|
|
return NULL;
|
|
|
|
spec->opts = global_opts_copy(global, argv0, tmp_opts);
|
|
|
|
if (!spec->opts)
|
|
|
|
return NULL;
|
|
|
|
return spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
proxyspec_set_proto(proxyspec_t *spec, const char *value)
|
|
|
|
{
|
|
|
|
/* Defaults */
|
|
|
|
spec->ssl = 0;
|
|
|
|
spec->http = 0;
|
|
|
|
spec->upgrade = 0;
|
|
|
|
spec->pop3 = 0;
|
|
|
|
spec->smtp = 0;
|
|
|
|
if (!strcmp(value, "tcp")) {
|
|
|
|
/* use defaults */
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "ssl")) {
|
|
|
|
spec->ssl = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "http")) {
|
|
|
|
spec->http = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "https")) {
|
|
|
|
spec->ssl = 1;
|
|
|
|
spec->http = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "autossl")) {
|
|
|
|
spec->upgrade = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "pop3")) {
|
|
|
|
spec->pop3 = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "pop3s")) {
|
|
|
|
spec->ssl = 1;
|
|
|
|
spec->pop3 = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "smtp")) {
|
|
|
|
spec->smtp = 1;
|
|
|
|
} else
|
|
|
|
if (!strcmp(value, "smtps")) {
|
|
|
|
spec->ssl = 1;
|
|
|
|
spec->smtp = 1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unknown connection "
|
|
|
|
"type '%s'\n", value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Proto: %s\n", value);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_listen_addr(proxyspec_t *spec, char *addr, char *port, const char *natengine)
|
|
|
|
{
|
|
|
|
int af = sys_sockaddr_parse(&spec->listen_addr,
|
|
|
|
&spec->listen_addrlen,
|
|
|
|
addr, port,
|
|
|
|
sys_get_af(addr),
|
|
|
|
EVUTIL_AI_PASSIVE);
|
|
|
|
if (af == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (natengine) {
|
|
|
|
spec->natengine = strdup(natengine);
|
|
|
|
if (!spec->natengine)
|
|
|
|
return oom_return_na();
|
|
|
|
} else {
|
|
|
|
spec->natengine = NULL;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Addr: [%s]:%s, %s\n", addr, port, natengine);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return af;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_divert(opts_t *opts)
|
|
|
|
{
|
|
|
|
opts->divert = 1;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Divert: yes\n");
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_unset_divert(opts_t *opts)
|
|
|
|
{
|
|
|
|
opts->divert = 0;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Divert: no\n");
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_divert_addr(proxyspec_t *spec, char *addr, char *port)
|
|
|
|
{
|
|
|
|
if (sys_sockaddr_parse(&spec->divert_addr,
|
|
|
|
&spec->divert_addrlen,
|
|
|
|
addr, port, AF_INET, EVUTIL_AI_PASSIVE) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("DivertAddr: [%s]:%s\n", addr, port);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_return_addr(proxyspec_t *spec, char *addr)
|
|
|
|
{
|
|
|
|
if (sys_sockaddr_parse(&spec->return_addr,
|
|
|
|
&spec->return_addrlen,
|
|
|
|
addr, "0", AF_INET, EVUTIL_AI_PASSIVE) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ReturnAddr: [%s]\n", addr);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_target_addr(proxyspec_t *spec, char *addr, char *port, int af)
|
|
|
|
{
|
|
|
|
if (sys_sockaddr_parse(&spec->connect_addr,
|
|
|
|
&spec->connect_addrlen,
|
|
|
|
addr, port, af, 0) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* explicit target address */
|
|
|
|
if (spec->natengine) {
|
|
|
|
free(spec->natengine);
|
|
|
|
spec->natengine = NULL;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("TargetAddr: [%s]:%s\n", addr, port);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_sni_port(proxyspec_t *spec, char *port)
|
|
|
|
{
|
|
|
|
if (!spec->ssl) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"SNI hostname lookup "
|
|
|
|
"only works for ssl "
|
|
|
|
"and https proxyspecs"
|
|
|
|
"\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* SNI dstport */
|
|
|
|
spec->sni_port = atoi(port);
|
|
|
|
if (!spec->sni_port) {
|
|
|
|
fprintf(stderr, "Invalid port '%s'\n", port);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
spec->dns = 1;
|
|
|
|
if (spec->natengine) {
|
|
|
|
free(spec->natengine);
|
|
|
|
spec->natengine = NULL;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("SNIPort: %u\n", spec->sni_port);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
proxyspec_set_natengine(proxyspec_t *spec, const char *natengine)
|
|
|
|
{
|
|
|
|
// Double checks if called by proxyspec_parse()
|
|
|
|
if (nat_exist(natengine)) {
|
|
|
|
if (spec->natengine)
|
|
|
|
free(spec->natengine);
|
|
|
|
spec->natengine = strdup(natengine);
|
|
|
|
if (!spec->natengine) {
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "No such nat engine '%s'\n", natengine);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("NatEngine: %s\n", spec->natengine);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_divert(proxyspec_t *spec, int split)
|
|
|
|
{
|
|
|
|
// The global divert option -n has precedence over the proxyspec Divert option
|
|
|
|
// Use split mode if no divert address is specified, even if the Divert option is used
|
|
|
|
// The Divert option in structured proxyspecs has precedence over the divert address option
|
|
|
|
// If the Divert option is not used in structured proxyspecs, use the global Divert option
|
|
|
|
if (split || !spec->divert_addrlen) {
|
|
|
|
opts_unset_divert(spec->opts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse proxyspecs using a simple state machine.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
proxyspec_parse(int *argc, char **argv[], const char *natengine, global_t *global, const char *argv0, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
proxyspec_t *spec = NULL;
|
|
|
|
char *addr = NULL;
|
|
|
|
int state = 0;
|
|
|
|
int af;
|
|
|
|
|
|
|
|
while ((*argc)--) {
|
|
|
|
switch (state) {
|
|
|
|
default:
|
|
|
|
case 0:
|
|
|
|
/* tcp | ssl | http | https | autossl | pop3 | pop3s | smtp | smtps */
|
|
|
|
spec = proxyspec_new(global, argv0, tmp_opts);
|
|
|
|
if (!spec)
|
|
|
|
return -1;
|
|
|
|
spec->next = global->spec;
|
|
|
|
global->spec = spec;
|
|
|
|
|
|
|
|
if (proxyspec_set_proto(spec, **argv) == -1)
|
|
|
|
return -1;
|
|
|
|
state++;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* listenaddr */
|
|
|
|
addr = **argv;
|
|
|
|
state++;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* listenport */
|
|
|
|
if ((af = proxyspec_set_listen_addr(spec, addr, **argv, natengine)) == -1)
|
|
|
|
return -1;
|
|
|
|
state++;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
state++;
|
|
|
|
if (strstr(**argv, "up:")) {
|
|
|
|
char *dp = **argv + 3;
|
|
|
|
// @todo IPv6?
|
|
|
|
char *da = "127.0.0.1";
|
|
|
|
char *ra = "127.0.0.1";
|
|
|
|
|
|
|
|
// da and ra are optional, if both specified, da should come before ra
|
|
|
|
// Divert address
|
|
|
|
if (*argc && strstr(*((*argv) + 1), "ua:")) {
|
|
|
|
(*argv)++; (*argc)--;
|
|
|
|
da = **argv + 3;
|
|
|
|
}
|
|
|
|
// Return address
|
|
|
|
if (*argc && strstr(*((*argv) + 1), "ra:")) {
|
|
|
|
(*argv)++; (*argc)--;
|
|
|
|
ra = **argv + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proxyspec_set_divert_addr(spec, da, dp) == -1)
|
|
|
|
return -1;
|
|
|
|
if (proxyspec_set_return_addr(spec, ra) == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall-through */
|
|
|
|
case 4:
|
|
|
|
/* [ natengine | dstaddr ] */
|
|
|
|
if (!strcmp(**argv, "tcp") ||
|
|
|
|
!strcmp(**argv, "ssl") ||
|
|
|
|
!strcmp(**argv, "http") ||
|
|
|
|
!strcmp(**argv, "https") ||
|
|
|
|
!strcmp(**argv, "autossl") ||
|
|
|
|
!strcmp(**argv, "pop3") ||
|
|
|
|
!strcmp(**argv, "pop3s") ||
|
|
|
|
!strcmp(**argv, "smtp") ||
|
|
|
|
!strcmp(**argv, "smtps")) {
|
|
|
|
/* implicit default natengine */
|
|
|
|
(*argv)--; (*argc)++; /* rewind */
|
|
|
|
state = 0;
|
|
|
|
} else
|
|
|
|
if (!strcmp(**argv, "sni")) {
|
|
|
|
state = 6;
|
|
|
|
} else
|
|
|
|
if (nat_exist(**argv)) {
|
|
|
|
/* natengine */
|
|
|
|
if (proxyspec_set_natengine(spec, **argv) == -1)
|
|
|
|
return -1;
|
|
|
|
state = 0;
|
|
|
|
} else {
|
|
|
|
/* explicit target address */
|
|
|
|
addr = **argv;
|
|
|
|
state++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
/* explicit target port */
|
|
|
|
if (proxyspec_set_target_addr(spec, addr, **argv, af) == -1)
|
|
|
|
return -1;
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* SNI dstport */
|
|
|
|
if (proxyspec_set_sni_port(spec, **argv) == -1)
|
|
|
|
return -1;
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*argv)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state != 0 && state != 3 && state != 4) {
|
|
|
|
fprintf(stderr, "Incomplete proxyspec!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Empty line does not create new spec
|
|
|
|
if (spec)
|
|
|
|
set_divert(spec, tmp_opts->split);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
conn_opts_str(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if (!conn_opts) {
|
|
|
|
s = strdup("");
|
|
|
|
if (!s)
|
|
|
|
return oom_return_na_null();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asprintf(&s, "conn opts: %s%s%s%s%s%s%s%s%s%s"
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
"%s"
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
"%s%s|%s|%s"
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
"|%s"
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
"|%s%s%s%s%s"
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
"%s|%s|%d"
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
"%s%s|%d",
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20702000L)
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
(conn_opts->sslmethod == SSLv2_method) ? "ssl2" :
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
(conn_opts->sslmethod == SSLv3_method) ? "ssl3" :
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
(conn_opts->sslmethod == TLSv1_method) ? "tls10" :
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
(conn_opts->sslmethod == TLSv1_1_method) ? "tls11" :
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
(conn_opts->sslmethod == TLSv1_2_method) ? "tls12" :
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
/* There is no TLSv1_3_method defined,
|
|
|
|
* since no ssl version < 0x10100000L supports it. */
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
(conn_opts->sslversion == SSL3_VERSION) ? "ssl3" :
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
(conn_opts->sslversion == TLS1_VERSION) ? "tls10" :
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
(conn_opts->sslversion == TLS1_1_VERSION) ? "tls11" :
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
(conn_opts->sslversion == TLS1_2_VERSION) ? "tls12" :
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
(conn_opts->sslversion == TLS1_3_VERSION) ? "tls13" :
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
"negotiate",
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
conn_opts->no_ssl2 ? " -ssl2" :
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
conn_opts->no_ssl3 ? " -ssl3" :
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
conn_opts->no_tls10 ? " -tls10" :
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
conn_opts->no_tls11 ? " -tls11" :
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
conn_opts->no_tls12 ? " -tls12" :
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
conn_opts->no_tls13 ? " -tls13" :
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
"",
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
(conn_opts->minsslversion == SSL3_VERSION) ? ">=ssl3" :
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
(conn_opts->minsslversion == TLS1_VERSION) ? ">=tls10" :
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
(conn_opts->minsslversion == TLS1_1_VERSION) ? ">=tls11" :
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
(conn_opts->minsslversion == TLS1_2_VERSION) ? ">=tls12" :
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
(conn_opts->minsslversion == TLS1_3_VERSION) ? ">=tls13" :
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
"",
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
(conn_opts->maxsslversion == SSL3_VERSION) ? "<=ssl3" :
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
(conn_opts->maxsslversion == TLS1_VERSION) ? "<=tls10" :
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
(conn_opts->maxsslversion == TLS1_1_VERSION) ? "<=tls11" :
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
(conn_opts->maxsslversion == TLS1_2_VERSION) ? "<=tls12" :
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
(conn_opts->maxsslversion == TLS1_3_VERSION) ? "<=tls13" :
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
"",
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
|
|
|
|
"", "",
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
|
|
|
|
(!conn_opts->sslcomp ? "|no sslcomp" : ""),
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
(conn_opts->no_ssl2 ? "|no_ssl2" : ""),
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
(conn_opts->no_ssl3 ? "|no_ssl3" : ""),
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
(conn_opts->no_tls10 ? "|no_tls10" : ""),
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
(conn_opts->no_tls11 ? "|no_tls11" : ""),
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
(conn_opts->no_tls12 ? "|no_tls12" : ""),
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
(conn_opts->no_tls13 ? "|no_tls13" : ""),
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
(conn_opts->passthrough ? "|passthrough" : ""),
|
|
|
|
(conn_opts->deny_ocsp ? "|deny_ocsp" : ""),
|
|
|
|
(conn_opts->ciphers ? conn_opts->ciphers : "no ciphers"),
|
|
|
|
(conn_opts->ciphersuites ? conn_opts->ciphersuites : "no ciphersuites"),
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
(conn_opts->ecdhcurve ? conn_opts->ecdhcurve : "no ecdhcurve"),
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
(conn_opts->leafcrlurl ? conn_opts->leafcrlurl : "no leafcrlurl"),
|
|
|
|
(conn_opts->remove_http_accept_encoding ? "|remove_http_accept_encoding" : ""),
|
|
|
|
(conn_opts->remove_http_referer ? "|remove_http_referer" : ""),
|
|
|
|
(conn_opts->verify_peer ? "|verify_peer" : ""),
|
|
|
|
(conn_opts->allow_wrong_host ? "|allow_wrong_host" : ""),
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
(conn_opts->user_auth ? "|user_auth" : ""),
|
|
|
|
(conn_opts->user_auth_url ? conn_opts->user_auth_url : "no user_auth_url"),
|
|
|
|
conn_opts->user_timeout,
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
(conn_opts->validate_proto ? "|validate_proto" : ""),
|
|
|
|
(conn_opts->reconnect_ssl ? "|reconnect_ssl" : ""),
|
|
|
|
conn_opts->max_http_header_size
|
|
|
|
) < 0) {
|
|
|
|
return oom_return_na_null();
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
opts_str(opts_t *opts, conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
char *s = NULL;
|
|
|
|
char *copts_str = NULL;
|
|
|
|
char *ms = NULL;
|
|
|
|
char *frs = NULL;
|
|
|
|
char *fs = NULL;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
char *du = NULL;
|
|
|
|
char *pu = NULL;
|
|
|
|
|
|
|
|
du = filter_userlist_str(opts->divertusers);
|
|
|
|
if (!du)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pu = filter_userlist_str(opts->passusers);
|
|
|
|
if (!pu)
|
|
|
|
goto out;
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
copts_str = conn_opts_str(conn_opts);
|
|
|
|
if (!copts_str)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ms = filter_macro_str(opts->macro);
|
|
|
|
if (!ms)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
frs = filter_rule_str(opts->filter_rules);
|
|
|
|
if (!frs)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
fs = filter_str(opts->filter);
|
|
|
|
if (!fs)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (asprintf(&s, "opts= %s\n%s"
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
"|%s|%s"
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
"%s%s%s%s%s%s",
|
|
|
|
copts_str,
|
|
|
|
(opts->divert ? "divert" : "split"),
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
du,
|
|
|
|
pu,
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
strlen(ms) ? "\n" : "", ms,
|
|
|
|
strlen(frs) ? "\n" : "", frs,
|
|
|
|
(!strlen(frs) && strlen(fs)) ? "\n" : "", fs) < 0) {
|
|
|
|
s = NULL;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (du)
|
|
|
|
free(du);
|
|
|
|
if (pu)
|
|
|
|
free(pu);
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
if (copts_str)
|
|
|
|
free(copts_str);
|
|
|
|
if (ms)
|
|
|
|
free(ms);
|
|
|
|
if (frs)
|
|
|
|
free(frs);
|
|
|
|
if (fs)
|
|
|
|
free(fs);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return text representation of proxy spec for display to the user.
|
|
|
|
* Returned string must be freed by caller.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
proxyspec_str(proxyspec_t *spec)
|
|
|
|
{
|
|
|
|
char *s = NULL;
|
|
|
|
char *lhbuf = NULL;
|
|
|
|
char *lpbuf = NULL;
|
|
|
|
char *cbuf = NULL;
|
|
|
|
char *pdstbuf = NULL;
|
|
|
|
char *csrcbuf = NULL;
|
|
|
|
char *optsstr = NULL;
|
|
|
|
|
|
|
|
if (sys_sockaddr_str((struct sockaddr *)&spec->listen_addr,
|
|
|
|
spec->listen_addrlen, &lhbuf, &lpbuf) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (spec->connect_addrlen) {
|
|
|
|
char *chbuf, *cpbuf;
|
|
|
|
if (sys_sockaddr_str((struct sockaddr *)&spec->connect_addr,
|
|
|
|
spec->connect_addrlen,
|
|
|
|
&chbuf, &cpbuf) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
int rv = asprintf(&cbuf, "\nconnect= [%s]:%s", chbuf, cpbuf);
|
|
|
|
free(chbuf);
|
|
|
|
free(cpbuf);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (spec->divert_addrlen) {
|
|
|
|
char *chbuf, *cpbuf;
|
|
|
|
if (sys_sockaddr_str((struct sockaddr *)&spec->divert_addr,
|
|
|
|
spec->divert_addrlen,
|
|
|
|
&chbuf, &cpbuf) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
int rv = asprintf(&pdstbuf, "\ndivert addr= [%s]:%s", chbuf, cpbuf);
|
|
|
|
free(chbuf);
|
|
|
|
free(cpbuf);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (spec->return_addrlen) {
|
|
|
|
char *chbuf, *cpbuf;
|
|
|
|
if (sys_sockaddr_str((struct sockaddr *)&spec->return_addr,
|
|
|
|
spec->return_addrlen,
|
|
|
|
&chbuf, &cpbuf) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
int rv = asprintf(&csrcbuf, "\nreturn addr= [%s]:%s", chbuf, cpbuf);
|
|
|
|
free(chbuf);
|
|
|
|
free(cpbuf);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (spec->sni_port) {
|
|
|
|
if (asprintf(&cbuf, "\nsni %i", spec->sni_port) < 0) {
|
|
|
|
goto out;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
}
|
|
|
|
optsstr = opts_str(spec->opts, spec->conn_opts);
|
|
|
|
if (!optsstr) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (asprintf(&s, "listen=[%s]:%s %s%s%s%s%s %s%s%s\n%s%s", lhbuf, lpbuf,
|
|
|
|
(spec->ssl ? "ssl" : "tcp"),
|
|
|
|
(spec->upgrade ? "|autossl" : ""),
|
|
|
|
(spec->http ? "|http" : ""),
|
|
|
|
(spec->pop3 ? "|pop3" : ""),
|
|
|
|
(spec->smtp ? "|smtp" : ""),
|
|
|
|
(spec->natengine ? spec->natengine : cbuf),
|
|
|
|
STRORNONE(pdstbuf),
|
|
|
|
STRORNONE(csrcbuf),
|
|
|
|
optsstr,
|
|
|
|
!spec->opts->divert && spec->divert_addrlen ? "\nWARNING: Divert address specified in split mode" : "") < 0) {
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
s = NULL;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (optsstr)
|
|
|
|
free(optsstr);
|
|
|
|
if (lhbuf)
|
|
|
|
free(lhbuf);
|
|
|
|
if (lpbuf)
|
|
|
|
free(lpbuf);
|
|
|
|
if (cbuf)
|
|
|
|
free(cbuf);
|
|
|
|
if (pdstbuf)
|
|
|
|
free(pdstbuf);
|
|
|
|
if (csrcbuf)
|
|
|
|
free(csrcbuf);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_cacrt(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->cacrt_str)
|
|
|
|
free(tmp_opts->cacrt_str);
|
|
|
|
tmp_opts->cacrt_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->cacrt_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_opts->cacrt)
|
|
|
|
X509_free(conn_opts->cacrt);
|
|
|
|
conn_opts->cacrt = ssl_x509_load(optarg);
|
|
|
|
if (!conn_opts->cacrt) {
|
|
|
|
fprintf(stderr, "%s: error loading CA cert from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ssl_x509_refcount_inc(conn_opts->cacrt);
|
|
|
|
sk_X509_insert(conn_opts->chain, conn_opts->cacrt, 0);
|
|
|
|
if (!conn_opts->cakey) {
|
|
|
|
conn_opts->cakey = ssl_key_load(optarg);
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (!conn_opts->dh) {
|
|
|
|
conn_opts->dh = ssl_dh_load(optarg);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("CACert: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_cakey(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->cakey_str)
|
|
|
|
free(tmp_opts->cakey_str);
|
|
|
|
tmp_opts->cakey_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->cakey_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
if (conn_opts->cakey)
|
|
|
|
EVP_PKEY_free(conn_opts->cakey);
|
|
|
|
conn_opts->cakey = ssl_key_load(optarg);
|
|
|
|
if (!conn_opts->cakey) {
|
|
|
|
fprintf(stderr, "%s: error loading CA key from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
return -1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
if (!conn_opts->cacrt) {
|
|
|
|
conn_opts->cacrt = ssl_x509_load(optarg);
|
|
|
|
if (conn_opts->cacrt) {
|
|
|
|
ssl_x509_refcount_inc(conn_opts->cacrt);
|
|
|
|
sk_X509_insert(conn_opts->chain, conn_opts->cacrt, 0);
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (!conn_opts->dh) {
|
|
|
|
conn_opts->dh = ssl_dh_load(optarg);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("CAKey: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_chain(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->chain_str)
|
|
|
|
free(tmp_opts->chain_str);
|
|
|
|
tmp_opts->chain_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->chain_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
if (ssl_x509chain_load(NULL, &conn_opts->chain, optarg) == -1) {
|
|
|
|
fprintf(stderr, "%s: error loading chain from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
return -1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("CAChain: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_leafcrlurl(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->leafcrlurl_str)
|
|
|
|
free(tmp_opts->leafcrlurl_str);
|
|
|
|
tmp_opts->leafcrlurl_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->leafcrlurl_str)
|
|
|
|
return oom_return(argv0);
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_opts->leafcrlurl)
|
|
|
|
free(conn_opts->leafcrlurl);
|
|
|
|
conn_opts->leafcrlurl = strdup(optarg);
|
|
|
|
if (!conn_opts->leafcrlurl)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LeafCRLURL: %s\n", conn_opts->leafcrlurl);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
set_certgendir(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->certgendir)
|
|
|
|
free(global->certgendir);
|
|
|
|
global->certgendir = strdup(optarg);
|
|
|
|
if (!global->certgendir)
|
|
|
|
return oom_return(argv0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_set_deny_ocsp(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->deny_ocsp = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_deny_ocsp(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->deny_ocsp = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_set_passthrough(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->passthrough = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_unset_passthrough(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->passthrough = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_clientcrt(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->clientcrt_str)
|
|
|
|
free(tmp_opts->clientcrt_str);
|
|
|
|
tmp_opts->clientcrt_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->clientcrt_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_opts->clientcrt)
|
|
|
|
X509_free(conn_opts->clientcrt);
|
|
|
|
conn_opts->clientcrt = ssl_x509_load(optarg);
|
|
|
|
if (!conn_opts->clientcrt) {
|
|
|
|
fprintf(stderr, "%s: error loading client cert from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ClientCert: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_clientkey(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->clientkey_str)
|
|
|
|
free(tmp_opts->clientkey_str);
|
|
|
|
tmp_opts->clientkey_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->clientkey_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_opts->clientkey)
|
|
|
|
EVP_PKEY_free(conn_opts->clientkey);
|
|
|
|
conn_opts->clientkey = ssl_key_load(optarg);
|
|
|
|
if (!conn_opts->clientkey) {
|
|
|
|
fprintf(stderr, "%s: error loading client key from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ClientKey: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
int
|
|
|
|
opts_set_dh(conn_opts_t *conn_opts, const char *argv0, const char *optarg, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (tmp_opts) {
|
|
|
|
if (tmp_opts->dh_str)
|
|
|
|
free(tmp_opts->dh_str);
|
|
|
|
tmp_opts->dh_str = strdup(optarg);
|
|
|
|
if (!tmp_opts->dh_str)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn_opts->dh)
|
|
|
|
DH_free(conn_opts->dh);
|
|
|
|
conn_opts->dh = ssl_dh_load(optarg);
|
|
|
|
if (!conn_opts->dh) {
|
|
|
|
fprintf(stderr, "%s: error loading DH params from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("DHGroupParams: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
int
|
|
|
|
opts_set_ecdhcurve(conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
EC_KEY *ec;
|
|
|
|
if (conn_opts->ecdhcurve)
|
|
|
|
free(conn_opts->ecdhcurve);
|
|
|
|
if (!(ec = ssl_ec_by_name(optarg))) {
|
|
|
|
fprintf(stderr, "%s: unknown curve '%s'\n", argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
EC_KEY_free(ec);
|
|
|
|
conn_opts->ecdhcurve = strdup(optarg);
|
|
|
|
if (!conn_opts->ecdhcurve)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ECDHCurve: %s\n", conn_opts->ecdhcurve);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
|
|
static void
|
|
|
|
opts_set_sslcomp(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->sslcomp = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
opts_unset_sslcomp(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->sslcomp = 0;
|
|
|
|
}
|
|
|
|
#endif /* SSL_OP_NO_COMPRESSION */
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_ciphers(conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (conn_opts->ciphers)
|
|
|
|
free(conn_opts->ciphers);
|
|
|
|
conn_opts->ciphers = strdup(optarg);
|
|
|
|
if (!conn_opts->ciphers)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Ciphers: %s\n", conn_opts->ciphers);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
opts_set_ciphersuites(conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (conn_opts->ciphersuites)
|
|
|
|
free(conn_opts->ciphersuites);
|
|
|
|
conn_opts->ciphersuites = strdup(optarg);
|
|
|
|
if (!conn_opts->ciphersuites)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("CipherSuites: %s\n", conn_opts->ciphersuites);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse SSL proto string in optarg and look up the corresponding SSL method.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
opts_force_proto(conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20702000L)
|
|
|
|
if (conn_opts->sslmethod != SSLv23_method) {
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
if (conn_opts->sslversion) {
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
fprintf(stderr, "%s: overriding -r ssl version option\n", argv0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20702000L)
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
if (!strcmp(optarg, "ssl2")) {
|
|
|
|
conn_opts->sslmethod = SSLv2_method;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (!strcmp(optarg, "ssl3")) {
|
|
|
|
conn_opts->sslmethod = SSLv3_method;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) {
|
|
|
|
conn_opts->sslmethod = TLSv1_method;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (!strcmp(optarg, "tls11")) {
|
|
|
|
conn_opts->sslmethod = TLSv1_1_method;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (!strcmp(optarg, "tls12")) {
|
|
|
|
conn_opts->sslmethod = TLSv1_2_method;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
/* There is no TLSv1_3_method defined,
|
|
|
|
* since no ssl version < 0x10100000L supports it. */
|
|
|
|
#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
/*
|
|
|
|
* Support for SSLv2 and the corresponding SSLv2_method(),
|
|
|
|
* SSLv2_server_method() and SSLv2_client_method() functions were
|
|
|
|
* removed in OpenSSL 1.1.0.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (!strcmp(optarg, "ssl3")) {
|
|
|
|
conn_opts->sslversion = SSL3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) {
|
|
|
|
conn_opts->sslversion = TLS1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (!strcmp(optarg, "tls11")) {
|
|
|
|
conn_opts->sslversion = TLS1_1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (!strcmp(optarg, "tls12")) {
|
|
|
|
conn_opts->sslversion = TLS1_2_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
if (!strcmp(optarg, "tls13")) {
|
|
|
|
conn_opts->sslversion = TLS1_3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ForceSSLProto: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse SSL proto string in optarg and set the corresponding no_foo bit.
|
|
|
|
* action: 1 for disable, 0 for enable.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
opts_disable_enable_proto(conn_opts_t *conn_opts, const char *argv0, const char *optarg, int action)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_SSLV2
|
|
|
|
if (!strcmp(optarg, "ssl2")) {
|
|
|
|
conn_opts->no_ssl2 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV2 */
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (!strcmp(optarg, "ssl3")) {
|
|
|
|
conn_opts->no_ssl3 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) {
|
|
|
|
conn_opts->no_tls10 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (!strcmp(optarg, "tls11")) {
|
|
|
|
conn_opts->no_tls11 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (!strcmp(optarg, "tls12")) {
|
|
|
|
conn_opts->no_tls12 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
if (!strcmp(optarg, "tls13")) {
|
|
|
|
conn_opts->no_tls13 = action;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
if (action)
|
|
|
|
log_dbg_printf("DisableSSLProto: %s\n", optarg);
|
|
|
|
else
|
|
|
|
log_dbg_printf("EnableSSLProto: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
opts_set_min_proto(UNUSED conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (!strcmp(optarg, "ssl3")) {
|
|
|
|
conn_opts->minsslversion = SSL3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) {
|
|
|
|
conn_opts->minsslversion = TLS1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (!strcmp(optarg, "tls11")) {
|
|
|
|
conn_opts->minsslversion = TLS1_1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (!strcmp(optarg, "tls12")) {
|
|
|
|
conn_opts->minsslversion = TLS1_2_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
if (!strcmp(optarg, "tls13")) {
|
|
|
|
conn_opts->minsslversion = TLS1_3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MinSSLProto: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
opts_set_max_proto(UNUSED conn_opts_t *conn_opts, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x20702000L)
|
|
|
|
#ifdef HAVE_SSLV3
|
|
|
|
if (!strcmp(optarg, "ssl3")) {
|
|
|
|
conn_opts->maxsslversion = SSL3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_SSLV3 */
|
|
|
|
#ifdef HAVE_TLSV10
|
|
|
|
if (!strcmp(optarg, "tls10") || !strcmp(optarg, "tls1")) {
|
|
|
|
conn_opts->maxsslversion = TLS1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV10 */
|
|
|
|
#ifdef HAVE_TLSV11
|
|
|
|
if (!strcmp(optarg, "tls11")) {
|
|
|
|
conn_opts->maxsslversion = TLS1_1_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV11 */
|
|
|
|
#ifdef HAVE_TLSV12
|
|
|
|
if (!strcmp(optarg, "tls12")) {
|
|
|
|
conn_opts->maxsslversion = TLS1_2_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV12 */
|
|
|
|
#ifdef HAVE_TLSV13
|
|
|
|
if (!strcmp(optarg, "tls13")) {
|
|
|
|
conn_opts->maxsslversion = TLS1_3_VERSION;
|
|
|
|
} else
|
|
|
|
#endif /* HAVE_TLSV13 */
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: Unsupported SSL/TLS protocol '%s'\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MaxSSLProto: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_remove_http_accept_encoding(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->remove_http_accept_encoding = 1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_remove_http_accept_encoding(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->remove_http_accept_encoding = 0;
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_remove_http_referer(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->remove_http_referer = 1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_remove_http_referer(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->remove_http_referer = 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_verify_peer(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->verify_peer = 1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_verify_peer(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->verify_peer = 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_allow_wrong_host(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->allow_wrong_host = 1;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_allow_wrong_host(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->allow_wrong_host = 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
static void
|
|
|
|
opts_set_user_auth(UNUSED conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
#if defined(__OpenBSD__) || defined(__linux__)
|
|
|
|
// Enable user auth on OpenBSD and Linux only
|
|
|
|
conn_opts->user_auth = 1;
|
|
|
|
#endif /* __OpenBSD__ || __linux__ */
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_user_auth(conn_opts_t *conn_opts)
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
{
|
|
|
|
conn_opts->user_auth = 0;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
static void
|
|
|
|
opts_set_validate_proto(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->validate_proto = 1;
|
|
|
|
}
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
|
|
|
|
static void
|
|
|
|
opts_unset_validate_proto(conn_opts_t *conn_opts)
|
|
|
|
{
|
|
|
|
conn_opts->validate_proto = 0;
|
Restructure passsite filter data structure
Now we don't go over all of the passsite rules in a linked list trying
to apply passsite to the sni or common names of a conn. Instead, we now
have user+keyword, keyword, ip, and all lists. For example, if we find
the conn user in the user+keyword list and a passsite in that list
matches, we don't look into other lists.
This change is expected to improve the performance of passsite
processing considerably, because in the earlier implementation we had to
go over all of the passsite rules trying to match passsite.
And this solution uses a correct data structure, even if not the best.
For example, each user or keyword in passsite rules is strdup()'ed only
once.
Note that a better solution could use, say, a hash table for users,
instead of a linked list. But hash tables are not suitable for keywords
or sites, because we search for substring matches with them, not exact
matches.
Also, this fixes passsite rules without any filters defined, i.e. to be
applied to all connections.
Also, now e2e tests error exit if WITHOUT_USERAUTH is enabled. E2e tests
require UserAuth enabled.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_leafkey(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->leafkey)
|
|
|
|
EVP_PKEY_free(global->leafkey);
|
|
|
|
global->leafkey = ssl_key_load(optarg);
|
|
|
|
if (!global->leafkey) {
|
|
|
|
fprintf(stderr, "%s: error loading leaf key from '%s':\n",
|
|
|
|
argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
if (!global->conn_opts->dh) {
|
|
|
|
global->conn_opts->dh = ssl_dh_load(optarg);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LeafKey: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
int
|
|
|
|
global_set_openssl_engine(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->openssl_engine)
|
|
|
|
free(global->openssl_engine);
|
|
|
|
global->openssl_engine = strdup(optarg);
|
|
|
|
if (!global->openssl_engine)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("OpenSSLEngine: %s\n", global->openssl_engine);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_ENGINE */
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_leafcertdir(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isdir(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not a directory\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->leafcertdir)
|
|
|
|
free(global->leafcertdir);
|
|
|
|
global->leafcertdir = strdup(optarg);
|
|
|
|
if (!global->leafcertdir)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LeafCertDir: %s\n", global->leafcertdir);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_defaultleafcert(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->defaultleafcert)
|
|
|
|
cert_free(global->defaultleafcert);
|
|
|
|
global->defaultleafcert = opts_load_cert_chain_key(optarg);
|
|
|
|
if (!global->defaultleafcert) {
|
|
|
|
fprintf(stderr, "%s: error loading default leaf cert/chain/key"
|
|
|
|
" from '%s':\n", argv0, optarg);
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("DefaultLeafCert: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_certgendir_writegencerts(global_t *global, const char *argv0,
|
|
|
|
const char *optarg)
|
|
|
|
{
|
|
|
|
global->certgen_writeall = 0;
|
|
|
|
if (set_certgendir(global, argv0, optarg) == -1)
|
|
|
|
return -1;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("WriteGenCertsDir: certgendir=%s, writeall=%u\n",
|
|
|
|
global->certgendir, global->certgen_writeall);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_certgendir_writeall(global_t *global, const char *argv0,
|
|
|
|
const char *optarg)
|
|
|
|
{
|
|
|
|
global->certgen_writeall = 1;
|
|
|
|
if (set_certgendir(global, argv0, optarg) == -1)
|
|
|
|
return -1;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("WriteAllCertsDir: certgendir=%s, writeall=%u\n",
|
|
|
|
global->certgendir, global->certgen_writeall);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_user(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isuser(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not an existing user\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->dropuser)
|
|
|
|
free(global->dropuser);
|
|
|
|
global->dropuser = strdup(optarg);
|
|
|
|
if (!global->dropuser)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("User: %s\n", global->dropuser);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_group(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isgroup(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not an existing group\n",
|
|
|
|
argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->dropgroup)
|
|
|
|
free(global->dropgroup);
|
|
|
|
global->dropgroup = strdup(optarg);
|
|
|
|
if (!global->dropgroup)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Group: %s\n", global->dropgroup);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_jaildir(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isdir(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->jaildir)
|
|
|
|
free(global->jaildir);
|
|
|
|
global->jaildir = realpath(optarg, NULL);
|
|
|
|
if (!global->jaildir) {
|
|
|
|
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
|
|
|
argv0, optarg, strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Chroot: %s\n", global->jaildir);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_pidfile(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->pidfile)
|
|
|
|
free(global->pidfile);
|
|
|
|
global->pidfile = strdup(optarg);
|
|
|
|
if (!global->pidfile)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("PidFile: %s\n", global->pidfile);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_connectlog(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->connectlog)
|
|
|
|
free(global->connectlog);
|
|
|
|
if (!(global->connectlog = sys_realdir(optarg))) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
fprintf(stderr, "Directory part of '%s' does not "
|
|
|
|
"exist\n", optarg);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
|
|
|
optarg, strerror(errno), errno);
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ConnectLog: %s\n", global->connectlog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_contentlog(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->contentlog)
|
|
|
|
free(global->contentlog);
|
|
|
|
if (!(global->contentlog = sys_realdir(optarg))) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
fprintf(stderr, "Directory part of '%s' does not "
|
|
|
|
"exist\n", optarg);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
|
|
|
optarg, strerror(errno), errno);
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
global->contentlog_isdir = 0;
|
|
|
|
global->contentlog_isspec = 0;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ContentLog: %s\n", global->contentlog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_contentlogdir(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isdir(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->contentlog)
|
|
|
|
free(global->contentlog);
|
|
|
|
global->contentlog = realpath(optarg, NULL);
|
|
|
|
if (!global->contentlog) {
|
|
|
|
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
|
|
|
argv0, optarg, strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
global->contentlog_isdir = 1;
|
|
|
|
global->contentlog_isspec = 0;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ContentLogDir: %s\n", global->contentlog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
global_set_logbasedir(const char *argv0, const char *optarg,
|
|
|
|
char **basedir, char **log)
|
|
|
|
{
|
|
|
|
char *lhs, *rhs, *p, *q;
|
|
|
|
size_t n;
|
|
|
|
if (*basedir)
|
|
|
|
free(*basedir);
|
|
|
|
if (*log)
|
|
|
|
free(*log);
|
|
|
|
if (log_content_split_pathspec(optarg, &lhs, &rhs) == -1) {
|
|
|
|
fprintf(stderr, "%s: Failed to split '%s' in lhs/rhs:"
|
|
|
|
" %s (%i)\n", argv0, optarg,
|
|
|
|
strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* eliminate %% from lhs */
|
|
|
|
for (p = q = lhs; *p; p++, q++) {
|
|
|
|
if (q < p)
|
|
|
|
*q = *p;
|
|
|
|
if (*p == '%' && *(p+1) == '%')
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
/* all %% in lhs resolved to % */
|
|
|
|
if (sys_mkpath(lhs, 0777) == -1) {
|
|
|
|
fprintf(stderr, "%s: Failed to create '%s': %s (%i)\n",
|
|
|
|
argv0, lhs, strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*basedir = realpath(lhs, NULL);
|
|
|
|
if (!*basedir) {
|
|
|
|
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
|
|
|
argv0, lhs, strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* count '%' in basedir */
|
|
|
|
for (n = 0, p = *basedir;
|
|
|
|
*p;
|
|
|
|
p++) {
|
|
|
|
if (*p == '%')
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
free(lhs);
|
|
|
|
n += strlen(*basedir);
|
|
|
|
if (!(lhs = malloc(n + 1)))
|
|
|
|
return oom_return(argv0);
|
|
|
|
/* re-encoding % to %%, copying basedir to lhs */
|
|
|
|
for (p = *basedir, q = lhs;
|
|
|
|
*p;
|
|
|
|
p++, q++) {
|
|
|
|
*q = *p;
|
|
|
|
if (*q == '%')
|
|
|
|
*(++q) = '%';
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
/* lhs contains encoded realpathed basedir */
|
|
|
|
if (asprintf(log, "%s/%s", lhs, rhs) < 0)
|
|
|
|
return oom_return(argv0);
|
|
|
|
free(lhs);
|
|
|
|
free(rhs);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_contentlogpathspec(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global_set_logbasedir(argv0, optarg, &global->contentlog_basedir, &global->contentlog) == -1)
|
|
|
|
return -1;
|
|
|
|
global->contentlog_isdir = 0;
|
|
|
|
global->contentlog_isspec = 1;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ContentLogPathSpec: basedir=%s, %s\n",
|
|
|
|
global->contentlog_basedir, global->contentlog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
void
|
|
|
|
global_set_lprocinfo(global_t *global)
|
|
|
|
{
|
|
|
|
global->lprocinfo = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_unset_lprocinfo(global_t *global)
|
|
|
|
{
|
|
|
|
global->lprocinfo = 0;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_masterkeylog(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->masterkeylog)
|
|
|
|
free(global->masterkeylog);
|
|
|
|
if (!(global->masterkeylog = sys_realdir(optarg))) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
fprintf(stderr, "Directory part of '%s' does not "
|
|
|
|
"exist\n", optarg);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
|
|
|
optarg, strerror(errno), errno);
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MasterKeyLog: %s\n", global->masterkeylog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_pcaplog(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->pcaplog)
|
|
|
|
free(global->pcaplog);
|
|
|
|
if (!(global->pcaplog = sys_realdir(optarg))) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
fprintf(stderr, "Directory part of '%s' does not "
|
|
|
|
"exist\n", optarg);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
|
|
|
|
optarg, strerror(errno), errno);
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
global->pcaplog_isdir = 0;
|
|
|
|
global->pcaplog_isspec = 0;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("PcapLog: %s\n", global->pcaplog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_pcaplogdir(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (!sys_isdir(optarg)) {
|
|
|
|
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (global->pcaplog)
|
|
|
|
free(global->pcaplog);
|
|
|
|
global->pcaplog = realpath(optarg, NULL);
|
|
|
|
if (!global->pcaplog) {
|
|
|
|
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
|
|
|
|
argv0, optarg, strerror(errno), errno);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
global->pcaplog_isdir = 1;
|
|
|
|
global->pcaplog_isspec = 0;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("PcapLogDir: %s\n", global->pcaplog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_pcaplogpathspec(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global_set_logbasedir(argv0, optarg, &global->pcaplog_basedir, &global->pcaplog) == -1)
|
|
|
|
return -1;
|
|
|
|
global->pcaplog_isdir = 0;
|
|
|
|
global->pcaplog_isspec = 1;
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("PcapLogPathSpec: basedir=%s, %s\n",
|
|
|
|
global->pcaplog_basedir, global->pcaplog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
int
|
|
|
|
global_set_mirrorif(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->mirrorif)
|
|
|
|
free(global->mirrorif);
|
|
|
|
global->mirrorif = strdup(optarg);
|
|
|
|
if (!global->mirrorif)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MirrorIf: %s\n", global->mirrorif);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_mirrortarget(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
if (global->mirrortarget)
|
|
|
|
free(global->mirrortarget);
|
|
|
|
global->mirrortarget = strdup(optarg);
|
|
|
|
if (!global->mirrortarget)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MirrorTarget: %s\n", global->mirrortarget);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
|
|
|
|
void
|
|
|
|
global_set_daemon(global_t *global)
|
|
|
|
{
|
|
|
|
global->detach = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_unset_daemon(global_t *global)
|
|
|
|
{
|
|
|
|
global->detach = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_set_debug(global_t *global)
|
|
|
|
{
|
|
|
|
log_dbg_mode(LOG_DBG_MODE_ERRLOG);
|
|
|
|
global->debug = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_unset_debug(global_t *global)
|
|
|
|
{
|
|
|
|
log_dbg_mode(LOG_DBG_MODE_NONE);
|
|
|
|
global->debug = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_debug_level(const char *optarg)
|
|
|
|
{
|
|
|
|
if (equal(optarg, "2")) {
|
|
|
|
log_dbg_mode(LOG_DBG_MODE_FINE);
|
|
|
|
} else if (equal(optarg, "3")) {
|
|
|
|
log_dbg_mode(LOG_DBG_MODE_FINER);
|
|
|
|
} else if (equal(optarg, "4")) {
|
|
|
|
log_dbg_mode(LOG_DBG_MODE_FINEST);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid DebugLevel '%s', use 2-4\n", optarg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("DebugLevel: %s\n", optarg);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_set_statslog(global_t *global)
|
|
|
|
{
|
|
|
|
global->statslog = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
global_unset_statslog(global_t *global)
|
|
|
|
{
|
|
|
|
global->statslog = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
static int WUNRES
|
|
|
|
global_set_userdb_path(global_t *global, const char *argv0, const char *optarg)
|
|
|
|
{
|
|
|
|
FILE *fd = fopen(optarg, "r");
|
|
|
|
if (!fd) {
|
|
|
|
log_err_level_printf(LOG_CRIT, "Error opening UserDB file '%s': %s\n", optarg, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fclose(fd);
|
|
|
|
|
|
|
|
if (global->userdb_path)
|
|
|
|
free(global->userdb_path);
|
|
|
|
global->userdb_path = strdup(optarg);
|
|
|
|
if (!global->userdb_path)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("UserDBPath: %s\n", global->userdb_path);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
int
|
|
|
|
is_yesno(const char *value)
|
|
|
|
{
|
|
|
|
if (equal(value, "yes"))
|
|
|
|
return 1;
|
|
|
|
else if (equal(value, "no"))
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
check_value_yesno(const char *value, const char *name, unsigned int line_num)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
if ((rv = is_yesno(value)) == -1)
|
|
|
|
fprintf(stderr, "Error in conf: Invalid '%s' value '%s' on line %d, use yes|no\n", name, value, line_num);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
set_conn_opts_option(conn_opts_t *conn_opts, const char *argv0,
|
|
|
|
const char *name, char *value, unsigned int line_num, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
int yes;
|
|
|
|
|
|
|
|
if (!value || !strlen(value)) {
|
|
|
|
fprintf(stderr, "Error in conf: No value assigned for %s on line %d\n", name, line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (equal(name, "CACert")) {
|
|
|
|
return opts_set_cacrt(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "CAKey")) {
|
|
|
|
return opts_set_cakey(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "ClientCert")) {
|
|
|
|
return opts_set_clientcrt(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "ClientKey")) {
|
|
|
|
return opts_set_clientkey(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "CAChain")) {
|
|
|
|
return opts_set_chain(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "LeafCRLURL")) {
|
|
|
|
return opts_set_leafcrlurl(conn_opts, argv0, value, tmp_opts);
|
|
|
|
} else if (equal(name, "DenyOCSP")) {
|
|
|
|
yes = check_value_yesno(value, "DenyOCSP", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_deny_ocsp(conn_opts) : opts_unset_deny_ocsp(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("DenyOCSP: %u\n", conn_opts->deny_ocsp);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "Passthrough")) {
|
|
|
|
yes = check_value_yesno(value, "Passthrough", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_passthrough(conn_opts) : opts_unset_passthrough(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Passthrough: %u\n", conn_opts->passthrough);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
} else if (equal(name, "DHGroupParams")) {
|
|
|
|
return opts_set_dh(conn_opts, argv0, value, tmp_opts);
|
|
|
|
#endif /* !OPENSSL_NO_DH */
|
|
|
|
#ifndef OPENSSL_NO_ECDH
|
|
|
|
} else if (equal(name, "ECDHCurve")) {
|
|
|
|
return opts_set_ecdhcurve(conn_opts, argv0, value);
|
|
|
|
#endif /* !OPENSSL_NO_ECDH */
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
|
|
} else if (equal(name, "SSLCompression")) {
|
|
|
|
yes = check_value_yesno(value, "SSLCompression", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_sslcomp(conn_opts) : opts_unset_sslcomp(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("SSLCompression: %u\n", conn_opts->sslcomp);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#endif /* SSL_OP_NO_COMPRESSION */
|
|
|
|
} else if (equal(name, "ForceSSLProto")) {
|
|
|
|
return opts_force_proto(conn_opts, argv0, value);
|
|
|
|
} else if (equal(name, "DisableSSLProto")) {
|
|
|
|
return opts_disable_enable_proto(conn_opts, argv0, value, 1);
|
|
|
|
} else if (equal(name, "EnableSSLProto")) {
|
|
|
|
return opts_disable_enable_proto(conn_opts, argv0, value, 0);
|
|
|
|
} else if (equal(name, "MinSSLProto")) {
|
|
|
|
return opts_set_min_proto(conn_opts, argv0, value);
|
|
|
|
} else if (equal(name, "MaxSSLProto")) {
|
|
|
|
return opts_set_max_proto(conn_opts, argv0, value);
|
|
|
|
} else if (equal(name, "Ciphers")) {
|
|
|
|
return opts_set_ciphers(conn_opts, argv0, value);
|
|
|
|
} else if (equal(name, "CipherSuites")) {
|
|
|
|
return opts_set_ciphersuites(conn_opts, argv0, value);
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
} else if (equal(name, "UserAuth")) {
|
|
|
|
yes = check_value_yesno(value, "UserAuth", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_user_auth(conn_opts) : opts_unset_user_auth(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("UserAuth: %u\n", conn_opts->user_auth);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "UserAuthURL")) {
|
|
|
|
return opts_set_user_auth_url(conn_opts, argv0, value);
|
|
|
|
} else if (equal(name, "UserTimeout")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i <= 86400) {
|
|
|
|
conn_opts->user_timeout = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid UserTimeout %s on line %d, use 0-86400\n", value, line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("UserTimeout: %u\n", conn_opts->user_timeout);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
} else if (equal(name, "ValidateProto")) {
|
|
|
|
yes = check_value_yesno(value, "ValidateProto", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_validate_proto(conn_opts) : opts_unset_validate_proto(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ValidateProto: %u\n", conn_opts->validate_proto);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "MaxHTTPHeaderSize")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i >= 1024 && i <= 65536) {
|
|
|
|
conn_opts->max_http_header_size = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid MaxHTTPHeaderSize %s on line %d, use 1024-65536\n", value, line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("MaxHTTPHeaderSize: %u\n", conn_opts->max_http_header_size);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "VerifyPeer")) {
|
|
|
|
yes = check_value_yesno(value, "VerifyPeer", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_verify_peer(conn_opts) : opts_unset_verify_peer(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("VerifyPeer: %u\n", conn_opts->verify_peer);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "AllowWrongHost")) {
|
|
|
|
yes = check_value_yesno(value, "AllowWrongHost", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_allow_wrong_host(conn_opts) : opts_unset_allow_wrong_host(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("AllowWrongHost: %u\n", conn_opts->allow_wrong_host);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "RemoveHTTPAcceptEncoding")) {
|
|
|
|
yes = check_value_yesno(value, "RemoveHTTPAcceptEncoding", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_remove_http_accept_encoding(conn_opts) : opts_unset_remove_http_accept_encoding(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("RemoveHTTPAcceptEncoding: %u\n", conn_opts->remove_http_accept_encoding);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "RemoveHTTPReferer")) {
|
|
|
|
yes = check_value_yesno(value, "RemoveHTTPReferer", line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? opts_set_remove_http_referer(conn_opts) : opts_unset_remove_http_referer(conn_opts);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("RemoveHTTPReferer: %u\n", conn_opts->remove_http_referer);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Unknown conn_opts option, but may not be an error, so return 1, instead of -1
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* tmp_opts param is used to save certain opts, so that we can use them
|
|
|
|
* to copy global opts to proxyspecs, or proxyspecs opts to struct filtering rules
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
set_option(opts_t *opts, conn_opts_t *conn_opts, const char *argv0,
|
|
|
|
const char *name, char *value, char **natengine, FILE *f, unsigned int *line_num, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
int yes;
|
|
|
|
|
|
|
|
if (!value || !strlen(value)) {
|
|
|
|
fprintf(stderr, "Error in conf: No value assigned for %s on line %d\n", name, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (equal(name, "NATEngine")) {
|
|
|
|
if (*natengine)
|
|
|
|
free(*natengine);
|
|
|
|
*natengine = strdup(value);
|
|
|
|
if (!*natengine)
|
|
|
|
return oom_return(argv0);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("NATEngine: %s\n", *natengine);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
} else if (equal(name, "DivertUsers")) {
|
|
|
|
return filter_userlist_set(value, *line_num, &opts->divertusers, "DivertUsers");
|
|
|
|
} else if (equal(name, "PassUsers")) {
|
|
|
|
return filter_userlist_set(value, *line_num, &opts->passusers, "PassUsers");
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
} else if (equal(name, "PassSite")) {
|
|
|
|
return filter_passsite_set(opts, conn_opts, value, *line_num);
|
|
|
|
} else if (equal(name, "Define")) {
|
|
|
|
return filter_macro_set(opts, value, *line_num);
|
|
|
|
} else if (equal(name, "Split") || equal(name, "Pass") || equal(name, "Block") || equal(name, "Match")) {
|
|
|
|
return filter_rule_set(opts, conn_opts, name, value, *line_num);
|
|
|
|
} else if (equal(name, "Divert")) {
|
|
|
|
yes = is_yesno(value);
|
|
|
|
if (yes == -1)
|
|
|
|
return filter_rule_set(opts, conn_opts, name, value, *line_num);
|
|
|
|
else
|
|
|
|
yes ? opts_set_divert(opts) : opts_unset_divert(opts);
|
|
|
|
} else if (equal(name, "FilterRule") && equal(value, "{")) {
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("FilterRule { on line %d\n", *line_num);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return load_filterrule_struct(opts, conn_opts, argv0, line_num, f, tmp_opts);
|
|
|
|
} else {
|
|
|
|
int rv = set_conn_opts_option(conn_opts, argv0, name, value, *line_num, tmp_opts);
|
|
|
|
if (rv == -1) {
|
|
|
|
fprintf(stderr, "Error in conf: '%s' on line %d\n", name, *line_num);
|
|
|
|
return -1;
|
|
|
|
} else if (rv == 1) {
|
|
|
|
fprintf(stderr, "Error in conf: Unknown option '%s' on line %d\n", name, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
set_proxyspec_option(proxyspec_t *spec, const char *argv0,
|
|
|
|
const char *name, char *value, char **natengine, spec_addrs_t *spec_addrs, FILE *f, unsigned int *line_num, tmp_opts_t *proxyspec_tmp_opts)
|
|
|
|
{
|
|
|
|
// Closing brace '}' is the only option without a value
|
|
|
|
// and only allowed in structured proxyspecs and filter rules
|
|
|
|
if ((!value || !strlen(value)) && !equal(name, "}")) {
|
|
|
|
fprintf(stderr, "Error in conf: No value assigned for %s on line %d\n", name, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (equal(name, "Proto")) {
|
|
|
|
if (proxyspec_set_proto(spec, value) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (equal(name, "Addr")) {
|
|
|
|
spec_addrs->addr = strdup(value);
|
|
|
|
if (!spec_addrs->addr)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
else if (equal(name, "Port")) {
|
|
|
|
if (spec_addrs->addr) {
|
|
|
|
spec_addrs->af = proxyspec_set_listen_addr(spec, spec_addrs->addr, value, *natengine);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "ProxySpec Port without Addr on line %d\n", *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (equal(name, "DivertAddr")) {
|
|
|
|
spec_addrs->divert_addr = strdup(value);
|
|
|
|
if (!spec_addrs->divert_addr)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
else if (equal(name, "DivertPort")) {
|
|
|
|
if (spec_addrs->divert_addr) {
|
|
|
|
if (proxyspec_set_divert_addr(spec, spec_addrs->divert_addr, value) == -1)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (proxyspec_set_divert_addr(spec, "127.0.0.1", value) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (equal(name, "ReturnAddr")) {
|
|
|
|
if (proxyspec_set_return_addr(spec, value) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (equal(name, "TargetAddr")) {
|
|
|
|
spec_addrs->target_addr = strdup(value);
|
|
|
|
if (!spec_addrs->target_addr)
|
|
|
|
return oom_return(argv0);
|
|
|
|
}
|
|
|
|
else if (equal(name, "TargetPort")) {
|
|
|
|
if (spec_addrs->target_addr) {
|
|
|
|
if (proxyspec_set_target_addr(spec, spec_addrs->target_addr, value, spec_addrs->af) == -1)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "ProxySpec TargetPort without TargetAddr on line %d\n", *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (equal(name, "SNIPort")) {
|
|
|
|
if (proxyspec_set_sni_port(spec, value) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (equal(name, "NatEngine")) {
|
|
|
|
if (proxyspec_set_natengine(spec, value) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (equal(name, "}")) {
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ProxySpec } on line %d\n", *line_num);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
if (!spec_addrs->addr || !spec_addrs->af) {
|
|
|
|
fprintf(stderr, "Incomplete ProxySpec on line %d\n", *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Return 2 to indicate the end of structured proxyspec
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return set_option(spec->opts, spec->conn_opts, argv0, name, value, natengine, f, line_num, proxyspec_tmp_opts);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Separator param is needed for command line options only.
|
|
|
|
* Conf file option separator is ' ', on the command line is '='.
|
|
|
|
* Allows multiple separators between name and value.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
get_name_value(char *name, char **value, const char sep, unsigned int line_num)
|
|
|
|
{
|
|
|
|
size_t len = strlen(name);
|
|
|
|
|
|
|
|
// Find end of name and null-terminate
|
|
|
|
char *n = name;
|
|
|
|
while (*n != '\0' && *n != ' ' && *n != '\t' && *n != '\r' && *n != '\n' && *n != sep)
|
|
|
|
n++;
|
|
|
|
*n = '\0';
|
|
|
|
|
|
|
|
size_t name_len = strlen(name);
|
|
|
|
|
|
|
|
if (!name_len) {
|
|
|
|
fprintf(stderr, "Error in option: No option name on line %d\n", line_num);
|
|
|
|
// Return empty value
|
|
|
|
*value = name;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == name_len) {
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Warning in option: No option separator on line %d\n", line_num);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
// Return empty value
|
|
|
|
*value = name + name_len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trim left of value (skip white space and sep until value)
|
|
|
|
do n++;
|
|
|
|
while (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n' || *n == sep);
|
|
|
|
|
|
|
|
*value = n;
|
|
|
|
|
|
|
|
size_t value_len = strlen(*value);
|
|
|
|
|
|
|
|
// Trim inline comment
|
|
|
|
n = *value;
|
|
|
|
while (*n) {
|
|
|
|
if (*n == '#') {
|
|
|
|
*n = '\0';
|
|
|
|
value_len = strlen(*value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!value_len) {
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Warning in option: No option value on line %d\n", line_num);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trim right of value
|
|
|
|
n = *value + value_len - 1;
|
|
|
|
while (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n' || *n == sep)
|
|
|
|
n--;
|
|
|
|
*(n + 1) = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_TOKENS 8
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
load_proxyspec_line(global_t *global, const char *argv0, char *value, char **natengine, unsigned int line_num, tmp_opts_t *global_tmp_opts)
|
|
|
|
{
|
|
|
|
/* Use MAX_TOKENS instead of computing the actual number of tokens in value */
|
|
|
|
char **argv = malloc(sizeof(char *) * MAX_TOKENS);
|
|
|
|
if (!argv)
|
|
|
|
return oom_return(argv0);
|
|
|
|
char **save_argv = argv;
|
|
|
|
int argc = 0;
|
|
|
|
char *p, *last = NULL;
|
|
|
|
|
|
|
|
for ((p = strtok_r(value, " ", &last));
|
|
|
|
p;
|
|
|
|
(p = strtok_r(NULL, " ", &last))) {
|
|
|
|
/* Limit max # token */
|
|
|
|
if (argc < MAX_TOKENS) {
|
|
|
|
argv[argc++] = p;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Too many arguments in proxyspec on line %d\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proxyspec_parse(&argc, &argv, *natengine, global, argv0, global_tmp_opts) == -1) {
|
|
|
|
fprintf(stderr, "Error in proxyspec on line %d\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(save_argv);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
load_proxyspec_struct(global_t *global, const char *argv0, char **natengine, unsigned int *line_num, FILE *f, tmp_opts_t *global_tmp_opts)
|
|
|
|
{
|
|
|
|
int retval = -1;
|
|
|
|
char *name, *value;
|
|
|
|
char *line = NULL;
|
|
|
|
size_t line_len;
|
|
|
|
|
|
|
|
proxyspec_t *spec = proxyspec_new(global, argv0, global_tmp_opts);
|
|
|
|
if (!spec)
|
|
|
|
return -1;
|
|
|
|
spec->next = global->spec;
|
|
|
|
global->spec = spec;
|
|
|
|
|
|
|
|
// Set the default return addr
|
|
|
|
if (proxyspec_set_return_addr(spec, "127.0.0.1") == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
spec_addrs_t *spec_addrs = malloc(sizeof(spec_addrs_t));
|
|
|
|
if (!spec_addrs)
|
|
|
|
return oom_return(argv0);
|
|
|
|
memset(spec_addrs, 0, sizeof(spec_addrs_t));
|
|
|
|
|
|
|
|
tmp_opts_t *proxyspec_tmp_opts = tmp_opts_copy(global_tmp_opts);
|
|
|
|
if (!proxyspec_tmp_opts) {
|
|
|
|
retval = -1;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
int closing_brace = 0;
|
|
|
|
|
|
|
|
while (!feof(f) && !closing_brace) {
|
|
|
|
if (getline(&line, &line_len, f) == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (line == NULL) {
|
|
|
|
fprintf(stderr, "Error in conf file: getline() returns NULL line after line %d\n", *line_num);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
(*line_num)++;
|
|
|
|
|
|
|
|
/* Skip white space */
|
|
|
|
for (name = line; *name == ' ' || *name == '\t'; name++);
|
|
|
|
|
|
|
|
/* Skip comments and empty lines */
|
|
|
|
if ((name[0] == '\0') || (name[0] == '#') || (name[0] == ';') ||
|
|
|
|
(name[0] == '\r') || (name[0] == '\n')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = get_name_value(name, &value, ' ', *line_num);
|
|
|
|
if (retval == 0) {
|
|
|
|
retval = set_proxyspec_option(spec, argv0, name, value, natengine, spec_addrs, f, line_num, proxyspec_tmp_opts);
|
|
|
|
}
|
|
|
|
if (retval == -1) {
|
|
|
|
goto leave;
|
|
|
|
} else if (retval == 2) {
|
|
|
|
closing_brace = 1;
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!closing_brace) {
|
|
|
|
fprintf(stderr, "Error in conf file: struct ProxySpec has no closing brace '}' after line %d\n", *line_num);
|
|
|
|
retval = -1;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_divert(spec, global_tmp_opts->split);
|
|
|
|
|
|
|
|
retval = 0;
|
|
|
|
leave:
|
|
|
|
if (line)
|
|
|
|
free(line);
|
|
|
|
spec_addrs_free(spec_addrs);
|
|
|
|
if (proxyspec_tmp_opts)
|
|
|
|
tmp_opts_free(proxyspec_tmp_opts);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
global_set_open_files_limit(const char *value, unsigned int line_num)
|
|
|
|
{
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i >= 50 && i <= 10000) {
|
|
|
|
struct rlimit rl;
|
|
|
|
rl.rlim_cur = i;
|
|
|
|
rl.rlim_max = i;
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
|
|
|
|
fprintf(stderr, "Failed setting OpenFilesLimit\n");
|
|
|
|
if (errno) {
|
|
|
|
fprintf(stderr, "%s\n", strerror(errno));
|
|
|
|
} else {
|
|
|
|
ERR_print_errors_fp(stderr);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid OpenFilesLimit %s on line %d, use 50-10000\n", value, line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("OpenFilesLimit: %u\n", i);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
opts_load_conffile(global_t *global, const char *argv0, char *conffile, char **natengine, tmp_opts_t *tmp_opts);
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
set_global_option(global_t *global, const char *argv0,
|
|
|
|
const char *name, char *value, char **natengine, unsigned int *line_num, FILE *f, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
int yes;
|
|
|
|
|
|
|
|
if (!value || !strlen(value)) {
|
|
|
|
fprintf(stderr, "Error in conf: No value assigned for %s on line %d\n", name, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (equal(name, "LeafCertDir")) {
|
|
|
|
return global_set_leafcertdir(global, argv0, value);
|
|
|
|
} else if (equal(name, "DefaultLeafCert")) {
|
|
|
|
return global_set_defaultleafcert(global, argv0, value);
|
|
|
|
} else if (equal(name, "WriteGenCertsDir")) {
|
|
|
|
return global_set_certgendir_writegencerts(global, argv0, value);
|
|
|
|
} else if (equal(name, "WriteAllCertsDir")) {
|
|
|
|
return global_set_certgendir_writeall(global, argv0, value);
|
|
|
|
} else if (equal(name, "User")) {
|
|
|
|
return global_set_user(global, argv0, value);
|
|
|
|
} else if (equal(name, "Group")) {
|
|
|
|
return global_set_group(global, argv0, value);
|
|
|
|
} else if (equal(name, "Chroot")) {
|
|
|
|
return global_set_jaildir(global, argv0, value);
|
|
|
|
} else if (equal(name, "PidFile")) {
|
|
|
|
return global_set_pidfile(global, argv0, value);
|
|
|
|
} else if (equal(name, "ConnectLog")) {
|
|
|
|
return global_set_connectlog(global, argv0, value);
|
|
|
|
} else if (equal(name, "ContentLog")) {
|
|
|
|
return global_set_contentlog(global, argv0, value);
|
|
|
|
} else if (equal(name, "ContentLogDir")) {
|
|
|
|
return global_set_contentlogdir(global, argv0, value);
|
|
|
|
} else if (equal(name, "ContentLogPathSpec")) {
|
|
|
|
return global_set_contentlogpathspec(global, argv0, value);
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
} else if (equal(name, "LogProcInfo")) {
|
|
|
|
yes = check_value_yesno(value, "LogProcInfo", *line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? global_set_lprocinfo(global) : global_unset_lprocinfo(global);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LogProcInfo: %u\n", global->lprocinfo);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
} else if (equal(name, "MasterKeyLog")) {
|
|
|
|
return global_set_masterkeylog(global, argv0, value);
|
|
|
|
} else if (equal(name, "PcapLog")) {
|
|
|
|
return global_set_pcaplog(global, argv0, value);
|
|
|
|
} else if (equal(name, "PcapLogDir")) {
|
|
|
|
return global_set_pcaplogdir(global, argv0, value);
|
|
|
|
} else if (equal(name, "PcapLogPathSpec")) {
|
|
|
|
return global_set_pcaplogpathspec(global, argv0, value);
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
} else if (equal(name, "MirrorIf")) {
|
|
|
|
return global_set_mirrorif(global, argv0, value);
|
|
|
|
} else if (equal(name, "MirrorTarget")) {
|
|
|
|
return global_set_mirrortarget(global, argv0, value);
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
} else if (equal(name, "Daemon")) {
|
|
|
|
yes = check_value_yesno(value, "Daemon", *line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? global_set_daemon(global) : global_unset_daemon(global);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Daemon: %u\n", global->detach);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "Debug")) {
|
|
|
|
yes = check_value_yesno(value, "Debug", *line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? global_set_debug(global) : global_unset_debug(global);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Debug: %u\n", global->debug);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "DebugLevel")) {
|
|
|
|
return global_set_debug_level(value);
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
} else if (equal(name, "UserDBPath")) {
|
|
|
|
return global_set_userdb_path(global, argv0, value);
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
} else if (equal(name, "ProxySpec")) {
|
|
|
|
if (equal(value, "{")) {
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ProxySpec { on line %d\n", *line_num);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
return load_proxyspec_struct(global, argv0, natengine, line_num, f, tmp_opts);
|
|
|
|
} else {
|
|
|
|
return load_proxyspec_line(global, argv0, value, natengine, *line_num, tmp_opts);
|
|
|
|
}
|
|
|
|
} else if (equal(name, "ConnIdleTimeout")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i >= 10 && i <= 3600) {
|
|
|
|
global->conn_idle_timeout = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid ConnIdleTimeout %s on line %d, use 10-3600\n", value, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ConnIdleTimeout: %u\n", global->conn_idle_timeout);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "ExpiredConnCheckPeriod")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i >= 10 && i <= 60) {
|
|
|
|
global->expired_conn_check_period = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid ExpiredConnCheckPeriod %s on line %d, use 10-60\n", value, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("ExpiredConnCheckPeriod: %u\n", global->expired_conn_check_period);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "LogStats")) {
|
|
|
|
yes = check_value_yesno(value, "LogStats", *line_num);
|
|
|
|
if (yes == -1)
|
|
|
|
return -1;
|
|
|
|
yes ? global_set_statslog(global) : global_unset_statslog(global);
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LogStats: %u\n", global->statslog);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "StatsPeriod")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i >= 1 && i <= 10) {
|
|
|
|
global->stats_period = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid StatsPeriod %s on line %d, use 1-10\n", value, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("StatsPeriod: %u\n", global->stats_period);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
} else if (equal(name, "OpenFilesLimit")) {
|
|
|
|
return global_set_open_files_limit(value, *line_num);
|
|
|
|
} else if (equal(name, "LeafKey")) {
|
|
|
|
return global_set_leafkey(global, argv0, value);
|
|
|
|
} else if (equal(name, "LeafKeyRSABits")) {
|
|
|
|
unsigned int i = atoi(value);
|
|
|
|
if (i == 1024 || i == 2048 || i == 3072 || i == 4096) {
|
|
|
|
global->leafkey_rsabits = i;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid LeafKeyRSABits %s on line %d, use 1024|2048|3072|4096\n", value, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("LeafKeyRSABits: %u\n", global->leafkey_rsabits);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
} else if (equal(name, "OpenSSLEngine")) {
|
|
|
|
return global_set_openssl_engine(global, argv0, value);
|
|
|
|
#endif /* !OPENSSL_NO_ENGINE */
|
|
|
|
} else if (equal(name, "Include")) {
|
|
|
|
// Prevent infinitely recursive include files
|
|
|
|
if (tmp_opts->include) {
|
|
|
|
fprintf(stderr, "Include option not allowed in include files '%s' on line %d\n", value, *line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_opts->include = 1;
|
|
|
|
int retval = opts_load_conffile(global, argv0, value, natengine, tmp_opts);
|
|
|
|
tmp_opts->include = 0;
|
|
|
|
|
|
|
|
if (retval == -1) {
|
|
|
|
fprintf(stderr, "Error in include file '%s' on line %d\n", value, *line_num);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
} else {
|
|
|
|
return set_option(global->opts, global->conn_opts, argv0, name, value, natengine, f, line_num, tmp_opts);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_set_option(global_t *global, const char *argv0, const char *optarg,
|
|
|
|
char **natengine, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
char *name, *value;
|
|
|
|
int retval = -1;
|
|
|
|
char *line = strdup(optarg);
|
|
|
|
if (!line)
|
|
|
|
return oom_return(argv0);
|
|
|
|
|
|
|
|
/* White spaces possible before option name,
|
|
|
|
* if the command line option is passed between the quotes */
|
|
|
|
for (name = line; *name == ' ' || *name == '\t'; name++);
|
|
|
|
|
|
|
|
/* Command line option separator is '=' */
|
|
|
|
retval = get_name_value(name, &value, '=', 0);
|
|
|
|
if (retval == 0) {
|
|
|
|
/* Line number param is for conf file, pass 0 for command line options */
|
|
|
|
unsigned int line_num = 0;
|
|
|
|
retval = set_global_option(global, argv0, name, value, natengine, &line_num, NULL, tmp_opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line)
|
|
|
|
free(line);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES
|
|
|
|
opts_load_conffile(global_t *global, const char *argv0, char *conffile, char **natengine, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
unsigned int line_num;
|
|
|
|
char *line, *name, *value;
|
|
|
|
size_t line_len;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
#ifdef DEBUG_OPTS
|
|
|
|
log_dbg_printf("Conf file: %s\n", conffile);
|
|
|
|
#endif /* DEBUG_OPTS */
|
|
|
|
|
|
|
|
f = fopen(conffile, "r");
|
|
|
|
if (!f) {
|
|
|
|
fprintf(stderr, "Error opening conf file '%s': %s\n", conffile, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
line = NULL;
|
|
|
|
line_num = 0;
|
|
|
|
retval = -1;
|
|
|
|
while (!feof(f)) {
|
|
|
|
if (getline(&line, &line_len, f) == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (line == NULL) {
|
|
|
|
fprintf(stderr, "Error in conf file: getline() returns NULL line after line %d\n", line_num);
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
line_num++;
|
|
|
|
|
|
|
|
/* Skip white space */
|
|
|
|
for (name = line; *name == ' ' || *name == '\t'; name++);
|
|
|
|
|
|
|
|
/* Skip comments and empty lines */
|
|
|
|
if ((name[0] == '\0') || (name[0] == '#') || (name[0] == ';') ||
|
|
|
|
(name[0] == '\r') || (name[0] == '\n')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = get_name_value(name, &value, ' ', line_num);
|
|
|
|
if (retval == 0) {
|
|
|
|
retval = set_global_option(global, argv0, name, value, natengine, &line_num, f, tmp_opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval == -1) {
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
leave:
|
|
|
|
fclose(f);
|
|
|
|
if (line)
|
|
|
|
free(line);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
global_load_conffile(global_t *global, const char *argv0, const char *optarg, char **natengine, tmp_opts_t *tmp_opts)
|
|
|
|
{
|
|
|
|
if (global->conffile)
|
|
|
|
free(global->conffile);
|
|
|
|
global->conffile = strdup(optarg);
|
|
|
|
if (!global->conffile)
|
|
|
|
return oom_return(argv0);
|
|
|
|
int retval = opts_load_conffile(global, argv0, global->conffile, natengine, tmp_opts);
|
|
|
|
if (retval == -1)
|
|
|
|
fprintf(stderr, "Error in conf file '%s'\n", global->conffile);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim: set noet ft=c: */
|