diff --git a/doc/sample.config b/doc/sample.config index 5f10359e..1da7c283 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -385,7 +385,7 @@ rekey-method = ssl # Script to call when a client connects and obtains an IP. # The following parameters are passed on the environment. -# REASON, USERNAME, GROUPNAME, DEVICE, IP_REAL (the real IP of the client), +# REASON, VHOST, USERNAME, GROUPNAME, DEVICE, IP_REAL (the real IP of the client), # IP_REAL_LOCAL (the local interface IP the client connected), IP_LOCAL # (the local IP in the P-t-P connection), IP_REMOTE (the VPN IP of the client), # IPV6_LOCAL (the IPv6 local address if there are both IPv4 and IPv6 @@ -671,3 +671,20 @@ dtls-legacy = true # and '%{G}', if present will be replaced by the username and group name. #custom-header = "X-My-Header: hi there" + + +# An example virtual host with different authentication methods serviced +# by this server. + +[vhost:www.example.com] +auth = "certificate" + +ca-cert = ../tests/certs/ca.pem + +server-cert = ../tests/certs/server-cert-secp521r1.pem +server-key = ../tests/certs/server-key-secp521r1.pem + +ipv4-network = 192.168.2.0 +ipv4-netmask = 255.255.255.0 + +cert-user-oid = 0.9.2342.19200300.100.1.1 diff --git a/src/Makefile.am b/src/Makefile.am index 96e05271..b65e1fd5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,7 +28,7 @@ AUTH_SOURCES=auth/pam.c auth/pam.h auth/plain.c auth/plain.h auth/radius.c auth/ ACCT_SOURCES=acct/radius.c acct/radius.h acct/pam.c acct/pam.h ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \ - main-worker-cmd.c ip-lease.c ip-lease.h main-proc.c \ + main-worker-cmd.c ip-lease.c ip-lease.h vhost.h main-proc.c \ vpn.h tlslib.h log.c tun.c tun.h config-kkdcp.c \ config.c worker-resume.c worker.h sec-mod-resume.c main.h \ worker-http-handlers.c html.c html.h worker-http.c \ diff --git a/src/acct/pam.c b/src/acct/pam.c index e0139347..c25f11bd 100644 --- a/src/acct/pam.c +++ b/src/acct/pam.c @@ -43,7 +43,7 @@ static int ocserv_conv(int msg_size, const struct pam_message **msg, return PAM_SUCCESS; } -static int pam_acct_open_session(unsigned auth_method, const struct common_acct_info_st *ai, const void *sid, unsigned sid_size) +static int pam_acct_open_session(void *vctx, unsigned auth_method, const struct common_acct_info_st *ai, const void *sid, unsigned sid_size) { int pret; pam_handle_t *ph; @@ -79,7 +79,7 @@ fail1: } -static void pam_acct_close_session(unsigned auth_method, const struct common_acct_info_st *ai, stats_st *stats, unsigned status) +static void pam_acct_close_session(void *vctx, unsigned auth_method, const struct common_acct_info_st *ai, stats_st *stats, unsigned status) { return; } diff --git a/src/acct/radius.c b/src/acct/radius.c index 565ce971..f76f4e97 100644 --- a/src/acct/radius.c +++ b/src/acct/radius.c @@ -42,42 +42,47 @@ #include "acct/radius.h" #include "common-config.h" -static rc_handle *rh = NULL; -static char nas_identifier[64]; - -static void acct_radius_global_init(void *pool, void *additional) +static void acct_radius_vhost_init(void **_vctx, void *pool, void *additional) { radius_cfg_st *config = additional; + struct radius_vhost_ctx *vctx; if (config == NULL) goto fail; - rh = rc_read_config(config->config); - if (rh == NULL) + vctx = talloc_zero(pool, struct radius_vhost_ctx); + if (vctx == NULL) goto fail; - if (config->nas_identifier) { - strlcpy(nas_identifier, config->nas_identifier, sizeof(nas_identifier)); - } else { - nas_identifier[0] = 0; + vctx->rh = rc_read_config(config->config); + if (vctx->rh == NULL) { + goto fail; } - if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { + if (config->nas_identifier) { + strlcpy(vctx->nas_identifier, config->nas_identifier, sizeof(vctx->nas_identifier)); + } else { + vctx->nas_identifier[0] = 0; + } + + if (rc_read_dictionary(vctx->rh, rc_conf_str(vctx->rh, "dictionary")) != 0) { fprintf(stderr, "error reading the radius dictionary\n"); exit(1); } + *_vctx = vctx; return; fail: - fprintf(stderr, "radius acct initialization error\n"); + fprintf(stderr, "radius initialization error\n"); exit(1); - } -static void acct_radius_global_deinit(void) +static void acct_radius_vhost_deinit(void *_vctx) { - if (rh != NULL) - rc_destroy(rh); + struct radius_vhost_ctx *vctx = _vctx; + + if (vctx->rh != NULL) + rc_destroy(vctx->rh); } static void append_stats(rc_handle *rh, VALUE_PAIR **send, stats_st *stats) @@ -104,12 +109,12 @@ static void append_stats(rc_handle *rh, VALUE_PAIR **send, stats_st *stats) return; } -static void append_acct_standard(rc_handle *rh, const common_acct_info_st *ai, VALUE_PAIR **send) +static void append_acct_standard(struct radius_vhost_ctx *vctx, rc_handle *rh, const common_acct_info_st *ai, VALUE_PAIR **send) { uint32_t i; - if (nas_identifier[0] != 0) { - rc_avpair_add(rh, send, PW_NAS_IDENTIFIER, nas_identifier, -1, 0); + if (vctx->nas_identifier[0] != 0) { + rc_avpair_add(rh, send, PW_NAS_IDENTIFIER, vctx->nas_identifier, -1, 0); } if (ai->our_ip[0] != 0) { @@ -162,24 +167,25 @@ static void append_acct_standard(rc_handle *rh, const common_acct_info_st *ai, V return; } -static void radius_acct_session_stats(unsigned auth_method, const common_acct_info_st *ai, stats_st *stats) +static void radius_acct_session_stats(void *_vctx, unsigned auth_method, const common_acct_info_st *ai, stats_st *stats) { -int ret; -uint32_t status_type; -VALUE_PAIR *send = NULL, *recvd = NULL; + int ret; + uint32_t status_type; + VALUE_PAIR *send = NULL, *recvd = NULL; + struct radius_vhost_ctx *vctx = _vctx; status_type = PW_STATUS_ALIVE; syslog(LOG_DEBUG, "radius-auth: sending session interim update"); - if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { + if (rc_avpair_add(vctx->rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { goto cleanup; } - append_acct_standard(rh, ai, &send); - append_stats(rh, &send, stats); + append_acct_standard(vctx, vctx->rh, ai, &send); + append_stats(vctx->rh, &send, stats); - ret = rc_aaa(rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); + ret = rc_aaa(vctx->rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); if (recvd != NULL) rc_avpair_free(recvd); @@ -194,11 +200,12 @@ VALUE_PAIR *send = NULL, *recvd = NULL; return; } -static int radius_acct_open_session(unsigned auth_method, const common_acct_info_st *ai, const void *sid, unsigned sid_size) +static int radius_acct_open_session(void *_vctx, unsigned auth_method, const common_acct_info_st *ai, const void *sid, unsigned sid_size) { -int ret; -uint32_t status_type; -VALUE_PAIR *send = NULL, *recvd = NULL; + int ret; + uint32_t status_type; + VALUE_PAIR *send = NULL, *recvd = NULL; + struct radius_vhost_ctx *vctx = _vctx; status_type = PW_STATUS_START; @@ -209,18 +216,18 @@ VALUE_PAIR *send = NULL, *recvd = NULL; syslog(LOG_DEBUG, "radius-auth: opening session %s", ai->safe_id); - if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { + if (rc_avpair_add(vctx->rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) { ret = -1; goto cleanup; } if (ai->user_agent[0] != 0) { - rc_avpair_add(rh, &send, PW_CONNECT_INFO, ai->user_agent, -1, 0); + rc_avpair_add(vctx->rh, &send, PW_CONNECT_INFO, ai->user_agent, -1, 0); } - append_acct_standard(rh, ai, &send); + append_acct_standard(vctx, vctx->rh, ai, &send); - ret = rc_aaa(rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); + ret = rc_aaa(vctx->rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); if (recvd != NULL) rc_avpair_free(recvd); @@ -237,16 +244,17 @@ VALUE_PAIR *send = NULL, *recvd = NULL; return ret; } -static void radius_acct_close_session(unsigned auth_method, const common_acct_info_st *ai, stats_st *stats, unsigned discon_reason) +static void radius_acct_close_session(void *_vctx, unsigned auth_method, const common_acct_info_st *ai, stats_st *stats, unsigned discon_reason) { -int ret; -uint32_t status_type; -VALUE_PAIR *send = NULL, *recvd = NULL; + int ret; + uint32_t status_type; + VALUE_PAIR *send = NULL, *recvd = NULL; + struct radius_vhost_ctx *vctx = _vctx; status_type = PW_STATUS_STOP; syslog(LOG_DEBUG, "radius-auth: closing session"); - if (rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) + if (rc_avpair_add(vctx->rh, &send, PW_ACCT_STATUS_TYPE, &status_type, -1, 0) == NULL) return; if (discon_reason == REASON_USER_DISCONNECT) @@ -263,12 +271,12 @@ VALUE_PAIR *send = NULL, *recvd = NULL; ret = PW_USER_ERROR; else ret = PW_LOST_SERVICE; - rc_avpair_add(rh, &send, PW_ACCT_TERMINATE_CAUSE, &ret, -1, 0); + rc_avpair_add(vctx->rh, &send, PW_ACCT_TERMINATE_CAUSE, &ret, -1, 0); - append_acct_standard(rh, ai, &send); - append_stats(rh, &send, stats); + append_acct_standard(vctx, vctx->rh, ai, &send); + append_stats(vctx->rh, &send, stats); - ret = rc_aaa(rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); + ret = rc_aaa(vctx->rh, ai->id, send, &recvd, NULL, 1, PW_ACCOUNTING_REQUEST); if (recvd != NULL) rc_avpair_free(recvd); @@ -285,8 +293,8 @@ VALUE_PAIR *send = NULL, *recvd = NULL; const struct acct_mod_st radius_acct_funcs = { .type = ACCT_TYPE_RADIUS, .auth_types = ALL_AUTH_TYPES, - .global_init = acct_radius_global_init, - .global_deinit = acct_radius_global_deinit, + .vhost_init = acct_radius_vhost_init, + .vhost_deinit = acct_radius_vhost_deinit, .open_session = radius_acct_open_session, .close_session = radius_acct_close_session, .session_stats = radius_acct_session_stats diff --git a/src/auth/gssapi.c b/src/auth/gssapi.c index c4fdfbcd..1d7492ac 100644 --- a/src/auth/gssapi.c +++ b/src/auth/gssapi.c @@ -38,10 +38,12 @@ #include #include "common-config.h" -static gss_cred_id_t glob_creds; -static gss_OID_set glob_oids; -static unsigned no_local_map = 0; -static time_t ticket_freshness_secs = 0; +struct gssapi_vhost_ctx_st { + gss_cred_id_t creds; + gss_OID_set oids; + unsigned no_local_map; + time_t ticket_freshness_secs; +}; struct gssapi_ctx_st { char username[MAX_USERNAME_SIZE]; @@ -49,6 +51,8 @@ struct gssapi_ctx_st { gss_cred_id_t delegated_creds; gss_buffer_desc msg; + + struct gssapi_vhost_ctx_st *vctx; }; /* Taken from openconnect's gssapi */ @@ -85,16 +89,23 @@ const gss_OID_set_desc desired_mechs = { .elements = (gss_OID)&spnego_mech }; -static void gssapi_global_init(void *pool, void *additional) +static void gssapi_vhost_init(void **_vctx, void *pool, void *additional) { int ret; OM_uint32 time, minor; gss_name_t name = GSS_C_NO_NAME; gssapi_cfg_st *config = additional; + struct gssapi_vhost_ctx_st *vctx; + + vctx = talloc_zero(pool, struct gssapi_vhost_ctx_st); + if (vctx == NULL) { + fprintf(stderr, "auth-gssapi: memory error\n"); + exit(1); + } if (config) { - no_local_map = config->no_local_map; - ticket_freshness_secs = config->ticket_freshness_secs; + vctx->no_local_map = config->no_local_map; + vctx->ticket_freshness_secs = config->ticket_freshness_secs; } if (config && config->keytab) { @@ -107,7 +118,7 @@ static void gssapi_global_init(void *pool, void *additional) cred_store.elements = &element; ret = gss_acquire_cred_from(&minor, name, 0, (gss_OID_set)&desired_mechs, 2, - &cred_store, &glob_creds, &glob_oids, &time); + &cred_store, &vctx->creds, &vctx->oids, &time); if (ret != GSS_S_COMPLETE) { ret = -1; @@ -116,7 +127,7 @@ static void gssapi_global_init(void *pool, void *additional) } } else { ret = gss_acquire_cred(&minor, name, 0, (gss_OID_set)&desired_mechs, 2, - &glob_creds, &glob_oids, &time); + &vctx->creds, &vctx->oids, &time); if (ret != GSS_S_COMPLETE) { ret = -1; @@ -128,15 +139,17 @@ static void gssapi_global_init(void *pool, void *additional) if (name != GSS_C_NO_NAME) gss_release_name(&minor, &name); + *_vctx = vctx; return; } -static void gssapi_global_deinit() +static void gssapi_vhost_deinit(void *_vctx) { + struct gssapi_vhost_ctx_st *vctx = _vctx; OM_uint32 minor; - if (glob_creds != NULL) - gss_release_cred(&minor, &glob_creds); + if (vctx->creds != NULL) + gss_release_cred(&minor, &vctx->creds); } static int get_name(struct gssapi_ctx_st *pctx, gss_name_t client, gss_OID mech_type) @@ -161,7 +174,7 @@ static int get_name(struct gssapi_ctx_st *pctx, gss_name_t client, gss_OID mech_ syslog(LOG_DEBUG, "gssapi: authenticated GSSAPI user: %.*s", (unsigned)name.length, (char*)name.value); gss_release_buffer(&minor, &name); - if (no_local_map == 0) { + if (pctx->vctx->no_local_map == 0) { ret = gss_localname(&minor, client, mech_type, &name); if (GSS_ERROR(ret) || name.length >= MAX_USERNAME_SIZE) { print_gss_err("gss_localname", mech_type, ret, minor); @@ -191,7 +204,7 @@ static int verify_krb5_constraints(struct gssapi_ctx_st *pctx, gss_OID mech_type if (mech_type == NULL || ((mech_type->length != gss_mech_krb5->length || memcmp(mech_type->elements, gss_mech_krb5->elements, mech_type->length) != 0) && (mech_type->length != gss_mech_krb5_old->length || memcmp(mech_type->elements, gss_mech_krb5_old->elements, mech_type->length) != 0)) || - ticket_freshness_secs == 0) { + pctx->vctx->ticket_freshness_secs == 0) { return 0; } @@ -201,7 +214,7 @@ static int verify_krb5_constraints(struct gssapi_ctx_st *pctx, gss_OID mech_type return -1; } - if (time(0) > authtime + ticket_freshness_secs) { + if (time(0) > authtime + pctx->vctx->ticket_freshness_secs) { syslog(LOG_INFO, "gssapi: the presented kerberos ticket for %s is too old", pctx->username); return -1; } @@ -209,7 +222,7 @@ static int verify_krb5_constraints(struct gssapi_ctx_st *pctx, gss_OID mech_type return 0; } -static int gssapi_auth_init(void **ctx, void *pool, const common_auth_init_st *info) +static int gssapi_auth_init(void **ctx, void *pool, void *_vctx, const common_auth_init_st *info) { struct gssapi_ctx_st *pctx; OM_uint32 minor, flags, time; @@ -220,6 +233,7 @@ static int gssapi_auth_init(void **ctx, void *pool, const common_auth_init_st *i size_t raw_len; char *raw; const char *spnego = info->username; + struct gssapi_vhost_ctx_st *vctx = _vctx; if (spnego == NULL || spnego[0] == 0) { syslog(LOG_ERR, "gssapi: error in spnego data %s", __func__); @@ -238,7 +252,7 @@ static int gssapi_auth_init(void **ctx, void *pool, const common_auth_init_st *i buf.value = raw; buf.length = raw_len; - ret = gss_accept_sec_context(&minor, &pctx->gssctx, glob_creds, &buf, + ret = gss_accept_sec_context(&minor, &pctx->gssctx, vctx->creds, &buf, GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &pctx->msg, &flags, &time, &pctx->delegated_creds); talloc_free(raw); @@ -258,6 +272,7 @@ static int gssapi_auth_init(void **ctx, void *pool, const common_auth_init_st *i return ERR_AUTH_FAIL; } + pctx->vctx = vctx; *ctx = pctx; return ret; @@ -300,7 +315,7 @@ static int gssapi_auth_pass(void *ctx, const char *spnego, unsigned spnego_len) buf.value = raw; buf.length = raw_len; - ret = gss_accept_sec_context(&minor, &pctx->gssctx, glob_creds, &buf, + ret = gss_accept_sec_context(&minor, &pctx->gssctx, pctx->vctx->creds, &buf, GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &pctx->msg, &flags, &time, &pctx->delegated_creds); talloc_free(raw); @@ -372,8 +387,8 @@ const struct auth_mod_st gssapi_auth_funcs = { .auth_pass = gssapi_auth_pass, .auth_user = gssapi_auth_user, .auth_group = gssapi_auth_group, - .global_init = gssapi_global_init, - .global_deinit = gssapi_global_deinit, + .vhost_init = gssapi_vhost_init, + .vhost_deinit = gssapi_vhost_deinit, .group_list = gssapi_group_list }; diff --git a/src/auth/pam.c b/src/auth/pam.c index 51767732..1c6dafb7 100644 --- a/src/auth/pam.c +++ b/src/auth/pam.c @@ -154,7 +154,7 @@ int pret; } } -static int pam_auth_init(void** ctx, void *pool, const common_auth_init_st *info) +static int pam_auth_init(void** ctx, void *pool, void *vctx, const common_auth_init_st *info) { int pret; struct pam_ctx_st * pctx; diff --git a/src/auth/plain.c b/src/auth/plain.c index 2052e07d..b39eafbd 100644 --- a/src/auth/plain.c +++ b/src/auth/plain.c @@ -51,40 +51,26 @@ struct plain_ctx_st { const char *pass_msg; unsigned retries; unsigned failed; /* non-zero if the username is wrong */ + + const struct plain_cfg_st *config; }; -static char *password_file = NULL; -static char *otp_file = NULL; - -static void plain_global_init(void *pool, void *additional) +static void plain_vhost_init(void **vctx, void *pool, void *additional) { struct plain_cfg_st *config = additional; + /* vctx is plain_cfg_st */ + if (config == NULL) { fprintf(stderr, "plain: no configuration passed!\n"); exit(1); } + *vctx = (void*)config; + #ifdef HAVE_LIBOATH oath_init(); #endif - - if (config->passwd) { - password_file = talloc_strdup(pool, config->passwd); - if (password_file == NULL) { - fprintf(stderr, "plain: memory error\n"); - exit(1); - } - } - - if (config->otp_file) { - otp_file = talloc_strdup(pool, config->otp_file); - if (otp_file == NULL) { - fprintf(stderr, "plain: memory error\n"); - exit(1); - } - } - return; } @@ -153,18 +139,18 @@ static int read_auth_pass(struct plain_ctx_st *pctx) char *p, *sp; int ret; - if (password_file == NULL) { + if (pctx->config->passwd == NULL) { /* no password file is set */ return 0; } pctx->failed = 1; - fp = fopen(password_file, "r"); + fp = fopen(pctx->config->passwd, "r"); if (fp == NULL) { syslog(LOG_AUTH, "error in plain authentication; cannot open: %s", - password_file); + pctx->config->passwd); return -1; } @@ -230,7 +216,7 @@ static int read_auth_pass(struct plain_ctx_st *pctx) return ret; } -static int plain_auth_init(void **ctx, void *pool, const common_auth_init_st *info) +static int plain_auth_init(void **ctx, void *pool, void *vctx, const common_auth_init_st *info) { struct plain_ctx_st *pctx; int ret; @@ -247,6 +233,7 @@ static int plain_auth_init(void **ctx, void *pool, const common_auth_init_st *in strlcpy(pctx->username, info->username, sizeof(pctx->username)); pctx->pass_msg = NULL; /* use default */ + pctx->config = vctx; /* this doesn't fail on password mismatch but sets p->failed */ ret = read_auth_pass(pctx); @@ -259,7 +246,7 @@ static int plain_auth_init(void **ctx, void *pool, const common_auth_init_st *in if (pctx->cpass[0] == 0 && pctx->failed == 0) { /* if there is no password set, nor an OTP file; don't ask for password */ - if (otp_file == NULL) + if (pctx->config->otp_file == NULL) return 0; /* only OTP is present */ @@ -332,7 +319,7 @@ static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len) } } - if (pctx->cpass[0] == 0 && otp_file == NULL) { + if (pctx->cpass[0] == 0 && pctx->config->otp_file == NULL) { syslog(LOG_AUTH, "plain-auth: user '%s' has empty password and no OTP file configured", pctx->username); @@ -340,7 +327,7 @@ static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len) } #ifdef HAVE_LIBOATH - if (otp_file != NULL) { + if (pctx->config->otp_file != NULL) { int ret; time_t last; @@ -351,7 +338,7 @@ static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len) } /* no primary password -> check OTP */ - ret = oath_authenticate_usersfile(otp_file, pctx->username, + ret = oath_authenticate_usersfile(pctx->config->otp_file, pctx->username, pass, HOTP_WINDOW, NULL, &last); if (ret != OATH_OK) { syslog(LOG_AUTH, @@ -492,7 +479,7 @@ static void plain_group_list(void *pool, void *additional, char ***groupname, un const struct auth_mod_st plain_auth_funcs = { .type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS, .allows_retries = 1, - .global_init = plain_global_init, + .vhost_init = plain_vhost_init, .auth_init = plain_auth_init, .auth_deinit = plain_auth_deinit, .auth_msg = plain_auth_msg, diff --git a/src/auth/radius.c b/src/auth/radius.c index 6b31d0f8..38c0c95e 100644 --- a/src/auth/radius.c +++ b/src/auth/radius.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2014-2016 Red Hat, Inc. - * Copyright (C) 2016 Nikos Mavrogiannopoulos + * Copyright (C) 2016-2018 Nikos Mavrogiannopoulos * * This file is part of ocserv. * @@ -58,31 +58,34 @@ # define CHALLENGE_RC 3 #endif -static rc_handle *rh = NULL; -static char nas_identifier[64]; - -static void radius_global_init(void *pool, void *additional) +static void radius_vhost_init(void **_vctx, void *pool, void *additional) { radius_cfg_st *config = additional; + struct radius_vhost_ctx *vctx; if (config == NULL) goto fail; - rh = rc_read_config(config->config); - if (rh == NULL) { + vctx = talloc_zero(pool, struct radius_vhost_ctx); + if (vctx == NULL) + goto fail; + + vctx->rh = rc_read_config(config->config); + if (vctx->rh == NULL) { goto fail; } if (config->nas_identifier) { - strlcpy(nas_identifier, config->nas_identifier, sizeof(nas_identifier)); + strlcpy(vctx->nas_identifier, config->nas_identifier, sizeof(vctx->nas_identifier)); } else { - nas_identifier[0] = 0; + vctx->nas_identifier[0] = 0; } - if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { + if (rc_read_dictionary(vctx->rh, rc_conf_str(vctx->rh, "dictionary")) != 0) { fprintf(stderr, "error reading the radius dictionary\n"); exit(1); } + *_vctx = vctx; return; fail: @@ -90,16 +93,19 @@ static void radius_global_init(void *pool, void *additional) exit(1); } -static void radius_global_deinit() +static void radius_vhost_deinit(void *_vctx) { - if (rh != NULL) - rc_destroy(rh); + struct radius_vhost_ctx *vctx = _vctx; + + if (vctx->rh != NULL) + rc_destroy(vctx->rh); } -static int radius_auth_init(void **ctx, void *pool, const common_auth_init_st *info) +static int radius_auth_init(void **ctx, void *pool, void *_vctx, const common_auth_init_st *info) { struct radius_ctx_st *pctx; char *default_realm; + struct radius_vhost_ctx *vctx = _vctx; if (info->username == NULL || info->username[0] == 0) { syslog(LOG_AUTH, @@ -117,8 +123,9 @@ static int radius_auth_init(void **ctx, void *pool, const common_auth_init_st *i strlcpy(pctx->our_ip, info->our_ip, sizeof(pctx->our_ip)); pctx->pass_msg[0] = 0; + pctx->vctx = vctx; - default_realm = rc_conf_str(rh, "default_realm"); + default_realm = rc_conf_str(pctx->vctx->rh, "default_realm"); if ((strchr(info->username, '@') == NULL) && default_realm && default_realm[0] != 0) { @@ -246,14 +253,14 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) /* send Access-Request */ syslog(LOG_DEBUG, "radius-auth: communicating username (%s) and password", pctx->username); - if (rc_avpair_add(rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_USER_NAME, pctx->username, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); return ERR_AUTH_FAIL; } - if (rc_avpair_add(rh, &send, PW_USER_PASSWORD, (char*)pass, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_USER_PASSWORD, (char*)pass, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -267,14 +274,14 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) if (inet_pton(AF_INET, pctx->our_ip, &in) != 0) { in.s_addr = ntohl(in.s_addr); - rc_avpair_add(rh, &send, PW_NAS_IP_ADDRESS, (char*)&in, sizeof(struct in_addr), 0); + rc_avpair_add(pctx->vctx->rh, &send, PW_NAS_IP_ADDRESS, (char*)&in, sizeof(struct in_addr), 0); } else if (inet_pton(AF_INET6, pctx->our_ip, &in6) != 0) { - rc_avpair_add(rh, &send, PW_NAS_IPV6_ADDRESS, (char*)&in6, sizeof(struct in6_addr), 0); + rc_avpair_add(pctx->vctx->rh, &send, PW_NAS_IPV6_ADDRESS, (char*)&in6, sizeof(struct in6_addr), 0); } } - if (nas_identifier[0] != 0) { - if (rc_avpair_add(rh, &send, PW_NAS_IDENTIFIER, nas_identifier, -1, 0) == NULL) { + if (pctx->vctx->nas_identifier[0] != 0) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_NAS_IDENTIFIER, pctx->vctx->nas_identifier, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -283,7 +290,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } } - if (rc_avpair_add(rh, &send, PW_CALLING_STATION_ID, pctx->remote_ip, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_CALLING_STATION_ID, pctx->remote_ip, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -292,7 +299,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } if (pctx->user_agent[0] != 0) { - if (rc_avpair_add(rh, &send, PW_CONNECT_INFO, pctx->user_agent, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_CONNECT_INFO, pctx->user_agent, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -302,7 +309,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } service = PW_AUTHENTICATE_ONLY; - if (rc_avpair_add(rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -311,7 +318,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } service = PW_ASYNC; - if (rc_avpair_add(rh, &send, PW_NAS_PORT_TYPE, &service, -1, 0) == NULL) { + if (rc_avpair_add(pctx->vctx->rh, &send, PW_NAS_PORT_TYPE, &service, -1, 0) == NULL) { syslog(LOG_ERR, "%s:%u: error in constructing radius message for user '%s'", __func__, __LINE__, pctx->username); @@ -320,7 +327,7 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) } pctx->pass_msg[0] = 0; - ret = rc_aaa(rh, pctx->id, send, &recvd, pctx->pass_msg, 1, PW_ACCESS_REQUEST); + ret = rc_aaa(pctx->vctx->rh, pctx->id, send, &recvd, pctx->pass_msg, 1, PW_ACCESS_REQUEST); if (ret == OK_RC) { uint32_t ipv4; @@ -454,8 +461,8 @@ static void radius_auth_deinit(void *ctx) const struct auth_mod_st radius_auth_funcs = { .type = AUTH_TYPE_RADIUS | AUTH_TYPE_USERNAME_PASS, .allows_retries = 1, - .global_init = radius_global_init, - .global_deinit = radius_global_deinit, + .vhost_init = radius_vhost_init, + .vhost_deinit = radius_vhost_deinit, .auth_init = radius_auth_init, .auth_deinit = radius_auth_deinit, .auth_msg = radius_auth_msg, diff --git a/src/auth/radius.h b/src/auth/radius.h index 7c608c05..bf69e8c8 100644 --- a/src/auth/radius.h +++ b/src/auth/radius.h @@ -31,6 +31,11 @@ # include # endif +struct radius_vhost_ctx { + rc_handle *rh; + char nas_identifier[64]; +}; + struct radius_ctx_st { char username[MAX_USERNAME_SIZE*2]; char user_agent[MAX_AGENT_NAME]; @@ -61,6 +66,8 @@ struct radius_ctx_st { char pass_msg[PW_MAX_MSG_SIZE]; unsigned retries; unsigned id; + + struct radius_vhost_ctx *vctx; }; extern const struct auth_mod_st radius_auth_funcs; diff --git a/src/common-config.h b/src/common-config.h index 9c65cfe0..2a08c2a5 100644 --- a/src/common-config.h +++ b/src/common-config.h @@ -63,10 +63,10 @@ typedef struct pam_cfg_st { struct perm_cfg_st; void *get_brackets_string1(void *pool, const char *str); -void *gssapi_get_brackets_string(struct perm_cfg_st *config, const char *str); -void *radius_get_brackets_string(struct perm_cfg_st *config, const char *str); -void *pam_get_brackets_string(struct perm_cfg_st *config, const char *str); -void *plain_get_brackets_string(struct perm_cfg_st *config, const char *str); +void *gssapi_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str); +void *radius_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str); +void *pam_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str); +void *plain_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str); void parse_kkdcp_string(char *str, int *socktype, char **_port, char **_server, char **_path, char **_realm); diff --git a/src/common/common.c b/src/common/common.c index 68e55fc5..a404480c 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -35,6 +35,14 @@ #include "defs.h" #include "common/base64-helper.h" +const char *_vhost_prefix(const char *name) +{ + static char tmp[128]; + + snprintf(tmp, sizeof(tmp), "vhost:%s: ", name); + return tmp; +} + /* A hash of the input, to a 20-byte output. The goal is one-wayness. */ static void safe_hash(const uint8_t *data, unsigned data_size, uint8_t output[20]) @@ -126,10 +134,14 @@ const char *cmd_request_to_str(unsigned _cmd) return "sm: decrypt"; case CMD_SEC_SIGN: return "sm: sign"; + case CMD_SECM_STATS: + return "sm: stats"; case CMD_SECM_SESSION_CLOSE: return "sm: session close"; case CMD_SECM_SESSION_OPEN: return "sm: session open"; + case CMD_SECM_SESSION_REPLY: + return "sm: session reply"; case CMD_SECM_BAN_IP: return "sm: ban IP"; case CMD_SECM_BAN_IP_REPLY: diff --git a/src/common/common.h b/src/common/common.h index 38ba4024..29a53ceb 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -123,6 +123,8 @@ void ms_sleep(unsigned ms) const char *ps_status_to_str(int status, unsigned cookie); +const char *_vhost_prefix(const char *name); + #ifndef HAVE_STRLCPY size_t oc_strlcpy(char *dst, char const *src, size_t siz); # define strlcpy oc_strlcpy diff --git a/src/config.c b/src/config.c index 5a2b586f..f2207ae7 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * Copyright (C) 2014, 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,9 @@ static void print_version(void); static char pid_file[_POSIX_PATH_MAX] = ""; static char cfg_file[_POSIX_PATH_MAX] = DEFAULT_CFG_FILE; -static void archive_cfg(struct perm_cfg_st* perm_config); +static void archive_cfg(struct list_head *head); +static void clear_cfg(struct list_head *head); +static void check_cfg(vhost_cfg_st *vhost, vhost_cfg_st *defvhost, unsigned silent); #define ERRSTR "error: " #define WARNSTR "warning: " @@ -157,7 +160,7 @@ typedef struct auth_types_st { unsigned name_size; const struct auth_mod_st *mod; unsigned type; - void *(*get_brackets_string)(struct perm_cfg_st *config, const char *); + void *(*get_brackets_string)(void *pool, struct perm_cfg_st *config, const char *); } auth_types_st; #define NAME(x) (x),(sizeof(x)-1) @@ -176,7 +179,8 @@ static auth_types_st avail_auth_types[] = {NAME("certificate"), NULL, AUTH_TYPE_CERTIFICATE, NULL}, }; -static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned auth_size, +static void figure_auth_funcs(void *pool, const char *vhostname, + struct perm_cfg_st *config, char **auth, unsigned auth_size, unsigned primary) { unsigned j, i; @@ -192,10 +196,10 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned for (i=0;iauth[0].additional = avail_auth_types[i].get_brackets_string(config, auth[j]+avail_auth_types[i].name_size); + config->auth[0].additional = avail_auth_types[i].get_brackets_string(pool, config, auth[j]+avail_auth_types[i].name_size); if (config->auth[0].amod != NULL && avail_auth_types[i].mod != NULL) { - fprintf(stderr, ERRSTR"%s: you cannot mix multiple authentication methods of this type\n", auth[j]); + fprintf(stderr, ERRSTR"%s: you cannot mix multiple authentication methods of %s type\n", vhostname, auth[j]); exit(1); } @@ -203,10 +207,10 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned config->auth[0].amod = avail_auth_types[i].mod; config->auth[0].type |= avail_auth_types[i].type; if (config->auth[0].name == NULL) { - config->auth[0].name = talloc_strdup(config, avail_auth_types[i].name); + config->auth[0].name = talloc_strdup(pool, avail_auth_types[i].name); } else { char *tmp; - tmp = talloc_asprintf(config, "%s+%s", config->auth[0].name, avail_auth_types[i].name); + tmp = talloc_asprintf(pool, "%s+%s", config->auth[0].name, avail_auth_types[i].name); talloc_free(config->auth[0].name); config->auth[0].name = tmp; } @@ -218,12 +222,12 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned } if (found == 0) { - fprintf(stderr, ERRSTR"unknown or unsupported auth method: %s\n", auth[j]); + fprintf(stderr, ERRSTR"%s: unknown or unsupported auth method: %s\n", vhostname, auth[j]); exit(1); } talloc_free(auth[j]); } - fprintf(stderr, NOTESTR"setting '%s' as primary authentication method\n", config->auth[0].name); + fprintf(stderr, NOTESTR"%ssetting '%s' as primary authentication method\n", vhostname, config->auth[0].name); } else { unsigned x = config->auth_methods; /* Append authentication methods (alternative options) */ @@ -232,10 +236,10 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned for (i=0;iauth[x].additional = avail_auth_types[i].get_brackets_string(config, auth[j]+avail_auth_types[i].name_size); + config->auth[x].additional = avail_auth_types[i].get_brackets_string(pool, config, auth[j]+avail_auth_types[i].name_size); - config->auth[x].name = talloc_strdup(config, avail_auth_types[i].name); - fprintf(stderr, NOTESTR"enabling '%s' as authentication method\n", avail_auth_types[i].name); + config->auth[x].name = talloc_strdup(pool, avail_auth_types[i].name); + fprintf(stderr, NOTESTR"%s: enabling '%s' as authentication method\n", vhostname, avail_auth_types[i].name); config->auth[x].amod = avail_auth_types[i].mod; config->auth[x].type |= avail_auth_types[i].type; @@ -243,7 +247,7 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned found = 1; x++; if (x >= MAX_AUTH_METHODS) { - fprintf(stderr, ERRSTR"you cannot enable more than %d authentication methods\n", x); + fprintf(stderr, ERRSTR"%s: you cannot enable more than %d authentication methods\n", vhostname, x); exit(1); } break; @@ -251,7 +255,7 @@ static void figure_auth_funcs(struct perm_cfg_st *config, char **auth, unsigned } if (found == 0) { - fprintf(stderr, ERRSTR"unknown or unsupported auth method: %s\n", auth[j]); + fprintf(stderr, ERRSTR"%s: unknown or unsupported auth method: %s\n", vhostname, auth[j]); exit(1); } talloc_free(auth[j]); @@ -265,7 +269,7 @@ typedef struct acct_types_st { const char *name; unsigned name_size; const struct acct_mod_st *mod; - void *(*get_brackets_string)(struct perm_cfg_st *config, const char *); + void *(*get_brackets_string)(void *pool, struct perm_cfg_st *config, const char *); } acct_types_st; static acct_types_st avail_acct_types[] = @@ -278,7 +282,7 @@ static acct_types_st avail_acct_types[] = #endif }; -static void figure_acct_funcs(struct perm_cfg_st *config, const char *acct) +static void figure_acct_funcs(void *pool, const char *vhostname, struct perm_cfg_st *config, const char *acct) { unsigned i; unsigned found = 0; @@ -293,10 +297,10 @@ static void figure_acct_funcs(struct perm_cfg_st *config, const char *acct) continue; if (avail_acct_types[i].get_brackets_string) - config->acct.additional = avail_acct_types[i].get_brackets_string(config, acct+avail_acct_types[i].name_size); + config->acct.additional = avail_acct_types[i].get_brackets_string(pool, config, acct+avail_acct_types[i].name_size); if ((avail_acct_types[i].mod->auth_types & config->auth[0].type) == 0) { - fprintf(stderr, ERRSTR"you cannot mix the '%s' accounting method with the '%s' authentication method\n", acct, config->auth[0].name); + fprintf(stderr, ERRSTR"%s: you cannot mix the '%s' accounting method with the '%s' authentication method\n", vhostname, acct, config->auth[0].name); exit(1); } @@ -308,10 +312,10 @@ static void figure_acct_funcs(struct perm_cfg_st *config, const char *acct) } if (found == 0) { - fprintf(stderr, ERRSTR"unknown or unsupported accounting method: %s\n", acct); + fprintf(stderr, ERRSTR"%s: unknown or unsupported accounting method: %s\n", vhostname, acct); exit(1); } - fprintf(stderr, NOTESTR"setting '%s' as accounting method\n", config->acct.name); + fprintf(stderr, NOTESTR"%ssetting '%s' as accounting method\n", vhostname, config->acct.name); } #ifdef HAVE_GSSAPI @@ -373,7 +377,7 @@ static void parse_kkdcp(struct cfg_st *config, char **urlfw, unsigned urlfw_size kkdcp_realm->realm = talloc_strdup(config->kkdcp, realm); - freeaddrinfo(res); + freeaddrinfo(res); kkdcp->realms_size++; } @@ -480,107 +484,289 @@ static void load_iroutes(struct cfg_st *config) } } +static void apply_default_conf(vhost_cfg_st *vhost, unsigned reload) +{ + /* set config (no-zero) default vals + */ + if (!reload) { /* perm config defaults */ + tls_vhost_init(vhost); + vhost->perm_config.stats_reset_time = 24*60*60*7; /* weekly */ + } + + vhost->perm_config.config->mobile_idle_timeout = (unsigned)-1; + vhost->perm_config.config->no_compress_limit = DEFAULT_NO_COMPRESS_LIMIT; + vhost->perm_config.config->rekey_time = 24*60*60; + vhost->perm_config.config->cookie_timeout = DEFAULT_COOKIE_RECON_TIMEOUT; + vhost->perm_config.config->auth_timeout = DEFAULT_AUTH_TIMEOUT_SECS; + vhost->perm_config.config->ban_reset_time = DEFAULT_BAN_RESET_TIME; + vhost->perm_config.config->max_ban_score = DEFAULT_MAX_BAN_SCORE; + vhost->perm_config.config->ban_points_wrong_password = DEFAULT_PASSWORD_POINTS; + vhost->perm_config.config->ban_points_connect = DEFAULT_CONNECT_POINTS; + vhost->perm_config.config->ban_points_kkdcp = DEFAULT_KKDCP_POINTS; + vhost->perm_config.config->dpd = DEFAULT_DPD_TIME; + vhost->perm_config.config->network.ipv6_subnet_prefix = 128; + vhost->perm_config.config->dtls_legacy = 1; + vhost->perm_config.config->dtls_psk = 1; + vhost->perm_config.config->predictable_ips = 1; + vhost->perm_config.config->use_utmp = 1; + vhost->perm_config.config->keepalive = 3600; + vhost->perm_config.config->dpd = 60; + +} + +static void cfg_new(struct vhost_cfg_st *vhost, unsigned reload) +{ + vhost->perm_config.config = talloc_zero(vhost->pool, struct cfg_st); + if (vhost->perm_config.config == NULL) + exit(1); + + vhost->perm_config.config->usage_count = talloc_zero(vhost->perm_config.config, int); + if (vhost->perm_config.config->usage_count == NULL) { + fprintf(stderr, ERRSTR"memory\n"); + exit(1); + } + + apply_default_conf(vhost, reload); +} + +static vhost_cfg_st *vhost_add(void *pool, struct list_head *head, const char *name, unsigned reload) +{ + vhost_cfg_st *vhost; + + vhost = talloc_zero(pool, struct vhost_cfg_st); + if (vhost == NULL) + exit(1); + vhost->pool = vhost; + + cfg_new(vhost, reload); + + if (name) { + vhost->name = talloc_strdup(vhost, name); + if (vhost->name == NULL) { + fprintf(stderr, ERRSTR"memory\n"); + exit(1); + } + } + + vhost->perm_config.sup_config_type = SUP_CONFIG_FILE; + list_head_init(&vhost->perm_config.attic); + + + list_add(head, &vhost->list); + + return vhost; +} + struct ini_ctx_st { - struct perm_cfg_st *perm_config; + struct list_head *head; unsigned reload; const char *file; - - char *acct; - char** auth; - size_t auth_size; - char** eauth; - size_t eauth_size; - unsigned expose_iroutes; - unsigned auto_select_group; -#ifdef HAVE_GSSAPI - char **urlfw; - size_t urlfw_size; -#endif + void *pool; }; +#define WARN_ON_VHOST(vname, oname, member) \ + ({int rval; \ + if (vname) { \ + fprintf(stderr, WARNSTR"%s is ignored on %s virtual host\n", oname, vname); \ + memcpy(&config->member, &defvhost->perm_config.config->member, sizeof(config->member)); \ + rval = 1; \ + } else { \ + rval = 0; \ + } \ + rval; \ + }) + +#define PWARN_ON_VHOST(vname, oname, member) \ + ({int rval; \ + if (vname) { \ + fprintf(stderr, WARNSTR"%s is ignored on %s virtual host\n", oname, vname); \ + vhost->perm_config.member = defvhost->perm_config.member; \ + rval = 1; \ + } else { \ + rval = 0; \ + } \ + rval; \ + }) + +#define PWARN_ON_VHOST_STRDUP(vname, oname, member) \ + ({int rval; \ + if (vname) { \ + fprintf(stderr, WARNSTR"%s is ignored on %s virtual host\n", oname, vname); \ + vhost->perm_config.member = talloc_strdup(pool, defvhost->perm_config.member); \ + rval = 1; \ + } else { \ + rval = 0; \ + } \ + rval; \ + }) + +static char *idna_map(void *pool, const char *name, unsigned size) +{ +#if GNUTLS_VERSION_NUMBER > 0x030508 + int ret; + gnutls_datum_t out; + + ret = gnutls_idna_map(name, size, &out, 0); + if (ret < 0) { + goto fallback; + } + + return talloc_strdup(pool, (char*)out.data); + + fallback: +#endif + return talloc_strndup(pool, name, size); +} + +static +char *sanitize_name(void *pool, const char *p) +{ + size_t len; + /* cleanup spaces before and after */ + while (c_isspace(*p)) + p++; + + len = strlen(p); + if (len > 0) { + while (c_isspace(p[len-1])) + len--; + } + + return idna_map(pool, p, len); +} + static int cfg_ini_handler(void *_ctx, const char *section, const char *name, const char *_value) { struct ini_ctx_st *ctx = _ctx; + vhost_cfg_st *vhost, *vtmp, *defvhost; unsigned use_dbus; + struct cfg_st *config; void *pool; - struct perm_cfg_st *perm_config = ctx->perm_config; - struct cfg_st *config = perm_config->config; unsigned reload = ctx->reload; int ret; unsigned stage1_found = 1; unsigned force_cert_auth; unsigned prefix = 0; unsigned prefix4 = 0; + unsigned found_vhost; char *value; - if (section != NULL && section[0] != 0) { - if (reload == 0) - fprintf(stderr, WARNSTR"skipping unknown section '%s'\n", section); - return 0; + defvhost = vhost = default_vhost(ctx->head); + if (vhost == NULL) { + vhost = vhost_add(ctx->pool, ctx->head, NULL, ctx->reload); } - value = sanitize_config_value(config, _value); + if (section != NULL && section[0] != 0) { + char *vname; + + if (strncmp(section, "vhost:", 6) != 0) { + if (reload == 0) + fprintf(stderr, WARNSTR"skipping unknown section '%s'\n", section); + return 0; + } + + vname = sanitize_name(ctx->pool, section+6); + if (vname == NULL || vname[0] == 0) { + fprintf(stderr, ERRSTR"virtual host name is illegal '%s'\n", section+6); + exit(1); + } + + /* virtual host */ + found_vhost = 0; + list_for_each(ctx->head, vtmp, list) { + if (vtmp->name && strcmp(vtmp->name, vname) == 0) { + vhost = vtmp; + found_vhost = 1; + break; + } + } + + if (c_strcasecmp(section+6, vname) != 0) { + fprintf(stderr, NOTESTR"virtual host name '%s' was canonicalized to '%s'\n", + section+6, vname); + } + + if (!found_vhost) { + /* add */ + fprintf(stderr, NOTESTR"adding virtual host: %s\n", vname); + vhost = vhost_add(ctx->pool, ctx->head, vname, reload); + } + talloc_free(vname); + } + + value = sanitize_config_value(vhost->pool, _value); if (value == NULL) return 0; /* read persistent configuration */ - if (reload == 0) { - pool = ctx->perm_config; + if (vhost->auth_init == 0) { + pool = vhost; if (strcmp(name, "auth") == 0) { - READ_MULTI_LINE(ctx->auth, ctx->auth_size); + READ_MULTI_LINE(vhost->auth, vhost->auth_size); } else if (strcmp(name, "enable-auth") == 0) { - READ_MULTI_LINE(ctx->eauth, ctx->eauth_size); + READ_MULTI_LINE(vhost->eauth, vhost->eauth_size); } else if (strcmp(name, "acct") == 0) { - ctx->acct = talloc_strdup(pool, value); + vhost->acct = talloc_strdup(pool, value); } else if (strcmp(name, "listen-host") == 0) { - PREAD_STRING(pool, perm_config->listen_host); + PREAD_STRING(pool, vhost->perm_config.listen_host); } else if (strcmp(name, "listen-clear-file") == 0) { - PREAD_STRING(pool, perm_config->unix_conn_file); + if (!PWARN_ON_VHOST_STRDUP(vhost->name, "listen-clear-file", unix_conn_file)) + PREAD_STRING(pool, vhost->perm_config.unix_conn_file); } else if (strcmp(name, "tcp-port") == 0) { - READ_NUMERIC(perm_config->port); + if (!PWARN_ON_VHOST(vhost->name, "tcp-port", port)) + READ_NUMERIC(vhost->perm_config.port); } else if (strcmp(name, "udp-port") == 0) { - READ_NUMERIC(perm_config->udp_port); + if (!PWARN_ON_VHOST(vhost->name, "udp-port", udp_port)) + READ_NUMERIC(vhost->perm_config.udp_port); } else if (strcmp(name, "run-as-user") == 0) { - const struct passwd* pwd = getpwnam(value); - if (pwd == NULL) { - fprintf(stderr, ERRSTR"unknown user: %s\n", value); - exit(1); + if (!PWARN_ON_VHOST(vhost->name, "run-as-user", uid)) { + const struct passwd* pwd = getpwnam(value); + if (pwd == NULL) { + fprintf(stderr, ERRSTR"unknown user: %s\n", value); + exit(1); + } + vhost->perm_config.uid = pwd->pw_uid; } - perm_config->uid = pwd->pw_uid; } else if (strcmp(name, "run-as-group") == 0) { - const struct group* grp = getgrnam(value); - if (grp == NULL) { - fprintf(stderr, ERRSTR"unknown group: %s\n", value); - exit(1); + if (!PWARN_ON_VHOST(vhost->name, "run-as-group", gid)) { + const struct group* grp = getgrnam(value); + if (grp == NULL) { + fprintf(stderr, ERRSTR"unknown group: %s\n", value); + exit(1); + } + vhost->perm_config.gid = grp->gr_gid; } - perm_config->gid = grp->gr_gid; } else if (strcmp(name, "server-cert") == 0) { - READ_MULTI_LINE(perm_config->cert, perm_config->cert_size); + READ_MULTI_LINE(vhost->perm_config.cert, vhost->perm_config.cert_size); } else if (strcmp(name, "server-key") == 0) { - READ_MULTI_LINE(perm_config->key, perm_config->key_size); + READ_MULTI_LINE(vhost->perm_config.key, vhost->perm_config.key_size); } else if (strcmp(name, "dh-params") == 0) { - READ_STRING(perm_config->dh_params_file); + READ_STRING(vhost->perm_config.dh_params_file); } else if (strcmp(name, "pin-file") == 0) { - READ_STRING(perm_config->pin_file); + READ_STRING(vhost->perm_config.pin_file); } else if (strcmp(name, "srk-pin-file") == 0) { - READ_STRING(perm_config->srk_pin_file); + READ_STRING(vhost->perm_config.srk_pin_file); } else if (strcmp(name, "ca-cert") == 0) { - READ_STRING(perm_config->ca); + READ_STRING(vhost->perm_config.ca); } else if (strcmp(name, "key-pin") == 0) { - READ_STRING(perm_config->key_pin); + READ_STRING(vhost->perm_config.key_pin); } else if (strcmp(name, "srk-pin") == 0) { - READ_STRING(perm_config->srk_pin); + READ_STRING(vhost->perm_config.srk_pin); } else if (strcmp(name, "socket-file") == 0) { - PREAD_STRING(perm_config, perm_config->socket_file_prefix); + if (!PWARN_ON_VHOST_STRDUP(vhost->name, "socket-file", socket_file_prefix)) + PREAD_STRING(pool, vhost->perm_config.socket_file_prefix); } else if (strcmp(name, "occtl-socket-file") == 0) { - PREAD_STRING(perm_config, perm_config->occtl_socket_file); + if (!PWARN_ON_VHOST_STRDUP(vhost->name, "occtl-socket-file", occtl_socket_file)) + PREAD_STRING(pool, vhost->perm_config.occtl_socket_file); } else if (strcmp(name, "chroot-dir") == 0) { - PREAD_STRING(perm_config, perm_config->chroot_dir); + if (!PWARN_ON_VHOST_STRDUP(vhost->name, "chroot-dir", chroot_dir)) + PREAD_STRING(pool, vhost->perm_config.chroot_dir); } else if (strcmp(name, "server-stats-reset-time") == 0) { /* cannot be modified as it would require sec-mod to * re-read configuration too */ - READ_NUMERIC(perm_config->stats_reset_time); + if (!PWARN_ON_VHOST(vhost->name, "server-stats-reset-time", stats_reset_time)) + READ_NUMERIC(vhost->perm_config.stats_reset_time); } else if (strcmp(name, "pid-file") == 0) { if (pid_file[0] == 0) { READ_STATIC_STRING(pid_file); @@ -596,7 +782,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co /* read the rest of the (non-permanent) configuration */ - pool = ctx->perm_config->config; + pool = vhost->perm_config.config; + config = vhost->perm_config.config; /* When adding allocated data, remember to modify * reload_cfg_file(); @@ -604,12 +791,13 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co if (strcmp(name, "listen-host-is-dyndns") == 0) { READ_TF(config->is_dyndns); } else if (strcmp(name, "listen-proxy-proto") == 0) { - READ_TF(config->listen_proxy_proto); + if (!WARN_ON_VHOST(vhost->name, "listen-proxy-proto", listen_proxy_proto)) + READ_TF(config->listen_proxy_proto); } else if (strcmp(name, "append-routes") == 0) { READ_TF(config->append_routes); #ifdef HAVE_GSSAPI } else if (strcmp(name, "kkdcp") == 0) { - READ_MULTI_LINE(ctx->urlfw, ctx->urlfw_size); + READ_MULTI_LINE(vhost->urlfw, vhost->urlfw_size); #endif } else if (strcmp(name, "tunnel-all-dns") == 0) { READ_TF(config->tunnel_all_dns); @@ -622,7 +810,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co } else if (strcmp(name, "mobile-dpd") == 0) { READ_NUMERIC(config->mobile_dpd); } else if (strcmp(name, "rate-limit-ms") == 0) { - READ_NUMERIC(config->rate_limit_ms); + if (!WARN_ON_VHOST(vhost->name, "rate-limit-ms", rate_limit_ms)) + READ_NUMERIC(config->rate_limit_ms); } else if (strcmp(name, "ocsp-response") == 0) { READ_STRING(config->ocsp_response); } else if (strcmp(name, "user-profile") == 0) { @@ -636,11 +825,14 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co } else if (strcmp(name, "cert-group-oid") == 0) { READ_STRING(config->cert_group_oid); } else if (strcmp(name, "connect-script") == 0) { - READ_STRING(config->connect_script); + if (!WARN_ON_VHOST(vhost->name, "connect-script", connect_script)) + READ_STRING(config->connect_script); } else if (strcmp(name, "host-update-script") == 0) { - READ_STRING(config->host_update_script); + if (!WARN_ON_VHOST(vhost->name, "host-update-script", host_update_script)) + READ_STRING(config->host_update_script); } else if (strcmp(name, "disconnect-script") == 0) { - READ_STRING(config->disconnect_script); + if (!WARN_ON_VHOST(vhost->name, "disconnect-script", disconnect_script)) + READ_STRING(config->disconnect_script); } else if (strcmp(name, "session-control") == 0) { fprintf(stderr, WARNSTR"the option 'session-control' is deprecated\n"); } else if (strcmp(name, "banner") == 0) { @@ -656,7 +848,8 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co config->cisco_client_compat = 1; } } else if (strcmp(name, "dtls-psk") == 0) { - READ_TF(config->dtls_psk); + if (!WARN_ON_VHOST(vhost->name, "dtls-psk", dtls_psk)) + READ_TF(config->dtls_psk); } else if (strcmp(name, "match-tls-dtls-ciphers") == 0) { READ_TF(config->match_dtls_and_tls); } else if (strcmp(name, "compression") == 0) { @@ -668,11 +861,13 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co if (config->isolate) fprintf(stderr, NOTESTR"'use-seccomp' was replaced by 'isolate-workers'\n"); } else if (strcmp(name, "isolate-workers") == 0) { - READ_TF(config->isolate); + if (!WARN_ON_VHOST(vhost->name, "isolate-workers", isolate)) + READ_TF(config->isolate); } else if (strcmp(name, "predictable-ips") == 0) { READ_TF(config->predictable_ips); } else if (strcmp(name, "use-utmp") == 0) { - READ_TF(config->use_utmp); + if (!WARN_ON_VHOST(vhost->name, "use-utmp", use_utmp)) + READ_TF(config->use_utmp); } else if (strcmp(name, "use-dbus") == 0) { READ_TF(use_dbus); if (use_dbus != 0) { @@ -680,11 +875,14 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co config->use_occtl = use_dbus; } } else if (strcmp(name, "use-occtl") == 0) { - READ_TF(config->use_occtl); + if (!WARN_ON_VHOST(vhost->name, "use-occtl", use_occtl)) + READ_TF(config->use_occtl); } else if (strcmp(name, "try-mtu-discovery") == 0) { - READ_TF(config->try_mtu); + if (!WARN_ON_VHOST(vhost->name, "try-mtu-discovery", try_mtu)) + READ_TF(config->try_mtu); } else if (strcmp(name, "ping-leases") == 0) { - READ_TF(config->ping_leases); + if (!WARN_ON_VHOST(vhost->name, "ping_leases", ping_leases)) + READ_TF(config->ping_leases); } else if (strcmp(name, "restrict-user-to-routes") == 0) { READ_TF(config->restrict_user_to_routes); } else if (strcmp(name, "restrict-user-to-ports") == 0) { @@ -729,29 +927,38 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co } else if (strcmp(name, "session-timeout") == 0) { READ_NUMERIC(config->session_timeout); } else if (strcmp(name, "auth-timeout") == 0) { - READ_NUMERIC(config->auth_timeout); + if (!WARN_ON_VHOST(vhost->name, "auth-timeout", auth_timeout)) + READ_NUMERIC(config->auth_timeout); } else if (strcmp(name, "idle-timeout") == 0) { READ_NUMERIC(config->idle_timeout); } else if (strcmp(name, "mobile-idle-timeout") == 0) { READ_NUMERIC(config->mobile_idle_timeout); } else if (strcmp(name, "max-clients") == 0) { - READ_NUMERIC(config->max_clients); + if (!WARN_ON_VHOST(vhost->name, "max-clients", max_clients)) + READ_NUMERIC(config->max_clients); } else if (strcmp(name, "min-reauth-time") == 0) { - READ_NUMERIC(config->min_reauth_time); + if (!WARN_ON_VHOST(vhost->name, "min-reauth-time", min_reauth_time)) + READ_NUMERIC(config->min_reauth_time); } else if (strcmp(name, "ban-reset-time") == 0) { - READ_NUMERIC(config->ban_reset_time); + if (!WARN_ON_VHOST(vhost->name, "ban-reset-time", ban_reset_time)) + READ_NUMERIC(config->ban_reset_time); } else if (strcmp(name, "max-ban-score") == 0) { - READ_NUMERIC( config->max_ban_score); + if (!WARN_ON_VHOST(vhost->name, "max-ban-score", max_ban_score)) + READ_NUMERIC( config->max_ban_score); } else if (strcmp(name, "ban-points-wrong-password") == 0) { - READ_NUMERIC(config->ban_points_wrong_password); + if (!WARN_ON_VHOST(vhost->name, "ban-points-wrong-password", ban_points_wrong_password)) + READ_NUMERIC(config->ban_points_wrong_password); } else if (strcmp(name, "ban-points-connection") == 0) { - READ_NUMERIC(config->ban_points_connect); + if (!WARN_ON_VHOST(vhost->name, "ban-points-connection", ban_points_connect)) + READ_NUMERIC(config->ban_points_connect); } else if (strcmp(name, "ban-points-kkdcp") == 0) { - READ_NUMERIC(config->ban_points_kkdcp); + if (!WARN_ON_VHOST(vhost->name, "ban-points-kkdcp", ban_points_kkdcp)) + READ_NUMERIC(config->ban_points_kkdcp); } else if (strcmp(name, "max-same-clients") == 0) { READ_NUMERIC(config->max_same_clients); } else if (strcmp(name, "device") == 0) { - READ_STATIC_STRING(config->network.name); + if (!WARN_ON_VHOST(vhost->name, "device", network.name)) + READ_STATIC_STRING(config->network.name); } else if (strcmp(name, "cgroup") == 0) { READ_STRING(config->cgroup); } else if (strcmp(name, "proxy-url") == 0) { @@ -798,7 +1005,7 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co } else if (strcmp(name, "default-select-group") == 0) { READ_STRING(config->default_select_group); } else if (strcmp(name, "auto-select-group") == 0) { - READ_TF(ctx->auto_select_group); + READ_TF(vhost->auto_select_group); } else if (strcmp(name, "select-group") == 0) { READ_MULTI_BRACKET_LINE(config->group_list, config->friendly_group_list, @@ -816,15 +1023,17 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co } else if (strcmp(name, "ipv6-nbns") == 0) { READ_MULTI_LINE(config->network.nbns, config->network.nbns_size); } else if (strcmp(name, "route-add-cmd") == 0) { - READ_STRING(config->route_add_cmd); + if (!WARN_ON_VHOST(vhost->name, "route-add-cmd", route_add_cmd)) + READ_STRING(config->route_add_cmd); } else if (strcmp(name, "route-del-cmd") == 0) { - READ_STRING(config->route_del_cmd); + if (!WARN_ON_VHOST(vhost->name, "route-del-cmd", route_del_cmd)) + READ_STRING(config->route_del_cmd); } else if (strcmp(name, "config-per-user") == 0) { READ_STRING(config->per_user_dir); } else if (strcmp(name, "config-per-group") == 0) { READ_STRING(config->per_group_dir); } else if (strcmp(name, "expose-iroutes") == 0) { - READ_TF(ctx->expose_iroutes); + READ_TF(vhost->expose_iroutes); } else if (strcmp(name, "default-user-config") == 0) { READ_STRING(config->default_user_conf); } else if (strcmp(name, "default-group-config") == 0) { @@ -839,50 +1048,20 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co return 0; } -static void parse_cfg_file(void *pool, const char *file, struct perm_cfg_st *perm_config, unsigned reload) + +static void parse_cfg_file(void *pool, const char *file, struct list_head *head, + unsigned reload) { int ret; struct cfg_st *config; struct ini_ctx_st ctx; + vhost_cfg_st *vhost = NULL; + vhost_cfg_st *defvhost; memset(&ctx, 0, sizeof(ctx)); ctx.file = file; ctx.reload = reload; - ctx.perm_config = perm_config; - - perm_config->config = talloc_zero(perm_config, struct cfg_st); - if (perm_config->config == NULL) - exit(1); - - config = perm_config->config; - config->usage_count = talloc_zero(config, int); - if (config->usage_count == NULL) { - fprintf(stderr, ERRSTR"memory\n"); - exit(1); - } - - /* set config (no-zero) default vals - */ - if (reload == 0) { - perm_config->sup_config_type = SUP_CONFIG_FILE; - list_head_init(&perm_config->attic); - } - config->mobile_idle_timeout = (unsigned)-1; - config->no_compress_limit = DEFAULT_NO_COMPRESS_LIMIT; - config->rekey_time = 24*60*60; - config->cookie_timeout = DEFAULT_COOKIE_RECON_TIMEOUT; - config->auth_timeout = DEFAULT_AUTH_TIMEOUT_SECS; - config->ban_reset_time = DEFAULT_BAN_RESET_TIME; - config->max_ban_score = DEFAULT_MAX_BAN_SCORE; - config->ban_points_wrong_password = DEFAULT_PASSWORD_POINTS; - config->ban_points_connect = DEFAULT_CONNECT_POINTS; - config->ban_points_kkdcp = DEFAULT_KKDCP_POINTS; - config->dpd = DEFAULT_DPD_TIME; - config->network.ipv6_subnet_prefix = 128; - config->dtls_legacy = 1; - config->dtls_psk = 1; - config->predictable_ips = 1; - config->use_utmp = 1; + ctx.head = head; /* parse configuration */ @@ -895,92 +1074,135 @@ static void parse_cfg_file(void *pool, const char *file, struct perm_cfg_st *per exit(1); } - if (reload == 0) { - if (ctx.auth_size == 0) { - fprintf(stderr, ERRSTR"the 'auth' configuration option was not specified!\n"); - exit(1); + /* apply configuration not yet applied. + * We start from the last, which is the default server (firstly + * added). + */ + list_for_each_rev(head, vhost, list) { + config = vhost->perm_config.config; + + if (vhost->auth_init == 0) { + if (vhost->auth_size == 0) { + fprintf(stderr, ERRSTR"%sthe 'auth' configuration option was not specified!\n", PREFIX_VHOST(vhost)); + exit(1); + } + + figure_auth_funcs(vhost, PREFIX_VHOST(vhost), &vhost->perm_config, vhost->auth, vhost->auth_size, 1); + figure_auth_funcs(vhost, PREFIX_VHOST(vhost), &vhost->perm_config, vhost->eauth, vhost->eauth_size, 0); + + figure_acct_funcs(vhost, PREFIX_VHOST(vhost), &vhost->perm_config, vhost->acct); + + vhost->auth_init = 1; } - figure_auth_funcs(perm_config, ctx.auth, ctx.auth_size, 1); - figure_auth_funcs(perm_config, ctx.eauth, ctx.eauth_size, 0); + if (vhost->auto_select_group != 0 && vhost->perm_config.auth[0].amod != NULL && vhost->perm_config.auth[0].amod->group_list != NULL) { + vhost->perm_config.auth[0].amod->group_list(config, vhost->perm_config.auth[0].additional, &config->group_list, &config->group_list_size); + } - figure_acct_funcs(perm_config, ctx.acct); - } + if (vhost->expose_iroutes != 0) { + load_iroutes(config); + } - if (ctx.auto_select_group != 0 && perm_config->auth[0].amod != NULL && perm_config->auth[0].amod->group_list != NULL) { - perm_config->auth[0].amod->group_list(config, perm_config->auth[0].additional, &config->group_list, &config->group_list_size); - } + if (vhost->name) + defvhost = default_vhost(head); + else + defvhost = NULL; - if (ctx.expose_iroutes != 0) { - load_iroutes(config); - } + /* this check copies mandatory fields from default vhost if needed */ + check_cfg(vhost, defvhost, reload); + + tls_load_files(NULL, vhost); + tls_load_prio(NULL, vhost); + tls_reload_crl(NULL, vhost, 1); #ifdef HAVE_GSSAPI - if (ctx.urlfw_size > 0) { - parse_kkdcp(config, ctx.urlfw, ctx.urlfw_size); - talloc_free(ctx.urlfw); - } + if (vhost->urlfw_size > 0) { + parse_kkdcp(config, vhost->urlfw, vhost->urlfw_size); + talloc_free(vhost->urlfw); + vhost->urlfw = NULL; + } #endif - - fprintf(stderr, NOTESTR"setting '%s' as supplemental config option\n", sup_config_name(perm_config->sup_config_type)); + fprintf(stderr, NOTESTR"%ssetting '%s' as supplemental config option\n", + PREFIX_VHOST(vhost), + sup_config_name(vhost->perm_config.sup_config_type)); + } } /* sanity checks on config */ -static void check_cfg(struct perm_cfg_st *perm_config, unsigned silent) +static void check_cfg(vhost_cfg_st *vhost, vhost_cfg_st *defvhost, unsigned silent) { unsigned j, i; - struct cfg_st *config = perm_config->config; + struct cfg_st *config; - if (perm_config->auth[0].enabled == 0) { - fprintf(stderr, ERRSTR"no authentication method was specified!\n"); + config = vhost->perm_config.config; + + if (vhost->perm_config.auth[0].enabled == 0) { + fprintf(stderr, ERRSTR"%sno authentication method was specified!\n", PREFIX_VHOST(vhost)); exit(1); } - if (perm_config->socket_file_prefix == NULL) { - fprintf(stderr, ERRSTR"the 'socket-file' configuration option must be specified!\n"); - exit(1); + if (vhost->perm_config.socket_file_prefix == NULL) { + if (vhost->name) { + vhost->perm_config.socket_file_prefix = talloc_strdup(vhost, defvhost->perm_config.socket_file_prefix); + } else { + /* The 'socket-file' is not mandatory on main server */ + fprintf(stderr, ERRSTR"%sthe 'socket-file' configuration option must be specified!\n", PREFIX_VHOST(vhost)); + exit(1); + } } - if (perm_config->cert_size == 0 || perm_config->key_size == 0) { - fprintf(stderr, ERRSTR"the 'server-cert' and 'server-key' configuration options must be specified!\n"); + if (vhost->perm_config.port == 0 && vhost->perm_config.unix_conn_file == NULL) { + if (defvhost) { + if (vhost->perm_config.port) + vhost->perm_config.port = vhost->perm_config.port; + else if (vhost->perm_config.unix_conn_file) + vhost->perm_config.unix_conn_file = talloc_strdup(vhost, vhost->perm_config.unix_conn_file); + } else { + fprintf(stderr, ERRSTR"%sthe tcp-port option is mandatory!\n", PREFIX_VHOST(vhost)); + exit(1); + } + } + + if (vhost->perm_config.cert_size == 0 || vhost->perm_config.key_size == 0) { + fprintf(stderr, ERRSTR"%sthe 'server-cert' and 'server-key' configuration options must be specified!\n", PREFIX_VHOST(vhost)); exit(1); } if (config->network.ipv4 == NULL && config->network.ipv6 == NULL) { - fprintf(stderr, ERRSTR"no ipv4-network or ipv6-network options set.\n"); + fprintf(stderr, ERRSTR"%sno ipv4-network or ipv6-network options set.\n", PREFIX_VHOST(vhost)); exit(1); } if (config->network.ipv4 != NULL && config->network.ipv4_netmask == NULL) { - fprintf(stderr, ERRSTR"no mask found for IPv4 network.\n"); + fprintf(stderr, ERRSTR"%sno mask found for IPv4 network.\n", PREFIX_VHOST(vhost)); exit(1); } if (config->network.ipv6 != NULL && config->network.ipv6_prefix == 0) { - fprintf(stderr, ERRSTR"no prefix found for IPv6 network.\n"); + fprintf(stderr, ERRSTR"%sno prefix found for IPv6 network.\n", PREFIX_VHOST(vhost)); exit(1); } if (config->banner && strlen(config->banner) > MAX_BANNER_SIZE) { - fprintf(stderr, ERRSTR"banner size is too long\n"); + fprintf(stderr, ERRSTR"%sbanner size is too long\n", PREFIX_VHOST(vhost)); exit(1); } - if (perm_config->cert_size != perm_config->key_size) { - fprintf(stderr, ERRSTR"the specified number of keys doesn't match the certificates\n"); + if (vhost->perm_config.cert_size != vhost->perm_config.key_size) { + fprintf(stderr, ERRSTR"%sthe specified number of keys doesn't match the certificates\n", PREFIX_VHOST(vhost)); exit(1); } - if ((perm_config->auth[0].type & AUTH_TYPE_CERTIFICATE) && perm_config->auth_methods == 1) { + if ((vhost->perm_config.auth[0].type & AUTH_TYPE_CERTIFICATE) && vhost->perm_config.auth_methods == 1) { if (config->cisco_client_compat == 0) config->cert_req = GNUTLS_CERT_REQUIRE; else config->cert_req = GNUTLS_CERT_REQUEST; } else { unsigned i; - for (i=0;iauth_methods;i++) { - if (perm_config->auth[i].type & AUTH_TYPE_CERTIFICATE) { + for (i=0;iperm_config.auth_methods;i++) { + if (vhost->perm_config.auth[i].type & AUTH_TYPE_CERTIFICATE) { config->cert_req = GNUTLS_CERT_REQUEST; break; } @@ -988,70 +1210,68 @@ static void check_cfg(struct perm_cfg_st *perm_config, unsigned silent) } if (config->cert_req != 0 && config->cert_user_oid == NULL) { - fprintf(stderr, ERRSTR"a certificate is requested by the option 'cert-user-oid' is not set\n"); + fprintf(stderr, ERRSTR"%sa certificate is requested by the option 'cert-user-oid' is not set\n", PREFIX_VHOST(vhost)); exit(1); } if (config->cert_req != 0 && config->cert_user_oid != NULL) { if (!c_isdigit(config->cert_user_oid[0]) && strcmp(config->cert_user_oid, "SAN(rfc822name)") != 0) { - fprintf(stderr, ERRSTR"the option 'cert-user-oid' has a unsupported value\n"); + fprintf(stderr, ERRSTR"%sthe option 'cert-user-oid' has a unsupported value\n", PREFIX_VHOST(vhost)); exit(1); } } - if (perm_config->unix_conn_file != NULL && (config->cert_req != 0)) { + if (vhost->perm_config.unix_conn_file != NULL && (config->cert_req != 0)) { if (config->listen_proxy_proto == 0) { - fprintf(stderr, ERRSTR"the option 'listen-clear-file' cannot be combined with 'auth=certificate'\n"); + fprintf(stderr, ERRSTR"%sthe option 'listen-clear-file' cannot be combined with 'auth=certificate'\n", PREFIX_VHOST(vhost)); exit(1); } } - if (perm_config->cert && perm_config->cert_hash == NULL) { - perm_config->cert_hash = calc_sha1_hash(perm_config, perm_config->cert[0], 1); + if (vhost->perm_config.cert && vhost->perm_config.cert_hash == NULL) { + vhost->perm_config.cert_hash = calc_sha1_hash(vhost->pool, vhost->perm_config.cert[0], 1); } if (config->xml_config_file) { - config->xml_config_hash = calc_sha1_hash(config, config->xml_config_file, 0); - if (config->xml_config_hash == NULL && perm_config->chroot_dir != NULL) { + config->xml_config_hash = calc_sha1_hash(vhost->pool, config->xml_config_file, 0); + if (config->xml_config_hash == NULL && vhost->perm_config.chroot_dir != NULL) { char path[_POSIX_PATH_MAX]; - snprintf(path, sizeof(path), "%s/%s", perm_config->chroot_dir, config->xml_config_file); - config->xml_config_hash = calc_sha1_hash(config, path, 0); + snprintf(path, sizeof(path), "%s/%s", vhost->perm_config.chroot_dir, config->xml_config_file); + config->xml_config_hash = calc_sha1_hash(vhost->pool, path, 0); if (config->xml_config_hash == NULL) { - fprintf(stderr, ERRSTR"cannot open file '%s'\n", path); + fprintf(stderr, ERRSTR"%scannot open file '%s'\n", PREFIX_VHOST(vhost), path); exit(1); } } if (config->xml_config_hash == NULL) { - fprintf(stderr, ERRSTR"cannot open file '%s'\n", config->xml_config_file); + fprintf(stderr, ERRSTR"%scannot open file '%s'\n", PREFIX_VHOST(vhost), config->xml_config_file); exit(1); } } - if (config->keepalive == 0) - config->keepalive = 3600; + if (config->priorities == NULL) { + /* on vhosts assign the main host priorities */ + if (defvhost) { + config->priorities = talloc_strdup(config, defvhost->perm_config.config->priorities); + } else { + config->priorities = talloc_strdup(config, "NORMAL:%SERVER_PRECEDENCE:%COMPAT"); + } + } - if (config->dpd == 0) - config->dpd = 60; + if (vhost->perm_config.occtl_socket_file == NULL) + vhost->perm_config.occtl_socket_file = talloc_strdup(vhost, OCCTL_UNIX_SOCKET); - if (config->priorities == NULL) - config->priorities = talloc_strdup(config, "NORMAL:%SERVER_PRECEDENCE:%COMPAT"); - - if (perm_config->occtl_socket_file == NULL) - perm_config->occtl_socket_file = talloc_strdup(perm_config, OCCTL_UNIX_SOCKET); - - if (perm_config->stats_reset_time <= 0) - perm_config->stats_reset_time = 24*60*60*7; /* weekly */ if (config->network.ipv6_prefix && config->network.ipv6_prefix >= config->network.ipv6_subnet_prefix) { - fprintf(stderr, ERRSTR"the subnet prefix (%u) cannot be smaller or equal to network's (%u)\n", - config->network.ipv6_subnet_prefix, config->network.ipv6_prefix); + fprintf(stderr, ERRSTR"%sthe subnet prefix (%u) cannot be smaller or equal to network's (%u)\n", + PREFIX_VHOST(vhost), config->network.ipv6_subnet_prefix, config->network.ipv6_prefix); exit(1); } if (config->network.name == NULL) { - fprintf(stderr, ERRSTR"the 'device' configuration option must be specified!\n"); + fprintf(stderr, ERRSTR"%sthe 'device' configuration option must be specified!\n", PREFIX_VHOST(vhost)); exit(1); } @@ -1060,21 +1280,21 @@ static void check_cfg(struct perm_cfg_st *perm_config, unsigned silent) if (config->cisco_client_compat) { if (!config->dtls_legacy && !silent) { - fprintf(stderr, NOTESTR"the cisco-client-compat option implies dtls-legacy = true; enabling\n"); + fprintf(stderr, NOTESTR"%sthe cisco-client-compat option implies dtls-legacy = true; enabling\n", PREFIX_VHOST(vhost)); } config->dtls_legacy = 1; } - if (perm_config->unix_conn_file) { + if (vhost->perm_config.unix_conn_file) { if (config->dtls_psk && !silent) { - fprintf(stderr, NOTESTR"'dtls-psk' cannot be combined with unix socket file\n"); + fprintf(stderr, NOTESTR"%s'dtls-psk' cannot be combined with unix socket file\n", PREFIX_VHOST(vhost)); } config->dtls_psk = 0; } if (config->match_dtls_and_tls) { if (config->dtls_legacy) { - fprintf(stderr, ERRSTR"'match-tls-dtls-ciphers' cannot be applied when 'dtls-legacy' or 'cisco-client-compat' is on\n"); + fprintf(stderr, ERRSTR"%s'match-tls-dtls-ciphers' cannot be applied when 'dtls-legacy' or 'cisco-client-compat' is on\n", PREFIX_VHOST(vhost)); exit(1); } } @@ -1087,7 +1307,7 @@ static void check_cfg(struct perm_cfg_st *perm_config, unsigned silent) #if !defined(HAVE_LIBSECCOMP) if (config->isolate != 0 && !silent) { - fprintf(stderr, ERRSTR"'isolate-workers' is set to true, but not compiled with seccomp or Linux namespaces support\n"); + fprintf(stderr, ERRSTR"%s'isolate-workers' is set to true, but not compiled with seccomp or Linux namespaces support\n", PREFIX_VHOST(vhost)); } #endif @@ -1112,15 +1332,15 @@ static void check_cfg(struct perm_cfg_st *perm_config, unsigned silent) for (j=0;jnetwork.dns_size;j++) { if (strcmp(config->network.dns[j], "local") == 0) { - fprintf(stderr, ERRSTR"the 'local' DNS keyword is no longer supported.\n"); + fprintf(stderr, ERRSTR"%sthe 'local' DNS keyword is no longer supported.\n", PREFIX_VHOST(vhost)); exit(1); } } if (config->per_user_dir || config->per_group_dir) { - if (perm_config->sup_config_type != SUP_CONFIG_FILE) { - fprintf(stderr, ERRSTR"specified config-per-user or config-per-group but supplemental config is '%s'\n", - sup_config_name(perm_config->sup_config_type)); + if (vhost->perm_config.sup_config_type != SUP_CONFIG_FILE) { + fprintf(stderr, ERRSTR"%sspecified config-per-user or config-per-group but supplemental config is '%s'\n", + PREFIX_VHOST(vhost), sup_config_name(vhost->perm_config.sup_config_type)); exit(1); } } @@ -1162,14 +1382,13 @@ void usage(void) fprintf(stderr, "Please send bug reports to: "PACKAGE_BUGREPORT"\n"); } -int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config) +int cmd_parser (void *pool, int argc, char **argv, struct list_head *head) { unsigned test_only = 0; int c; + vhost_cfg_st *vhost; - *config = talloc_zero(pool, struct perm_cfg_st); - if (*config == NULL) - exit(1); + vhost = vhost_add(pool, head, NULL, 0); while (1) { c = getopt_long(argc, argv, "d:c:p:ftvh", long_options, NULL); @@ -1178,7 +1397,7 @@ int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config) switch(c) { case 'f': - (*config)->foreground = 1; + vhost->perm_config.foreground = 1; break; case 'p': strlcpy(pid_file, optarg, sizeof(pid_file)); @@ -1187,7 +1406,7 @@ int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config) strlcpy(cfg_file, optarg, sizeof(cfg_file)); break; case 'd': - (*config)->debug = atoi(optarg); + vhost->perm_config.debug = atoi(optarg); break; case 't': test_only = 1; @@ -1212,9 +1431,7 @@ int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config) exit(1); } - parse_cfg_file(pool, cfg_file, *config, 0); - - check_cfg(*config, 0); + parse_cfg_file(pool, cfg_file, head, 0); if (test_only) exit(0); @@ -1223,40 +1440,61 @@ int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config) } -static void archive_cfg(struct perm_cfg_st* perm_config) +static void archive_cfg(struct list_head *head) { attic_entry_st *e; + struct vhost_cfg_st* vhost = NULL; - /* we don't clear anything as it may be referenced by some - * client (proc_st). We move everything to attic and - * once nothing is in use we clear that */ + list_for_each(head, vhost, list) { + /* we don't clear anything as it may be referenced by some + * client (proc_st). We move everything to attic and + * once nothing is in use we clear that */ - e = talloc(perm_config, attic_entry_st); - if (e == NULL) { - /* we leak, but better than crashing */ - return; - } + e = talloc(vhost, attic_entry_st); + if (e == NULL) { + /* we leak, but better than crashing */ + return; + } - e->usage_count = perm_config->config->usage_count; + e->usage_count = vhost->perm_config.config->usage_count; - /* we rely on talloc doing that recursively */ - talloc_steal(e, perm_config->config); - perm_config->config = NULL; + /* we rely on talloc doing that recursively */ + talloc_steal(e, vhost->perm_config.config); + vhost->perm_config.config = NULL; - if (e->usage_count == NULL || *e->usage_count == 0) { - talloc_free(e); - } else { - list_add(&perm_config->attic, &e->list); + if (e->usage_count == NULL || *e->usage_count == 0) { + talloc_free(e); + } else { + list_add(&vhost->perm_config.attic, &e->list); + } } return; } -void clear_cfg(struct perm_cfg_st* perm_config) +static void clear_cfg(struct list_head *head) { - /* we rely on talloc doing that recursively */ - talloc_free(perm_config->config); - perm_config->config = NULL; + vhost_cfg_st *cpos = NULL, *ctmp; + + list_for_each_safe(head, cpos, ctmp, list) { + /* we rely on talloc freeing recursively */ + talloc_free(cpos->perm_config.config); + cpos->perm_config.config = NULL; + } + + return; +} + +void clear_vhosts(struct list_head *head) +{ + vhost_cfg_st *vhost = NULL, *ctmp; + + list_for_each_safe(head, vhost, ctmp, list) { + tls_vhost_deinit(vhost); + /* we rely on talloc freeing recursively */ + talloc_free(vhost->perm_config.config); + vhost->perm_config.config = NULL; + } return; } @@ -1312,16 +1550,24 @@ static void print_version(void) } -void reload_cfg_file(void *pool, struct perm_cfg_st* perm_config, unsigned archive) +void reload_cfg_file(void *pool, struct list_head *configs, unsigned archive) { + struct vhost_cfg_st* vhost = NULL; + + /* Archive or clear any non-permanent configs */ if (archive) - archive_cfg(perm_config); + archive_cfg(configs); else - clear_cfg(perm_config); + clear_cfg(configs); - parse_cfg_file(pool, cfg_file, perm_config, 1); + /* Create new config structures and apply defaults */ + list_for_each(configs, vhost, list) { + if (vhost->perm_config.config == NULL) + cfg_new(vhost, 1); + } - check_cfg(perm_config, 1); + /* parse the config again */ + parse_cfg_file(pool, cfg_file, configs, 1); return; } @@ -1379,15 +1625,18 @@ int _add_multi_line_val(void *pool, char ***varname, size_t *num, return 0; } -void clear_old_configs(struct perm_cfg_st* config) +void clear_old_configs(struct list_head *head) { attic_entry_st *e = NULL, *pos; + vhost_cfg_st *cpos = NULL; - /* go through the attic and clear old configurations if unused */ - list_for_each_safe(&config->attic, e, pos, list) { - if (*e->usage_count == 0) { - list_del(&e->list); - talloc_free(e); + list_for_each(head, cpos, list) { + /* go through the attic and clear old configurations if unused */ + list_for_each_safe(&cpos->perm_config.attic, e, pos, list) { + if (*e->usage_count == 0) { + list_del(&e->list); + talloc_free(e); + } } } } diff --git a/src/ctl.proto b/src/ctl.proto index 8334f221..fca898b9 100644 --- a/src/ctl.proto +++ b/src/ctl.proto @@ -75,6 +75,7 @@ message user_info_rep repeated fw_port_st fw_ports = 31; required bytes safe_id = 32; /* a value derived from the cookie */ + required string vhost = 33; } message user_list_rep diff --git a/src/icmp-ping.c b/src/icmp-ping.c index f26fa0bd..a3dc1f2b 100644 --- a/src/icmp-ping.c +++ b/src/icmp-ping.c @@ -180,7 +180,7 @@ int icmp_ping4(main_server_st * s, struct sockaddr_in *addr1) uint16_t id1; unsigned gotreply = 0, unreachable = 0; - if (s->config->ping_leases == 0) + if (GETCONFIG(s)->ping_leases == 0) return 0; gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1)); @@ -269,7 +269,7 @@ int icmp_ping6(main_server_st * s, unsigned gotreply = 0, unreachable = 0; time_t now; - if (s->config->ping_leases == 0) + if (GETCONFIG(s)->ping_leases == 0) return 0; gnutls_rnd(GNUTLS_RND_NONCE, &id1, sizeof(id1)); diff --git a/src/ip-lease.c b/src/ip-lease.c index c6c51df6..014d1d2b 100644 --- a/src/ip-lease.c +++ b/src/ip-lease.c @@ -160,16 +160,18 @@ int get_ipv4_lease(main_server_st* s, struct proc_st* proc) unsigned i; unsigned max_loops = MAX_IP_TRIES; int ret; - const char* c_network, *c_netmask; + const char *c_network, *c_netmask; char buf[64]; /* Our IP accounting */ if (proc->config->ipv4_net && proc->config->ipv4_netmask) { + /* We only read from user/group configuration as this + * is updated with the current vhost information */ c_network = proc->config->ipv4_net; c_netmask = proc->config->ipv4_netmask; } else { - c_network = s->config->network.ipv4; - c_netmask = s->config->network.ipv4_netmask; + c_network = proc->vhost->perm_config.config->network.ipv4; + c_netmask = proc->vhost->perm_config.config->network.ipv4_netmask; } if (c_network == NULL || c_netmask == NULL) { @@ -322,8 +324,8 @@ int get_ipv6_lease(main_server_st* s, struct proc_st* proc) struct sockaddr_storage tmp, mask, network, rnd, subnet_mask; unsigned i, max_loops = MAX_IP_TRIES; - const char* c_network; - unsigned prefix, subnet_prefix; + const char* c_network = NULL; + unsigned prefix, subnet_prefix ; int ret; char buf[64]; @@ -332,9 +334,9 @@ int get_ipv6_lease(main_server_st* s, struct proc_st* proc) prefix = proc->config->ipv6_prefix; subnet_prefix = proc->config->ipv6_subnet_prefix; } else { - c_network = s->config->network.ipv6; - prefix = s->config->network.ipv6_prefix; - subnet_prefix = s->config->network.ipv6_subnet_prefix; + c_network = proc->vhost->perm_config.config->network.ipv6; + prefix = proc->vhost->perm_config.config->network.ipv6_prefix; + subnet_prefix = proc->vhost->perm_config.config->network.ipv6_subnet_prefix; } if (c_network == NULL || prefix == 0 || subnet_prefix == 0) { diff --git a/src/ipc.proto b/src/ipc.proto index 52acab7f..44cc8c3f 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -93,6 +93,7 @@ message session_resume_fetch_msg * and contains the address of the client. */ required bytes cli_addr = 2; + optional string vhost = 3; } /* RESUME_STORE_REQ */ @@ -104,6 +105,7 @@ message session_resume_store_req_msg * and contains the address of the client. */ required bytes cli_addr = 3; + optional string vhost = 4; } /* RESUME_FETCH_REP */ @@ -211,6 +213,7 @@ message sec_auth_init_msg required uint32 auth_type = 9 [default = 0]; optional string our_ip = 10; optional string user_agent = 11; + optional string vhost = 12; } /* SEC_AUTH_CONT */ @@ -238,12 +241,14 @@ message sec_op_msg optional uint32 key_idx = 1; required bytes data = 2; required uint32 sig = 3; + optional string vhost = 4; } message sec_get_pk_msg { required uint32 key_idx = 1; required uint32 pk = 2; + optional string vhost = 3; } @@ -297,6 +302,7 @@ message secm_session_reply_msg required uint32 ipv4_seed = 8; required bytes sid = 9; required bool tls_auth_ok = 10; + optional string vhost = 11; } /* internal struct */ @@ -313,6 +319,7 @@ message cookie_int_msg required uint32 expires = 9; required uint32 status = 10; /* the authentication status (PS_*) */ required bool in_use = 11; + required string vhost = 12; } /* SECM_LIST_COOKIES - no content */ diff --git a/src/log.c b/src/log.c index 55623c7d..71018e34 100644 --- a/src/log.c +++ b/src/log.c @@ -35,24 +35,32 @@ void __attribute__ ((format(printf, 3, 4))) _oclog(const worker_st * ws, int priority, const char *fmt, ...) { char buf[512]; + char name[MAX_USERNAME_SIZE+MAX_HOSTNAME_SIZE+3]; const char* ip; va_list args; + int debug_prio; + unsigned have_vhosts; - if (priority == LOG_DEBUG && ws->perm_config->debug < DEBUG_INFO) + if (ws->vhost) + debug_prio = WSPCONFIG(ws)->debug; + else + debug_prio = GETPCONFIG(ws)->debug; + + if (priority == LOG_DEBUG && debug_prio < DEBUG_INFO) return; if (priority == LOG_HTTP_DEBUG) { - if (ws->perm_config->debug < DEBUG_HTTP) + if (debug_prio < DEBUG_HTTP) return; else priority = LOG_INFO; } else if (priority == LOG_TRANSFER_DEBUG) { - if (ws->perm_config->debug < DEBUG_TRANSFERRED) + if (debug_prio < DEBUG_TRANSFERRED) return; else priority = LOG_DEBUG; } else if (priority == LOG_SENSITIVE) { - if (ws->perm_config->debug < DEBUG_SENSITIVE) + if (debug_prio < DEBUG_SENSITIVE) return; else priority = LOG_DEBUG; @@ -64,14 +72,18 @@ void __attribute__ ((format(printf, 3, 4))) vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - if (ip) { - if (ws->username[0] == 0) - syslog(priority, "worker: %s %s", ip, buf); - else - syslog(priority, "worker[%s]: %s %s", ws->username, ip, buf); - } else { - syslog(priority, "worker: [unknown] %s", buf); - } + have_vhosts = HAVE_VHOSTS(ws); + + if (have_vhosts && ws->username[0] != 0) { + snprintf(name, sizeof(name), "[%s%s]", PREFIX_VHOST(ws->vhost), ws->username); + } else if (have_vhosts && ws->username[0] == 0 && ws->vhost && ws->vhost->name) { + snprintf(name, sizeof(name), "[vhost:%s]", VHOSTNAME(ws->vhost)); + } else if (ws->username[0] != 0) { + snprintf(name, sizeof(name), "[%s]", ws->username); + } else + name[0] = 0; + + syslog(priority, "worker%s: %s %s", name, ip?ip:"[unknown]", buf); return; } @@ -83,19 +95,27 @@ void __attribute__ ((format(printf, 4, 5))) { char buf[512]; char ipbuf[128]; + char name[MAX_USERNAME_SIZE+MAX_HOSTNAME_SIZE+3]; const char* ip = NULL; va_list args; + int debug_prio; + unsigned have_vhosts; - if (priority == LOG_DEBUG && s->perm_config->debug < 3) + if (s) + debug_prio = GETPCONFIG(s)->debug; + else + debug_prio = 1; + + if (priority == LOG_DEBUG && debug_prio < 3) return; if (priority == LOG_HTTP_DEBUG) { - if (s->perm_config->debug < DEBUG_HTTP) + if (debug_prio < DEBUG_HTTP) return; else priority = LOG_DEBUG; } else if (priority == LOG_TRANSFER_DEBUG) { - if (s->perm_config->debug < DEBUG_TRANSFERRED) + if (debug_prio < DEBUG_TRANSFERRED) return; else priority = LOG_DEBUG; @@ -104,20 +124,26 @@ void __attribute__ ((format(printf, 4, 5))) if (proc) { ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len, ipbuf, sizeof(ipbuf)); + } else { + ip = ""; } va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - if (ip) { - if (proc->username[0] == 0) - syslog(priority, "main: %s %s", ip, buf); - else - syslog(priority, "main[%s]: %s %s", proc->username, ip, buf); - } else { - syslog(priority, "main: %s", buf); - } + have_vhosts = s?HAVE_VHOSTS(s):0; + + if (have_vhosts && proc && proc->username[0] != 0) { + snprintf(name, sizeof(name), "[%s%s]", PREFIX_VHOST(proc->vhost), proc->username); + } else if (have_vhosts && proc && proc->username[0] == 0 && proc->vhost && proc->vhost->name) { + snprintf(name, sizeof(name), "[vhost:%s]", VHOSTNAME(proc->vhost)); + } else if (proc && proc->username[0] != 0) { + snprintf(name, sizeof(name), "[%s]", proc->username); + } else + name[0] = 0; + + syslog(priority, "main%s:%s %s", name, ip?ip:"[unknown]", buf); return; } @@ -129,8 +155,14 @@ void mslog_hex(const main_server_st * s, const struct proc_st* proc, int ret; size_t buf_size; gnutls_datum_t data = {bin, bin_size}; + int debug_prio; - if (priority == LOG_DEBUG && s->perm_config->debug == 0) + if (s) + debug_prio = GETPCONFIG(s)->debug; + else + debug_prio = 1; + + if (priority == LOG_DEBUG && debug_prio == 0) return; if (b64) { @@ -154,8 +186,14 @@ void oclog_hex(const worker_st* ws, int priority, int ret; size_t buf_size; gnutls_datum_t data = {bin, bin_size}; + int debug_prio; - if (priority == LOG_DEBUG && ws->perm_config->debug == 0) + if (ws->vhost) + debug_prio = WSPCONFIG(ws)->debug; + else + debug_prio = GETPCONFIG(ws)->debug; + + if (priority == LOG_DEBUG && debug_prio == 0) return; if (b64) { @@ -180,7 +218,7 @@ void seclog_hex(const struct sec_mod_st* sec, int priority, size_t buf_size; gnutls_datum_t data = {bin, bin_size}; - if (priority == LOG_DEBUG && sec->perm_config->debug == 0) + if (priority == LOG_DEBUG && GETPCONFIG(sec)->debug == 0) return; if (b64) { diff --git a/src/main-ban.c b/src/main-ban.c index 6fcfaa49..019a1ff9 100644 --- a/src/main-ban.c +++ b/src/main-ban.c @@ -113,13 +113,13 @@ int add_ip_to_ban_list(main_server_st *s, const unsigned char *ip, unsigned ip_s struct ban_entry_st *e; ban_entry_st t; time_t now = time(0); - time_t expiration = now + s->config->min_reauth_time; + time_t expiration = now + GETCONFIG(s)->min_reauth_time; int ret = 0; char str_ip[MAX_IP_STR]; const char *p_str_ip = NULL; unsigned print_msg; - if (db == NULL || s->config->max_ban_score == 0 || ip == NULL || (ip_size != 4 && ip_size != 16)) + if (db == NULL || GETCONFIG(s)->max_ban_score == 0 || ip == NULL || (ip_size != 4 && ip_size != 16)) return 0; memcpy(t.ip.ip, ip, ip_size); @@ -144,7 +144,7 @@ int add_ip_to_ban_list(main_server_st *s, const unsigned char *ip, unsigned ip_s goto fail; } } else { - if (now > e->last_reset + s->config->ban_reset_time) { + if (now > e->last_reset + GETCONFIG(s)->ban_reset_time) { e->score = 0; e->last_reset = now; } @@ -153,7 +153,7 @@ int add_ip_to_ban_list(main_server_st *s, const unsigned char *ip, unsigned ip_s /* if the user is already banned, don't increase the expiration time * on further attempts, or the user will never be unbanned if he * periodically polls the server */ - if (e->score < s->config->max_ban_score) { + if (e->score < GETCONFIG(s)->max_ban_score) { e->expires = expiration; print_msg = 0; } else @@ -165,14 +165,15 @@ int add_ip_to_ban_list(main_server_st *s, const unsigned char *ip, unsigned ip_s else p_str_ip = inet_ntop(AF_INET6, ip, str_ip, sizeof(str_ip)); - if (s->config->max_ban_score > 0 && e->score >= s->config->max_ban_score) { + if (GETCONFIG(s)->max_ban_score > 0 && e->score >= GETCONFIG(s)->max_ban_score) { if (print_msg && p_str_ip) { mslog(s, NULL, LOG_INFO, "added IP '%s' (with score %d) to ban list, will be reset at: %s", str_ip, e->score, ctime(&e->expires)); } ret = -1; } else { - if (p_str_ip) + if (p_str_ip) { mslog(s, NULL, LOG_DEBUG, "added %d points (total %d) for IP '%s' to ban list", score, e->score, str_ip); + } ret = 0; } @@ -188,7 +189,7 @@ int add_str_ip_to_ban_list(main_server_st *s, const char *ip, unsigned score) ban_entry_st t; int ret = 0; - if (db == NULL || s->config->max_ban_score == 0 || ip == NULL || ip[0] == 0) + if (db == NULL || GETCONFIG(s)->max_ban_score == 0 || ip == NULL || ip[0] == 0) return 0; if (strchr(ip, ':') != 0) { @@ -219,9 +220,10 @@ int remove_ip_from_ban_list(main_server_st *s, const uint8_t *ip, unsigned size) return 0; if (size == 4 || size == 16) { - if (inet_ntop(size==16?AF_INET6:AF_INET, ip, txt_ip, sizeof(txt_ip)) != NULL) + if (inet_ntop(size==16?AF_INET6:AF_INET, ip, txt_ip, sizeof(txt_ip)) != NULL) { mslog(s, NULL, LOG_INFO, "unbanning IP '%s'", txt_ip); + } t.ip.size = size; memcpy(&t.ip.ip, ip, size); @@ -248,7 +250,7 @@ unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, sockl unsigned in_size; char txt[MAX_IP_STR]; - if (db == NULL || s->config->max_ban_score == 0) + if (db == NULL || GETCONFIG(s)->max_ban_score == 0) return 0; in_size = SA_IN_SIZE(addr_size); @@ -264,7 +266,7 @@ unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, sockl massage_ipv6_address(&t); /* add its current connection points */ - add_ip_to_ban_list(s, t.ip.ip, t.ip.size, s->config->ban_points_connect); + add_ip_to_ban_list(s, t.ip.ip, t.ip.size, GETCONFIG(s)->ban_points_connect); now = time(0); e = htable_get(db, rehash(&t, NULL), ban_entry_cmp, &t); @@ -272,7 +274,7 @@ unsigned check_if_banned(main_server_st *s, struct sockaddr_storage *addr, sockl if (now > e->expires) return 0; - if (e->score >= s->config->max_ban_score) { + if (e->score >= GETCONFIG(s)->max_ban_score) { mslog(s, NULL, LOG_INFO, "rejected connection from banned IP: %s", human_addr2((struct sockaddr*)addr, addr_size, txt, sizeof(txt), 0)); return 1; } @@ -292,7 +294,7 @@ void cleanup_banned_entries(main_server_st *s) t = htable_first(db, &iter); while (t != NULL) { - if (now >= t->expires && now > t->last_reset + s->config->ban_reset_time) { + if (now >= t->expires && now > t->last_reset + GETCONFIG(s)->ban_reset_time) { htable_delval(db, &iter); talloc_free(t); } diff --git a/src/main-ctl-unix.c b/src/main-ctl-unix.c index b041fd17..15d4e23c 100644 --- a/src/main-ctl-unix.c +++ b/src/main-ctl-unix.c @@ -104,7 +104,7 @@ static const ctl_method_st methods[] = { void ctl_handler_deinit(main_server_st * s) { - if (s->config->use_occtl == 0) + if (GETCONFIG(s)->use_occtl == 0) return; if (s->ctl_fd >= 0) { @@ -122,22 +122,22 @@ int ctl_handler_init(main_server_st * s) struct sockaddr_un sa; int sd, e; - if (s->config->use_occtl == 0 || s->perm_config->occtl_socket_file == NULL) { + if (GETCONFIG(s)->use_occtl == 0 || GETPCONFIG(s)->occtl_socket_file == NULL) { mslog(s, NULL, LOG_INFO, "not using control unix socket"); return 0; } - mslog(s, NULL, LOG_DEBUG, "initializing control unix socket: %s", s->perm_config->occtl_socket_file); + mslog(s, NULL, LOG_DEBUG, "initializing control unix socket: %s", GETPCONFIG(s)->occtl_socket_file); memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - strlcpy(sa.sun_path, s->perm_config->occtl_socket_file, sizeof(sa.sun_path)); - remove(s->perm_config->occtl_socket_file); + strlcpy(sa.sun_path, GETPCONFIG(s)->occtl_socket_file, sizeof(sa.sun_path)); + remove(GETPCONFIG(s)->occtl_socket_file); sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { e = errno; mslog(s, NULL, LOG_ERR, "could not create socket '%s': %s", - s->perm_config->occtl_socket_file, strerror(e)); + GETPCONFIG(s)->occtl_socket_file, strerror(e)); return -1; } @@ -146,22 +146,22 @@ int ctl_handler_init(main_server_st * s) if (ret == -1) { e = errno; mslog(s, NULL, LOG_ERR, "could not bind socket '%s': %s", - s->perm_config->occtl_socket_file, strerror(e)); + GETPCONFIG(s)->occtl_socket_file, strerror(e)); return -1; } - ret = chown(s->perm_config->occtl_socket_file, s->perm_config->uid, s->perm_config->gid); + ret = chown(GETPCONFIG(s)->occtl_socket_file, GETPCONFIG(s)->uid, GETPCONFIG(s)->gid); if (ret == -1) { e = errno; mslog(s, NULL, LOG_ERR, "could not chown socket '%s': %s", - s->perm_config->occtl_socket_file, strerror(e)); + GETPCONFIG(s)->occtl_socket_file, strerror(e)); } ret = listen(sd, 1024); if (ret == -1) { e = errno; mslog(s, NULL, LOG_ERR, "could not listen to socket '%s': %s", - s->perm_config->occtl_socket_file, strerror(e)); + GETPCONFIG(s)->occtl_socket_file, strerror(e)); return -1; } @@ -291,6 +291,7 @@ static int append_user_info(method_ctx *ctx, rep->id = ctmp->pid; rep->username = ctmp->username; rep->groupname = ctmp->groupname; + rep->vhost = VHOSTNAME(ctmp->vhost); ipbuf = talloc_size(ctx->pool, IPBUF_SIZE); if (ipbuf == NULL) @@ -404,8 +405,10 @@ static int append_user_info(method_ctx *ctx, rep->dpd = ctmp->config->dpd; rep->keepalive = ctmp->config->keepalive; - rep->domains = ctx->s->config->split_dns; - rep->n_domains = ctx->s->config->split_dns_size; + if (ctmp->vhost) { + rep->domains = ctmp->vhost->perm_config.config->split_dns; + rep->n_domains = ctmp->vhost->perm_config.config->split_dns_size; + } rep->dns = ctmp->config->dns; rep->n_dns = ctmp->config->n_dns; @@ -474,10 +477,11 @@ static void method_top(method_ctx *ctx, int cfd, uint8_t * msg, } static int append_ban_info(method_ctx *ctx, - BanListRep *list, - struct ban_entry_st *e) + BanListRep *list, + struct ban_entry_st *e) { BanInfoRep *rep; + main_server_st *s = ctx->s; list->info = talloc_realloc(ctx->pool, list->info, BanInfoRep *, (1 + list->n_info)); @@ -495,7 +499,7 @@ static int append_ban_info(method_ctx *ctx, rep->ip.len = e->ip.size; rep->score = e->score; - if (ctx->s->config->max_ban_score > 0 && e->score >= ctx->s->config->max_ban_score) { + if (GETCONFIG(s)->max_ban_score > 0 && e->score >= GETCONFIG(s)->max_ban_score) { rep->expires = e->expires; rep->has_expires = 1; } @@ -836,7 +840,7 @@ static void ctl_handle_commands(main_server_st * s) goto fail; } - ret = check_upeer_id("ctl", s->perm_config->debug, cfd, 0, 0, NULL, NULL); + ret = check_upeer_id("ctl", GETPCONFIG(s)->debug, cfd, 0, 0, NULL, NULL); if (ret < 0) { mslog(s, NULL, LOG_ERR, "ctl: unauthorized connection"); goto fail; @@ -861,7 +865,7 @@ static void ctl_handle_commands(main_server_st * s) void ctl_handler_set_fds(main_server_st * s, ev_io *watcher) { - if (s->config->use_occtl == 0) + if (GETCONFIG(s)->use_occtl == 0) return; ev_io_set(watcher, s->ctl_fd, EV_READ); @@ -869,7 +873,7 @@ void ctl_handler_set_fds(main_server_st * s, ev_io *watcher) void ctl_handler_run_pending(main_server_st* s, ev_io *watcher) { - if (s->config->use_occtl == 0) + if (GETCONFIG(s)->use_occtl == 0) return; ctl_handle_commands(s); diff --git a/src/main-proc.c b/src/main-proc.c index 3a4f55ae..537e3e13 100644 --- a/src/main-proc.c +++ b/src/main-proc.c @@ -65,7 +65,7 @@ struct proc_st *new_proc(main_server_st * s, pid_t pid, int cmd_fd, struct sockaddr_storage *our_addr, socklen_t our_addr_len, uint8_t *sid, size_t sid_size) { -struct proc_st *ctmp; + struct proc_st *ctmp; ctmp = talloc_zero(s, struct proc_st); if (ctmp == NULL) @@ -84,7 +84,11 @@ struct proc_st *ctmp; ctmp->our_addr_len = our_addr_len; list_add(&s->proc_list.head, &(ctmp->list)); - put_into_cgroup(s, s->config->cgroup, pid); + + /* initially we put into the "default" vhost cgroup. We + * will change cgroup once it is known which vhost this + * proc belongs to */ + put_into_cgroup(s, GETCONFIG(s)->cgroup, pid); s->stats.active_clients++; return ctmp; diff --git a/src/main-sec-mod-cmd.c b/src/main-sec-mod-cmd.c index 316b1fff..aaeabf65 100644 --- a/src/main-sec-mod-cmd.c +++ b/src/main-sec-mod-cmd.c @@ -211,16 +211,18 @@ int handle_sec_mod_commands(main_server_st * s) static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) { + vhost_cfg_st *vhost = proc->vhost; + /* if we have known_iroutes, we must append them to the routes list */ - if (s->config->known_iroutes_size > 0 || s->config->append_routes) { + if (vhost->perm_config.config->known_iroutes_size > 0 || vhost->perm_config.config->append_routes) { char **old_routes = gc->routes; unsigned old_routes_size = gc->n_routes; unsigned i, j, append; unsigned to_append = 0; - to_append = s->config->known_iroutes_size; - if (s->config->append_routes) - to_append += s->config->network.routes_size; + to_append = vhost->perm_config.config->known_iroutes_size; + if (vhost->perm_config.config->append_routes) + to_append += vhost->perm_config.config->network.routes_size; gc->n_routes = 0; gc->routes = talloc_size(proc, sizeof(char*)*(old_routes_size+to_append)); @@ -234,17 +236,17 @@ static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) if (gc->routes) { /* Append any iroutes that are known and don't match the client's */ - for (i=0;iconfig->known_iroutes_size;i++) { + for (i=0;iperm_config.config->known_iroutes_size;i++) { append = 1; for (j=0;jn_iroutes;j++) { - if (strcmp(gc->iroutes[j], s->config->known_iroutes[i]) == 0) { + if (strcmp(gc->iroutes[j], vhost->perm_config.config->known_iroutes[i]) == 0) { append = 0; break; } } if (append) { - gc->routes[gc->n_routes] = talloc_strdup(proc, s->config->known_iroutes[i]); + gc->routes[gc->n_routes] = talloc_strdup(proc, vhost->perm_config.config->known_iroutes[i]); if (gc->routes[gc->n_routes] == NULL) break; gc->n_routes++; @@ -252,11 +254,11 @@ static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) } } - if (s->config->append_routes) { + if (vhost->perm_config.config->append_routes) { /* Append all global routes */ if (gc->routes) { - for (i=0;iconfig->network.routes_size;i++) { - gc->routes[gc->n_routes] = talloc_strdup(proc, s->config->network.routes[i]); + for (i=0;iperm_config.config->network.routes_size;i++) { + gc->routes[gc->n_routes] = talloc_strdup(proc, vhost->perm_config.config->network.routes[i]); if (gc->routes[gc->n_routes] == NULL) break; gc->n_routes++; @@ -264,14 +266,14 @@ static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) } /* Append no-routes */ - if (s->config->network.no_routes_size == 0) + if (vhost->perm_config.config->network.no_routes_size == 0) return; old_routes = gc->no_routes; old_routes_size = gc->n_no_routes; gc->n_no_routes = 0; - gc->no_routes = talloc_size(proc, sizeof(char*)*(old_routes_size+s->config->network.no_routes_size)); + gc->no_routes = talloc_size(proc, sizeof(char*)*(old_routes_size+vhost->perm_config.config->network.no_routes_size)); for (i=0;ino_routes[i] = talloc_strdup(proc, old_routes[i]); @@ -280,8 +282,8 @@ static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) gc->n_no_routes++; } - for (i=0;iconfig->network.no_routes_size;i++) { - gc->no_routes[gc->n_no_routes] = talloc_strdup(proc, s->config->network.no_routes[i]); + for (i=0;iperm_config.config->network.no_routes_size;i++) { + gc->no_routes[gc->n_no_routes] = talloc_strdup(proc, vhost->perm_config.config->network.no_routes[i]); if (gc->no_routes[gc->n_no_routes] == NULL) break; gc->n_no_routes++; @@ -293,149 +295,151 @@ static void append_routes(main_server_st *s, proc_st *proc, GroupCfgSt *gc) static void apply_default_config(main_server_st *s, proc_st *proc, GroupCfgSt *gc) { + vhost_cfg_st *vhost = proc->vhost; + if (!gc->has_no_udp) { - gc->no_udp = (s->perm_config->udp_port!=0)?0:1; + gc->no_udp = (vhost->perm_config.udp_port!=0)?0:1; gc->has_no_udp = 1; } if (gc->routes == NULL) { - gc->routes = s->config->network.routes; - gc->n_routes = s->config->network.routes_size; + gc->routes = vhost->perm_config.config->network.routes; + gc->n_routes = vhost->perm_config.config->network.routes_size; } append_routes(s, proc, gc); if (gc->no_routes == NULL) { - gc->no_routes = s->config->network.no_routes; - gc->n_no_routes = s->config->network.no_routes_size; + gc->no_routes = vhost->perm_config.config->network.no_routes; + gc->n_no_routes = vhost->perm_config.config->network.no_routes_size; } if (gc->dns == NULL) { - gc->dns = s->config->network.dns; - gc->n_dns = s->config->network.dns_size; + gc->dns = vhost->perm_config.config->network.dns; + gc->n_dns = vhost->perm_config.config->network.dns_size; } if (gc->nbns == NULL) { - gc->nbns = s->config->network.nbns; - gc->n_nbns = s->config->network.nbns_size; + gc->nbns = vhost->perm_config.config->network.nbns; + gc->n_nbns = vhost->perm_config.config->network.nbns_size; } if (!gc->has_interim_update_secs) { - gc->interim_update_secs = s->config->stats_report_time; + gc->interim_update_secs = vhost->perm_config.config->stats_report_time; gc->has_interim_update_secs = 1; } if (!gc->has_session_timeout_secs) { - gc->session_timeout_secs = s->config->session_timeout; + gc->session_timeout_secs = vhost->perm_config.config->session_timeout; gc->has_session_timeout_secs = 1; } if (!gc->has_deny_roaming) { - gc->deny_roaming = s->config->deny_roaming; + gc->deny_roaming = vhost->perm_config.config->deny_roaming; gc->has_deny_roaming = 1; } if (!gc->ipv4_net) { - gc->ipv4_net = s->config->network.ipv4_network; + gc->ipv4_net = vhost->perm_config.config->network.ipv4_network; } if (!gc->ipv4_netmask) { - gc->ipv4_netmask = s->config->network.ipv4_netmask; + gc->ipv4_netmask = vhost->perm_config.config->network.ipv4_netmask; } if (!gc->ipv6_net) { - gc->ipv6_net = s->config->network.ipv6_network; + gc->ipv6_net = vhost->perm_config.config->network.ipv6_network; } if (!gc->has_ipv6_prefix) { - gc->ipv6_prefix = s->config->network.ipv6_prefix; + gc->ipv6_prefix = vhost->perm_config.config->network.ipv6_prefix; gc->has_ipv6_prefix = 1; } if (!gc->has_ipv6_subnet_prefix) { - gc->ipv6_subnet_prefix = s->config->network.ipv6_subnet_prefix; + gc->ipv6_subnet_prefix = vhost->perm_config.config->network.ipv6_subnet_prefix; gc->has_ipv6_subnet_prefix = 1; } if (!gc->cgroup) { - gc->cgroup = s->config->cgroup; + gc->cgroup = vhost->perm_config.config->cgroup; } if (!gc->xml_config_file) { - gc->xml_config_file = s->config->xml_config_file; + gc->xml_config_file = vhost->perm_config.config->xml_config_file; } if (!gc->has_rx_per_sec) { - gc->rx_per_sec = s->config->rx_per_sec; + gc->rx_per_sec = vhost->perm_config.config->rx_per_sec; gc->has_rx_per_sec = 1; } if (!gc->has_tx_per_sec) { - gc->tx_per_sec = s->config->tx_per_sec; + gc->tx_per_sec = vhost->perm_config.config->tx_per_sec; gc->has_tx_per_sec = 1; } if (!gc->has_net_priority) { - gc->net_priority = s->config->net_priority; + gc->net_priority = vhost->perm_config.config->net_priority; gc->has_net_priority = 1; } if (!gc->has_keepalive) { - gc->keepalive = s->config->keepalive; + gc->keepalive = vhost->perm_config.config->keepalive; gc->has_keepalive = 1; } if (!gc->has_dpd) { - gc->dpd = s->config->dpd; + gc->dpd = vhost->perm_config.config->dpd; gc->has_dpd = 1; } if (!gc->has_mobile_dpd) { - gc->mobile_dpd = s->config->mobile_dpd; + gc->mobile_dpd = vhost->perm_config.config->mobile_dpd; gc->has_mobile_dpd = 1; } if (!gc->has_max_same_clients) { - gc->max_same_clients = s->config->max_same_clients; + gc->max_same_clients = vhost->perm_config.config->max_same_clients; gc->has_max_same_clients = 1; } if (!gc->has_tunnel_all_dns) { - gc->tunnel_all_dns = s->config->tunnel_all_dns; + gc->tunnel_all_dns = vhost->perm_config.config->tunnel_all_dns; gc->has_tunnel_all_dns = 1; } if (!gc->has_restrict_user_to_routes) { - gc->restrict_user_to_routes = s->config->restrict_user_to_routes; + gc->restrict_user_to_routes = vhost->perm_config.config->restrict_user_to_routes; gc->has_restrict_user_to_routes = 1; } if (!gc->has_mtu) { - gc->mtu = s->config->network.mtu; + gc->mtu = vhost->perm_config.config->network.mtu; gc->has_mtu = 1; } if (!gc->has_idle_timeout) { - gc->idle_timeout = s->config->idle_timeout; + gc->idle_timeout = vhost->perm_config.config->idle_timeout; gc->has_idle_timeout = 1; } if (!gc->has_mobile_idle_timeout) { - gc->mobile_idle_timeout = s->config->mobile_idle_timeout; + gc->mobile_idle_timeout = vhost->perm_config.config->mobile_idle_timeout; gc->has_mobile_idle_timeout = 1; } - if (gc->n_fw_ports == 0 && s->config->n_fw_ports > 0) { - gc->n_fw_ports = s->config->n_fw_ports; - gc->fw_ports = s->config->fw_ports; + if (gc->n_fw_ports == 0 && vhost->perm_config.config->n_fw_ports > 0) { + gc->n_fw_ports = vhost->perm_config.config->n_fw_ports; + gc->fw_ports = vhost->perm_config.config->fw_ports; } /* since we keep pointers on s->config, increase its usage count */ - proc->config_usage_count = s->config->usage_count; + proc->config_usage_count = vhost->perm_config.config->usage_count; (*proc->config_usage_count)++; } -int session_open(main_server_st * s, struct proc_st *proc, const uint8_t *cookie, unsigned cookie_size) +int session_open(main_server_st *s, struct proc_st *proc, const uint8_t *cookie, unsigned cookie_size) { int ret, e; SecmSessionOpenMsg ireq = SECM_SESSION_OPEN_MSG__INIT; @@ -506,6 +510,7 @@ int session_open(main_server_st * s, struct proc_st *proc, const uint8_t *cookie memcpy(proc->ipv4_seed, &msg->ipv4_seed, sizeof(proc->ipv4_seed)); proc->config = msg->config; + proc->vhost = find_vhost(s->vconfig, msg->vhost); apply_default_config(s, proc, proc->config); @@ -563,9 +568,10 @@ static void update_main_stats(main_server_st * s, struct proc_st *proc) { uint64_t kb_in, kb_out; time_t now = time(0), stime; + vhost_cfg_st *vhost = proc->vhost; - if (s->perm_config->stats_reset_time != 0 && - now - s->stats.last_reset > s->perm_config->stats_reset_time) { + if (vhost->perm_config.stats_reset_time != 0 && + now - s->stats.last_reset > vhost->perm_config.stats_reset_time) { mslog(s, NULL, LOG_INFO, "resetting stats counters"); reset_stats(s, now); } @@ -662,24 +668,29 @@ int session_close(main_server_st * s, struct proc_st *proc) return 0; } +void secmod_socket_file_name(struct perm_cfg_st *perm_config, char *name, unsigned max_name_size) +{ + /* make socket name and full socket name */ + snprintf(name, max_name_size, "%s.%u", + perm_config->socket_file_prefix, (unsigned)getpid()); +} + /* Returns two file descriptors to be used for communication with sec-mod. * The sync_fd is used by main to send synchronous commands- commands which * expect a reply immediately. */ -int run_sec_mod(main_server_st * s, int *sync_fd) +int run_sec_mod(main_server_st *s, int *sync_fd) { int e, fd[2], ret; int sfd[2]; pid_t pid; const char *p; - /* make socket name */ - snprintf(s->socket_file, sizeof(s->socket_file), "%s.%u", - s->perm_config->socket_file_prefix, (unsigned)getpid()); + secmod_socket_file_name(GETPCONFIG(s), s->socket_file, sizeof(s->socket_file)); - if (s->perm_config->chroot_dir != NULL) { + if (GETPCONFIG(s)->chroot_dir != NULL) { ret = snprintf(s->full_socket_file, sizeof(s->full_socket_file), "%s/%s", - s->perm_config->chroot_dir, s->socket_file); + GETPCONFIG(s)->chroot_dir, s->socket_file); if (ret != strlen(s->full_socket_file)) { mslog(s, NULL, LOG_ERR, "too long chroot path; cannot create socket: %s", s->full_socket_file); exit(1); @@ -687,6 +698,7 @@ int run_sec_mod(main_server_st * s, int *sync_fd) } else { strlcpy(s->full_socket_file, s->socket_file, sizeof(s->full_socket_file)); } + p = s->full_socket_file; ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); @@ -716,7 +728,7 @@ int run_sec_mod(main_server_st * s, int *sync_fd) close(sfd[1]); set_cloexec_flag (fd[0], 1); set_cloexec_flag (sfd[0], 1); - sec_mod_server(s->main_pool, s->perm_config, p, fd[0], sfd[0]); + sec_mod_server(s->main_pool, s->config_pool, s->vconfig, p, fd[0], sfd[0]); exit(0); } else if (pid > 0) { /* parent */ close(fd[0]); diff --git a/src/main-user.c b/src/main-user.c index 32f234b4..f9fc3a39 100644 --- a/src/main-user.c +++ b/src/main-user.c @@ -253,11 +253,11 @@ int ret; const char* script, *next_script = NULL; if (type == SCRIPT_CONNECT) - script = s->config->connect_script; + script = GETCONFIG(s)->connect_script; else if (type == SCRIPT_HOST_UPDATE) - script = s->config->host_update_script; + script = GETCONFIG(s)->host_update_script; else - script = s->config->disconnect_script; + script = GETCONFIG(s)->disconnect_script; if (type != SCRIPT_HOST_UPDATE) { if (proc->config->restrict_user_to_routes || proc->config->n_fw_ports > 0) { @@ -336,6 +336,8 @@ const char* script, *next_script = NULL; } } + if (proc->vhost) + setenv("VHOST", VHOSTNAME(proc->vhost), 1); setenv("USERNAME", proc->username, 1); setenv("GROUPNAME", proc->groupname, 1); setenv("HOSTNAME", proc->hostname, 1); @@ -402,7 +404,7 @@ add_utmp_entry(main_server_st *s, struct proc_st* proc) struct utmpx entry; struct timespec tv; - if (s->config->use_utmp == 0) + if (GETCONFIG(s)->use_utmp == 0) return; memset(&entry, 0, sizeof(entry)); @@ -440,7 +442,7 @@ static void remove_utmp_entry(main_server_st *s, struct proc_st* proc) struct utmpx entry; struct timespec tv; - if (s->config->use_utmp == 0) + if (GETCONFIG(s)->use_utmp == 0) return; memset(&entry, 0, sizeof(entry)); diff --git a/src/main-worker-cmd.c b/src/main-worker-cmd.c index 015572dc..e5f4d0ea 100644 --- a/src/main-worker-cmd.c +++ b/src/main-worker-cmd.c @@ -157,7 +157,7 @@ static int accept_user(main_server_st * s, struct proc_st *proc, unsigned cmd) if (ret < 0) { mslog(s, proc, LOG_INFO, "user tried to connect more than %u times", - s->config->max_same_clients); + proc->config->max_same_clients); return ret; } @@ -375,7 +375,7 @@ int handle_worker_commands(main_server_st * s, struct proc_st *proc) user_hostname_update(s, proc); } - if (s->config->listen_proxy_proto) { + if (GETCONFIG(s)->listen_proxy_proto) { if (tmsg->has_remote_addr && tmsg->remote_addr.len <= sizeof(struct sockaddr_storage)) { proc_table_update_ip(s, proc, (struct sockaddr_storage*)tmsg->remote_addr.data, tmsg->remote_addr.len); diff --git a/src/main.c b/src/main.c index 6376af27..322bcda0 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2016 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * Copyright (C) 2015-2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify @@ -456,7 +456,7 @@ int y; y = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y)); - if (s->config->try_mtu) { + if (GETCONFIG(s)->try_mtu) { set_mtu_disc(fd, family, 1); } set_cloexec_flag (fd, 1); @@ -481,8 +481,8 @@ static void update_fd_limits(main_server_st *s, unsigned main) exit(1); } - if (s->config->max_clients > 0 && s->config->max_clients > def_set.rlim_cur) - max = s->config->max_clients + 32; + if (GETCONFIG(s)->max_clients > 0 && GETCONFIG(s)->max_clients > def_set.rlim_cur) + max = GETCONFIG(s)->max_clients + 32; else max = MAX(4*1024, def_set.rlim_cur); @@ -510,46 +510,46 @@ static void drop_privileges(main_server_st* s) int ret, e; struct rlimit rl; - if (s->perm_config->chroot_dir) { - ret = chdir(s->perm_config->chroot_dir); + if (GETPCONFIG(s)->chroot_dir) { + ret = chdir(GETPCONFIG(s)->chroot_dir); if (ret != 0) { e = errno; - mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", s->perm_config->chroot_dir, strerror(e)); + mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", GETPCONFIG(s)->chroot_dir, strerror(e)); exit(1); } - ret = chroot(s->perm_config->chroot_dir); + ret = chroot(GETPCONFIG(s)->chroot_dir); if (ret != 0) { e = errno; - mslog(s, NULL, LOG_ERR, "cannot chroot to %s: %s", s->perm_config->chroot_dir, strerror(e)); + mslog(s, NULL, LOG_ERR, "cannot chroot to %s: %s", GETPCONFIG(s)->chroot_dir, strerror(e)); exit(1); } } - if (s->perm_config->gid != -1 && (getgid() == 0 || getegid() == 0)) { - ret = setgid(s->perm_config->gid); + if (GETPCONFIG(s)->gid != -1 && (getgid() == 0 || getegid() == 0)) { + ret = setgid(GETPCONFIG(s)->gid); if (ret < 0) { e = errno; mslog(s, NULL, LOG_ERR, "cannot set gid to %d: %s\n", - (int) s->perm_config->gid, strerror(e)); + (int) GETPCONFIG(s)->gid, strerror(e)); exit(1); } - ret = setgroups(1, &s->perm_config->gid); + ret = setgroups(1, &GETPCONFIG(s)->gid); if (ret < 0) { e = errno; mslog(s, NULL, LOG_ERR, "cannot set groups to %d: %s\n", - (int) s->perm_config->gid, strerror(e)); + (int) GETPCONFIG(s)->gid, strerror(e)); exit(1); } } - if (s->perm_config->uid != -1 && (getuid() == 0 || geteuid() == 0)) { - ret = setuid(s->perm_config->uid); + if (GETPCONFIG(s)->uid != -1 && (getuid() == 0 || geteuid() == 0)) { + ret = setuid(GETPCONFIG(s)->uid); if (ret < 0) { e = errno; mslog(s, NULL, LOG_ERR, "cannot set uid to %d: %s\n", - (int) s->perm_config->uid, strerror(e)); + (int) GETPCONFIG(s)->uid, strerror(e)); exit(1); } @@ -577,7 +577,7 @@ static void drop_privileges(main_server_st* s) } #define MAX_WORKER_MEM (16*1024*1024) - if (s->perm_config->debug == 0) { + if (GETPCONFIG(s)->debug == 0) { rl.rlim_cur = MAX_WORKER_MEM; rl.rlim_max = MAX_WORKER_MEM; ret = setrlimit(RLIMIT_AS, &rl); @@ -694,7 +694,7 @@ unsigned get_session_id(main_server_st* s, uint8_t *buffer, size_t buffer_size, return 0; } - if (!s->config->dtls_psk) + if (!GETCONFIG(s)->dtls_psk) goto fallback; /* try to read the extension data */ @@ -780,7 +780,7 @@ int sfd = -1; ret = oc_recvfrom_at(listener->fd, s->msg_buffer, sizeof(s->msg_buffer), 0, (struct sockaddr*)&cli_addr, &cli_addr_size, (struct sockaddr*)&our_addr, &our_addr_size, - s->perm_config->udp_port); + GETPCONFIG(s)->udp_port); if (ret < 0) { mslog(s, NULL, LOG_INFO, "error receiving in UDP socket"); return -1; @@ -821,7 +821,7 @@ int sfd = -1; match_ip_only = 1; /* don't bother IP matching when the listen-clear-file is in use */ - if (s->perm_config->unix_conn_file) + if (GETPCONFIG(s)->unix_conn_file) goto fail; } else { if (!get_session_id(s, s->msg_buffer, buffer_size, &session_id, &session_id_size)) { @@ -1029,18 +1029,11 @@ static void reload_sig_watcher_cb(struct ev_loop *loop, ev_signal *w, int revent mslog(s, NULL, LOG_INFO, "reloading configuration"); kill(s->sec_mod_pid, SIGHUP); - reload_cfg_file(s->main_pool, s->perm_config, 1); - s->config = s->perm_config->config; - - /* These must be called after sec-mod has been sent SIGHUP. + /* Reload on main needs to happen later than sec-mod. * That's because of a test that the certificate matches the - * used key in certain gnutls versions */ -#if GNUTLS_VERSION_NUMBER < 0x030407 - ms_sleep(1000); -#endif - tls_load_files(s, s->creds); - tls_load_prio(s, s->creds); - tls_reload_crl(s, s->creds, 1); + * used key. */ + ms_sleep(1500); + reload_cfg_file(s->config_pool, s->vconfig, 1); } static void cmd_watcher_cb (EV_P_ ev_io *w, int revents) @@ -1083,7 +1076,7 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) set_block(fd); #endif - if (s->config->max_clients > 0 && s->stats.active_clients >= s->config->max_clients) { + if (GETCONFIG(s)->max_clients > 0 && s->stats.active_clients >= GETCONFIG(s)->max_clients) { close(fd); mslog(s, NULL, LOG_INFO, "reached maximum client limit (active: %u)", s->stats.active_clients); return; @@ -1095,7 +1088,7 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) return; } - if (ws->conn_type != SOCK_TYPE_UNIX && !s->config->listen_proxy_proto) { + if (ws->conn_type != SOCK_TYPE_UNIX && !GETCONFIG(s)->listen_proxy_proto) { memset(&ws->our_addr, 0, sizeof(ws->our_addr)); ws->our_addr_len = sizeof(ws->our_addr); if (getsockname(fd, (struct sockaddr*)&ws->our_addr, &ws->our_addr_len) < 0) @@ -1135,14 +1128,14 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents) ws->secmod_addr_len = s->secmod_addr_len; ws->main_pool = s->main_pool; - ws->config = s->config; - ws->perm_config = s->perm_config; + + ws->vconfig = s->vconfig; + ws->cmd_fd = cmd_fd[1]; ws->tun_fd = -1; ws->dtls_tptr.fd = -1; ws->conn_fd = fd; ws->conn_type = stype; - ws->creds = s->creds; /* Drop privileges after this point */ drop_privileges(s); @@ -1188,8 +1181,8 @@ fork_failed: forward_udp_to_owner(s, ltmp); } - if (s->config->rate_limit_ms > 0) - ms_sleep(s->config->rate_limit_ms); + if (GETCONFIG(s)->rate_limit_ms > 0) + ms_sleep(GETCONFIG(s)->rate_limit_ms); } static void sec_mod_watcher_cb (EV_P_ ev_io *w, int revents) @@ -1215,12 +1208,16 @@ static void ctl_watcher_cb (EV_P_ ev_io *w, int revents) static void maintainance_watcher_cb(EV_P_ ev_timer *w, int revents) { main_server_st *s = ev_userdata(loop); + vhost_cfg_st *vhost = NULL; /* Check if we need to expire any data */ mslog(s, NULL, LOG_DEBUG, "performing maintenance (banned IPs: %d)", main_ban_db_elems(s)); - tls_reload_crl(s, s->creds, 0); cleanup_banned_entries(s); - clear_old_configs(s->perm_config); + clear_old_configs(s->vconfig); + + list_for_each_rev(s->vconfig, vhost, list) { + tls_reload_crl(s->config_pool, vhost, 0); + } } static void syserr_cb (const char *msg) @@ -1238,19 +1235,16 @@ int main(int argc, char** argv) int ret, flags; char *p; void *worker_pool; - void *main_pool; + void *main_pool, *config_pool; main_server_st *s; - /* tls credentials */ - struct tls_st creds; #ifdef DEBUG_LEAKS talloc_enable_leak_report_full(); #endif + saved_argc = argc; saved_argv = argv; - memset(&creds, 0, sizeof(creds)); - /* main pool */ main_pool = talloc_init("main"); if (main_pool == NULL) { @@ -1258,13 +1252,19 @@ int main(int argc, char** argv) exit(1); } + config_pool = talloc_init("config"); + if (config_pool == NULL) { + fprintf(stderr, "talloc init error\n"); + exit(1); + } + s = talloc_zero(main_pool, main_server_st); if (s == NULL) { fprintf(stderr, "memory error\n"); exit(1); } s->main_pool = main_pool; - s->creds = &creds; + s->config_pool = config_pool; s->stats.start_time = s->stats.last_reset = time(0); s->top_fd = -1; s->ctl_fd = -1; @@ -1280,17 +1280,22 @@ int main(int argc, char** argv) ocsignal(SIGPIPE, SIG_IGN); /* Initialize GnuTLS */ - tls_global_init(&creds); + tls_global_init(); /* load configuration */ - ret = cmd_parser(main_pool, argc, argv, &s->perm_config); + s->vconfig = talloc_zero(config_pool, struct list_head); + if (s->vconfig == NULL) { + fprintf(stderr, "memory error\n"); + exit(1); + } + list_head_init(s->vconfig); + + ret = cmd_parser(config_pool, argc, argv, s->vconfig); if (ret < 0) { fprintf(stderr, "Error in arguments\n"); exit(1); } - s->config = s->perm_config->config; - setproctitle(PACKAGE_NAME"-main"); if (getuid() != 0) { @@ -1299,7 +1304,7 @@ int main(int argc, char** argv) } /* Listen to network ports */ - ret = listen_ports(s, s->perm_config, &s->listen_list); + ret = listen_ports(s, GETPCONFIG(s), &s->listen_list); if (ret < 0) { fprintf(stderr, "Cannot listen to specified ports\n"); exit(1); @@ -1307,7 +1312,7 @@ int main(int argc, char** argv) flags = LOG_PID|LOG_NDELAY; #ifdef LOG_PERROR - if (s->perm_config->debug != 0) + if (GETPCONFIG(s)->debug != 0) flags |= LOG_PERROR; #endif openlog("ocserv", flags, LOG_DAEMON); @@ -1317,7 +1322,7 @@ int main(int argc, char** argv) deny_severity = LOG_DAEMON|LOG_WARNING; #endif - if (s->perm_config->foreground == 0) { + if (GETPCONFIG(s)->foreground == 0) { if (daemon(0, 0) == -1) { e = errno; fprintf(stderr, "daemon failed: %s\n", strerror(e)); @@ -1348,22 +1353,18 @@ int main(int argc, char** argv) /* chdir to our chroot directory, to allow opening the sec-mod * socket if necessary. */ - if (s->perm_config->chroot_dir) { - if (chdir(s->perm_config->chroot_dir) != 0) { + if (GETPCONFIG(s)->chroot_dir) { + if (chdir(GETPCONFIG(s)->chroot_dir) != 0) { e = errno; - mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", s->perm_config->chroot_dir, strerror(e)); + mslog(s, NULL, LOG_ERR, "cannot chdir to %s: %s", GETPCONFIG(s)->chroot_dir, strerror(e)); exit(1); } } ms_sleep(100); /* give some time for sec-mod to initialize */ - /* Initialize certificates */ - tls_load_files(s, &creds); - tls_load_prio(s, s->creds); - s->secmod_addr.sun_family = AF_UNIX; p = s->socket_file; - if (s->perm_config->chroot_dir) /* if we are on chroot make the socket file path relative */ + if (GETPCONFIG(s)->chroot_dir) /* if we are on chroot make the socket file path relative */ while (*p == '/') p++; strlcpy(s->secmod_addr.sun_path, p, sizeof(s->secmod_addr.sun_path)); s->secmod_addr_len = SUN_LEN(&s->secmod_addr); @@ -1383,12 +1384,10 @@ int main(int argc, char** argv) #ifdef HAVE_GSSAPI /* Initialize kkdcp structures */ - if (s->config->kkdcp) { - ret = asn1_array2tree(kkdcp_asn1_tab, &_kkdcp_pkix1_asn, NULL); - if (ret != ASN1_SUCCESS) { - mslog(s, NULL, LOG_ERR, "KKDCP ASN.1 initialization error"); - exit(1); - } + ret = asn1_array2tree(kkdcp_asn1_tab, &_kkdcp_pkix1_asn, NULL); + if (ret != ASN1_SUCCESS) { + mslog(s, NULL, LOG_ERR, "KKDCP ASN.1 initialization error"); + exit(1); } #endif @@ -1440,13 +1439,12 @@ int main(int argc, char** argv) * for memory leaks. */ remove(s->full_socket_file); - remove(s->perm_config->occtl_socket_file); + remove(GETPCONFIG(s)->occtl_socket_file); remove_pid_file(); clear_lists(s); - tls_global_deinit(s->creds); - clear_cfg(s->perm_config); - talloc_free(s->perm_config); + clear_vhosts(s->vconfig); + talloc_free(s->config_pool); talloc_free(s->main_pool); closelog(); diff --git a/src/main.h b/src/main.h index 97eb8692..00754306 100644 --- a/src/main.h +++ b/src/main.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * Copyright (C) 2015 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos @@ -34,6 +34,8 @@ #include #include +#include "vhost.h" + #if defined(__FreeBSD__) || defined(__OpenBSD__) # include # define SOL_IP IPPROTO_IP @@ -49,7 +51,7 @@ extern ev_timer maintainance_watcher; #define MAIN_MAINTAINANCE_TIME (900) -int cmd_parser (void *pool, int argc, char **argv, struct perm_cfg_st** config); +int cmd_parser (void *pool, int argc, char **argv, struct list_head *head); struct listener_st { ev_io io; @@ -153,6 +155,9 @@ typedef struct proc_st { /* The following we rely on talloc for deallocation */ GroupCfgSt *config; /* custom user/group config */ int *config_usage_count; /* points to s->config->usage_count */ + /* pointer to perm_cfg - set after we know the virtual host. As + * vhosts never get deleted, this pointer is always valid */ + vhost_cfg_st *vhost; } proc_st; struct ip_lease_db_st { @@ -206,15 +211,13 @@ struct main_stats_st { }; typedef struct main_server_st { - struct cfg_st *config; /* pointer inside perm_config */ - struct perm_cfg_st *perm_config; - + /* virtual hosts are only being added to that list, never removed */ + struct list_head *vconfig; + struct ip_lease_db_st ip_leases; struct htable *ban_db; - tls_st *creds; - struct listen_list_st listen_list; struct proc_list_st proc_list; struct script_list_st script_list; @@ -241,6 +244,7 @@ typedef struct main_server_st { int sec_mod_fd; /* messages are sent and received async */ int sec_mod_fd_sync; /* messages are send in a sync order (ping-pong). Only main sends. */ void *main_pool; /* talloc main pool */ + void *config_pool; /* talloc config pool */ /* used as temporary buffer (currently by forward_udp_to_owner) */ uint8_t msg_buffer[MAX_MSG_SIZE]; @@ -342,6 +346,9 @@ int send_socket_msg_to_worker(main_server_st* s, struct proc_st* proc, uint8_t c return send_socket_msg(proc, proc->fd, cmd, socketfd, msg, get_size, pack); } +void secmod_socket_file_name(struct perm_cfg_st *perm_config, char *name, unsigned max_name_size); +void clear_vhosts(struct list_head *head); + void request_reload(int signo); void request_stop(int signo); diff --git a/src/occtl/geoip.c b/src/occtl/geoip.c index b9512b14..3c62aca8 100644 --- a/src/occtl/geoip.c +++ b/src/occtl/geoip.c @@ -211,6 +211,10 @@ char *geo_lookup(const char *ip, char *buf, unsigned buf_size) return buf; fail: + free(country); + free(city); + free(coord); + return "unknown"; } diff --git a/src/occtl/unix.c b/src/occtl/unix.c index a1ab0a5a..6d8da6bb 100644 --- a/src/occtl/unix.c +++ b/src/occtl/unix.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014-2017 Red Hat + * Copyright (C) 2014-2018 Nikos Mavrogiannopoulos * * Author: Nikos Mavrogiannopoulos * @@ -586,7 +587,7 @@ static const char *get_ip(const char *ip1, const char *ip2) void common_user_list(struct unix_ctx *ctx, UserListRep *rep, FILE *out, cmd_params_st *params) { unsigned i; - const char *vpn_ip, *groupname, *username; + const char *vpn_ip, *username; const char *dtls_ciphersuite; char tmpbuf[MAX_TMPSTR_SIZE]; time_t t; @@ -605,7 +606,7 @@ void common_user_list(struct unix_ctx *ctx, UserListRep *rep, FILE *out, cmd_par /* add header */ if (i == 0) { fprintf(out, "%8s %8s %8s %14s %14s %6s %7s %14s %9s\n", - "id", "user", "group", "ip", "vpn-ip", "device", + "id", "user", "vhost", "ip", "vpn-ip", "device", "since", "dtls-cipher", "status"); } @@ -613,14 +614,10 @@ void common_user_list(struct unix_ctx *ctx, UserListRep *rep, FILE *out, cmd_par tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); - groupname = rep->user[i]->groupname; - if (groupname == NULL || groupname[0] == 0) - groupname = NO_GROUP; - print_time_ival7(tmpbuf, time(0), t); fprintf(out, "%8d %8s %8s %14s %14s %6s ", - (int)rep->user[i]->id, username, groupname, rep->user[i]->ip, vpn_ip, rep->user[i]->tun); + (int)rep->user[i]->id, username, rep->user[i]->vhost, rep->user[i]->ip, vpn_ip, rep->user[i]->tun); dtls_ciphersuite = fix_ciphersuite(rep->user[i]->dtls_ciphersuite); @@ -692,7 +689,7 @@ void session_list(struct unix_ctx *ctx, SecmListCookiesReplyMsg *rep, FILE *out, unsigned all) { unsigned i; - const char *groupname, *username; + const char *username; char tmpbuf[MAX_TMPSTR_SIZE]; time_t t; struct tm *tm; @@ -714,7 +711,7 @@ void session_list(struct unix_ctx *ctx, SecmListCookiesReplyMsg *rep, FILE *out, /* add header */ if (i == 0) { fprintf(out, "%6s %8s %8s %14s %24s %8s %8s\n", - "session", "user", "group", "ip", "user agent", "created", "status"); + "session", "user", "vhost", "ip", "user agent", "created", "status"); } t = rep->cookies[i]->created; @@ -724,15 +721,11 @@ void session_list(struct unix_ctx *ctx, SecmListCookiesReplyMsg *rep, FILE *out, print_time_ival7(tmpbuf, time(0), t); } - groupname = rep->cookies[i]->groupname; - if (groupname == NULL || groupname[0] == 0) - groupname = NO_GROUP; - sid = shorten(rep->cookies[i]->safe_id.data, rep->cookies[i]->safe_id.len, 1); session_entries_add(ctx, sid); fprintf(out, "%.6s %8s %8s %14s %.24s %8s %8s\n", - sid, username, groupname, rep->cookies[i]->remote_ip, + sid, username, rep->cookies[i]->vhost, rep->cookies[i]->remote_ip, rep->cookies[i]->user_agent, tmpbuf, ps_status_to_str(rep->cookies[i]->status, 1)); } } @@ -876,13 +869,13 @@ int handle_list_iroutes_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st /* add header */ if (i == 0) { - fprintf(out, "%6s %8s %6s %16s %28s\n", - "id", "user", "device", "vpn-ip", "iroute"); + fprintf(out, "%6s %8s %8s %6s %16s %28s\n", + "id", "user", "vhost", "device", "vpn-ip", "iroute"); } for (j=0;juser[i]->n_iroutes;j++) - fprintf(out, "%6d %8s %6s %16s %28s\n", - (int)rep->user[i]->id, username, rep->user[i]->tun, vpn_ip, rep->user[i]->iroutes[j]); + fprintf(out, "%6d %8s %8s %6s %16s %28s\n", + (int)rep->user[i]->id, username, rep->user[i]->vhost, rep->user[i]->tun, vpn_ip, rep->user[i]->iroutes[j]); } } else { @@ -898,6 +891,7 @@ int handle_list_iroutes_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st print_single_value_int(out, params, "ID", rep->user[i]->id, 1); print_single_value(out, params, "Username", username, 1); + print_single_value(out, params, "vhost", rep->user[i]->vhost, 1); print_single_value(out, params, "Device", rep->user[i]->tun, 1); print_single_value(out, params, "IP", vpn_ip, 1); print_list_entries(out, params, "iRoutes", rep->user[i]->iroutes, rep->user[i]->n_iroutes, 1); @@ -1098,6 +1092,7 @@ int common_info_cmd(UserListRep * args, FILE *out, cmd_params_st *params) print_pair_value(out, params, "Username", username, "Groupname", groupname, 1); print_single_value(out, params, "State", ps_status_to_str(args->user[i]->status, 0), 1); + print_single_value(out, params, "vhost", args->user[i]->vhost, 1); if (args->user[i]->has_mtu != 0) print_pair_value(out, params, "Device", args->user[i]->tun, "MTU", int2str(tmpbuf, args->user[i]->mtu), 1); else @@ -1292,7 +1287,7 @@ int session_info_cmd(void *ctx, SecmListCookiesReplyMsg * args, FILE *out, groupname = NO_GROUP; print_pair_value(out, params, "Username", username, "Groupname", groupname, 1); - print_single_value(out, params, "User-Agent", args->cookies[i]->user_agent, 1); + print_pair_value(out, params, "vhost", args->cookies[i]->vhost, "User-Agent", args->cookies[i]->user_agent, 1); print_pair_value(out, params, "Remote IP", args->cookies[i]->remote_ip, "Location", geo_lookup(args->cookies[i]->remote_ip, tmpbuf, sizeof(tmpbuf)), 1); if (HAVE_JSON(params)) { @@ -1520,7 +1515,8 @@ int handle_events_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *para common_info_cmd(rep2->user, stdout, params); } else { if (rep2->connected) { - printf("connected user '%s' (%u) from %s with IP %s\n", + printf("%s: connected user '%s' (%u) from %s with IP %s\n", + rep2->user->user[0]->vhost, rep2->user->user[0]->username, rep2->user->user[0]->id, rep2->user->user[0]->ip, @@ -1530,7 +1526,8 @@ int handle_events_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *para entries_add(ctx, rep2->user->user[0]->username, strlen(rep2->user->user[0]->username), rep2->user->user[0]->id); } else { print_time_ival7(tmpbuf, time(0), rep2->user->user[0]->conn_time); - printf("disconnect user '%s' (%u) from %s with IP %s (reason: %s, time: %s)\n", + printf("%s: disconnect user '%s' (%u) from %s with IP %s (reason: %s, time: %s)\n", + rep2->user->user[0]->vhost, rep2->user->user[0]->username, rep2->user->user[0]->id, rep2->user->user[0]->ip, diff --git a/src/route-add.c b/src/route-add.c index 4ebd113a..446b2550 100644 --- a/src/route-add.c +++ b/src/route-add.c @@ -146,13 +146,13 @@ char *cmd = NULL; static int route_add(struct main_server_st* s, proc_st *proc, const char* route, const char* dev) { - return route_adddel(s, proc, s->config->route_add_cmd, route, dev); + return route_adddel(s, proc, GETCONFIG(s)->route_add_cmd, route, dev); } static int route_del(struct main_server_st* s, proc_st *proc, const char* route, const char* dev) { - return route_adddel(s, proc, s->config->route_del_cmd, route, dev); + return route_adddel(s, proc, GETCONFIG(s)->route_del_cmd, route, dev); } /* Executes the commands required to apply all the configured routes diff --git a/src/sec-mod-acct.h b/src/sec-mod-acct.h index 4697fb03..12ba87ab 100644 --- a/src/sec-mod-acct.h +++ b/src/sec-mod-acct.h @@ -27,13 +27,13 @@ typedef struct acct_mod_st { unsigned int type; /* ACCT_TYPE_ */ unsigned int auth_types; /* or of the AUTH_TYPEs which are compatible with this */ - void (*global_init)(void *pool, void* additional); - void (*global_deinit)(void); + void (*vhost_init)(void **vctx, void *pool, void* additional); + void (*vhost_deinit)(void *vctx); /* The context provided below is of the authentication method */ - int (*open_session)(unsigned auth_method, const common_acct_info_st *ai, const void *sid, unsigned sid_size); /* optional, may be null */ - void (*session_stats)(unsigned auth_method, const common_acct_info_st *ai, struct stats_st *stats); /* optional, may be null */ - void (*close_session)(unsigned auth_method, const common_acct_info_st *ai, struct stats_st *stats, unsigned discon_reason/*REASON_*/); /* optional may be null */ + int (*open_session)(void *vctx, unsigned auth_method, const common_acct_info_st *ai, const void *sid, unsigned sid_size); /* optional, may be null */ + void (*session_stats)(void *vctx, unsigned auth_method, const common_acct_info_st *ai, struct stats_st *stats); /* optional, may be null */ + void (*close_session)(void *vctx, unsigned auth_method, const common_acct_info_st *ai, struct stats_st *stats, unsigned discon_reason/*REASON_*/); /* optional may be null */ } acct_mod_st; /* The accounting messages exchanged with the worker thread are shown in ipc.proto. diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index faed836a..f61c2939 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * Copyright (C) 2014-2016 Red Hat * * This program is free software; you can redistribute it and/or modify @@ -52,23 +52,30 @@ #include #include #include +#include #ifdef HAVE_GSSAPI # include # include #endif -void sec_auth_init(sec_mod_st * sec, struct perm_cfg_st *config) +/* initializes vhost acct and auth modules if not already initialized + */ +void sec_auth_init(struct vhost_cfg_st *vhost) { unsigned i; + void *pool = vhost; - for (i=0;iauth_methods;i++) { - if (config->auth[i].enabled && config->auth[i].amod && config->auth[i].amod->global_init) - config->auth[i].amod->global_init(sec, config->auth[i].additional); + for (i=0;iperm_config.auth_methods;i++) { + if (vhost->perm_config.auth[i].enabled && vhost->perm_config.auth[i].amod && + vhost->perm_config.auth[i].amod->vhost_init && vhost->perm_config.auth[i].auth_ctx == NULL) { + vhost->perm_config.auth[i].amod->vhost_init(&vhost->perm_config.auth[i].auth_ctx, pool, vhost->perm_config.auth[i].additional); + } } - if (config->acct.amod && config->acct.amod->global_init) - config->acct.amod->global_init(sec, config->acct.additional); + if (vhost->perm_config.acct.amod && vhost->perm_config.acct.amod->vhost_init && + vhost->perm_config.acct.acct_ctx == NULL) + vhost->perm_config.acct.amod->vhost_init(&vhost->perm_config.acct.acct_ctx, pool, vhost->perm_config.acct.additional); } /* returns a negative number if we have reached the score for this client. @@ -81,7 +88,7 @@ void sec_mod_add_score_to_ip(sec_mod_st *sec, client_entry_st *e, const char *ip BanIpMsg msg = BAN_IP_MSG__INIT; /* no reporting if banning is disabled */ - if (sec->config->max_ban_score == 0) + if (e->vhost->perm_config.config->max_ban_score == 0) return; msg.ip = (char*)ip; @@ -223,24 +230,24 @@ static int check_cert_user_group_status(sec_mod_st * sec, client_entry_st * e) return -1; } - if (e->acct_info.username[0] == 0 && sec->config->cert_user_oid != NULL) { + if (e->acct_info.username[0] == 0 && e->vhost->perm_config.config->cert_user_oid != NULL) { if (e->cert_user_name[0] == 0) { seclog(sec, LOG_INFO, "no username in the certificate; rejecting"); return -1; } strlcpy(e->acct_info.username, e->cert_user_name, sizeof(e->acct_info.username)); - if (e->cert_group_names_size > 0 && sec->config->cert_group_oid != NULL && e->acct_info.groupname[0] == 0) + if (e->cert_group_names_size > 0 && e->vhost->perm_config.config->cert_group_oid != NULL && e->acct_info.groupname[0] == 0) strlcpy(e->acct_info.groupname, e->cert_group_names[0], sizeof(e->acct_info.groupname)); } else { - if (sec->config->cert_user_oid != NULL && e->cert_user_name[0] && strcmp(e->acct_info.username, e->cert_user_name) != 0) { + if (e->vhost->perm_config.config->cert_user_oid != NULL && e->cert_user_name[0] && strcmp(e->acct_info.username, e->cert_user_name) != 0) { seclog(sec, LOG_INFO, "user '%s' "SESSION_STR" presented a certificate which is for user '%s'; rejecting", e->acct_info.username, e->acct_info.safe_id, e->cert_user_name); return -1; } - if (sec->config->cert_group_oid != NULL) { + if (e->vhost->perm_config.config->cert_group_oid != NULL) { found = 0; for (i=0;icert_group_names_size;i++) { if (strcmp(e->acct_info.groupname, e->cert_group_names[i]) == 0) { @@ -282,7 +289,7 @@ int check_group(sec_mod_st * sec, client_entry_st * e) /* set group name using the certificate info */ if (e->auth_type & AUTH_TYPE_CERTIFICATE) { - if (e->acct_info.groupname[0] == 0 && req_group != NULL && sec->config->cert_group_oid != NULL) { + if (e->acct_info.groupname[0] == 0 && req_group != NULL && e->vhost->perm_config.config->cert_group_oid != NULL) { unsigned i, found = 0; for (i=0;icert_group_names_size;i++) { @@ -337,7 +344,7 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu if (result == ERR_AUTH_CONTINUE) { /* if the module allows multiple retries for the password */ if (e->status != PS_AUTH_INIT && e->module && e->module->allows_retries) { - sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, sec->config->ban_points_wrong_password); + sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, e->vhost->perm_config.config->ban_points_wrong_password); } ret = send_sec_auth_reply_msg(cfd, sec, e); @@ -377,7 +384,7 @@ int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int resu } else { e->status = PS_AUTH_FAILED; - sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, sec->config->ban_points_wrong_password); + sec_mod_add_score_to_ip(sec, e, e->acct_info.remote_ip, e->vhost->perm_config.config->ban_points_wrong_password); ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__FAILED); if (ret < 0) { @@ -467,8 +474,8 @@ int handle_secm_session_open_cmd(sec_mod_st *sec, int fd, const SecmSessionOpenM if (req->ipv6) strlcpy(e->acct_info.ipv6, req->ipv6, sizeof(e->acct_info.ipv6)); - if (sec->perm_config->acct.amod != NULL && sec->perm_config->acct.amod->open_session != NULL && e->session_is_open == 0) { - ret = sec->perm_config->acct.amod->open_session(e->auth_type, &e->acct_info, req->sid.data, req->sid.len); + if (e->vhost->perm_config.acct.amod != NULL && e->vhost->perm_config.acct.amod->open_session != NULL && e->session_is_open == 0) { + ret = e->vhost->perm_config.acct.amod->open_session(e->vhost_acct_ctx, e->auth_type, &e->acct_info, req->sid.data, req->sid.len); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_INFO, "denied session for user '%s' "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); @@ -481,9 +488,10 @@ int handle_secm_session_open_cmd(sec_mod_st *sec, int fd, const SecmSessionOpenM rep.groupname = e->acct_info.groupname; rep.ip = e->acct_info.remote_ip; rep.tls_auth_ok = e->tls_auth_ok; + rep.vhost = e->vhost->name; /* Fixme: possibly we should allow for completely random seeds */ - if (sec->config->predictable_ips != 0) { + if (e->vhost->perm_config.config->predictable_ips != 0) { rep.ipv4_seed = hash_any(e->acct_info.username, strlen(e->acct_info.username), 0); } else { ret = gnutls_rnd(GNUTLS_RND_NONCE, &rep.ipv4_seed, sizeof(rep.ipv4_seed)); @@ -501,8 +509,8 @@ int handle_secm_session_open_cmd(sec_mod_st *sec, int fd, const SecmSessionOpenM return ERR_BAD_COMMAND; /* we desync */ } - if (sec->config_module && sec->config_module->get_sup_config) { - ret = sec->config_module->get_sup_config(sec->config, e, &rep, lpool); + if (e->vhost->config_module && e->vhost->config_module->get_sup_config) { + ret = e->vhost->config_module->get_sup_config(e->vhost->perm_config.config, e, &rep, lpool); if (ret < 0) { seclog(sec, LOG_ERR, "error reading additional configuration for '%s' "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); talloc_free(lpool); @@ -519,9 +527,9 @@ int handle_secm_session_open_cmd(sec_mod_st *sec, int fd, const SecmSessionOpenM } talloc_free(lpool); - seclog(sec, LOG_INFO, "initiating session for user '%s' "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); + seclog(sec, LOG_INFO, "%sinitiating session for user '%s' "SESSION_STR, PREFIX_VHOST(e->vhost), e->acct_info.username, e->acct_info.safe_id); /* refresh cookie validity */ - e->exptime = time(0) + sec->config->cookie_timeout + AUTH_SLACK_TIME; + e->exptime = time(0) + e->vhost->perm_config.config->cookie_timeout + AUTH_SLACK_TIME; e->in_use++; return 0; @@ -647,7 +655,7 @@ int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req, pid_t p /* update PID */ e->acct_info.id = pid; - if (sec->perm_config->acct.amod == NULL || sec->perm_config->acct.amod->session_stats == NULL) + if (e->vhost->perm_config.acct.amod == NULL || e->vhost->perm_config.acct.amod->session_stats == NULL) return 0; stats_add_to(&totals, &e->stats, &e->saved_stats); @@ -658,7 +666,7 @@ int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req, pid_t p if (req->ipv6) strlcpy(e->acct_info.ipv6, req->ipv6, sizeof(e->acct_info.ipv6)); - sec->perm_config->acct.amod->session_stats(e->auth_type, &e->acct_info, &totals); + e->vhost->perm_config.acct.amod->session_stats(e->vhost_acct_ctx, e->auth_type, &e->acct_info, &totals); return 0; } @@ -721,7 +729,7 @@ int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req) } static -int set_module(sec_mod_st * sec, client_entry_st *e, unsigned auth_type) +int set_module(sec_mod_st * sec, vhost_cfg_st *vhost, client_entry_st *e, unsigned auth_type) { unsigned i; @@ -730,12 +738,14 @@ int set_module(sec_mod_st * sec, client_entry_st *e, unsigned auth_type) /* Find the first configured authentication method which contains * the method asked by the worker, and use that. */ - for (i=0;iperm_config->auth_methods;i++) { - if (sec->perm_config->auth[i].enabled && (sec->perm_config->auth[i].type & auth_type) == auth_type) { - e->module = sec->perm_config->auth[i].amod; - e->auth_type = sec->perm_config->auth[i].type; + for (i=0;iperm_config.auth_methods;i++) { + if (vhost->perm_config.auth[i].enabled && (vhost->perm_config.auth[i].type & auth_type) == auth_type) { + e->module = vhost->perm_config.auth[i].amod; + e->auth_type = vhost->perm_config.auth[i].type; + e->vhost_auth_ctx = vhost->perm_config.auth[i].auth_ctx; + e->vhost_acct_ctx = vhost->perm_config.acct.acct_ctx; - seclog(sec, LOG_INFO, "using '%s' authentication to authenticate user "SESSION_STR, sec->perm_config->auth[i].name, e->acct_info.safe_id); + seclog(sec, LOG_INFO, "%susing '%s' authentication to authenticate user "SESSION_STR, PREFIX_VHOST(vhost), vhost->perm_config.auth[i].name, e->acct_info.safe_id); return 0; } } @@ -749,14 +759,17 @@ int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req, pi client_entry_st *e; unsigned i; unsigned need_continue = 0; + vhost_cfg_st *vhost; - e = new_client_entry(sec, req->ip, pid); + vhost = find_vhost(sec->vconfig, req->vhost); + + e = new_client_entry(sec, vhost, req->ip, pid); if (e == NULL) { seclog(sec, LOG_ERR, "cannot initialize memory"); return -1; } - ret = set_module(sec, e, req->auth_type); + ret = set_module(sec, vhost, e, req->auth_type); if (ret < 0) { seclog(sec, LOG_ERR, "no module found for auth type %u", (unsigned)req->auth_type); goto cleanup; @@ -772,7 +785,7 @@ int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req, pi st.id = pid; ret = - e->module->auth_init(&e->auth_ctx, e, &st); + e->module->auth_init(&e->auth_ctx, e, e->vhost_auth_ctx, &st); if (ret == ERR_AUTH_CONTINUE) { need_continue = 1; } else if (ret < 0) { @@ -827,9 +840,13 @@ int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req, pi void sec_auth_user_deinit(sec_mod_st *sec, client_entry_st *e) { + vhost_cfg_st *vhost; + + vhost = e->vhost; + seclog(sec, LOG_DEBUG, "permamently closing session of user '%s' "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); - if (sec->perm_config->acct.amod != NULL && sec->perm_config->acct.amod->close_session != NULL && e->session_is_open != 0) { - sec->perm_config->acct.amod->close_session(e->auth_type, &e->acct_info, &e->saved_stats, e->discon_reason); + if (vhost->perm_config.acct.amod != NULL && vhost->perm_config.acct.amod->close_session != NULL && e->session_is_open != 0) { + vhost->perm_config.acct.amod->close_session(e->vhost_acct_ctx, e->auth_type, &e->acct_info, &e->saved_stats, e->discon_reason); } if (e->auth_ctx != NULL) { diff --git a/src/sec-mod-auth.h b/src/sec-mod-auth.h index 7dcd31ed..7d8681fd 100644 --- a/src/sec-mod-auth.h +++ b/src/sec-mod-auth.h @@ -34,9 +34,9 @@ typedef struct passwd_msg_st { typedef struct auth_mod_st { unsigned int type; unsigned int allows_retries; /* whether the module allows retries of the same password */ - void (*global_init)(void *pool, void* additional); - void (*global_deinit)(void); - int (*auth_init)(void** ctx, void *pool, const common_auth_init_st *); + void (*vhost_init)(void **vctx, void *pool, void* additional); + void (*vhost_deinit)(void *vctx); + int (*auth_init)(void **ctx, void *pool, void *vctx, const common_auth_init_st *); int (*auth_msg)(void* ctx, void *pool, passwd_msg_st *); int (*auth_pass)(void* ctx, const char* pass, unsigned pass_len); int (*auth_group)(void* ctx, const char *suggested, char *groupname, int groupname_size); diff --git a/src/sec-mod-cookies.c b/src/sec-mod-cookies.c index 54ef59a2..a93bbff2 100644 --- a/src/sec-mod-cookies.c +++ b/src/sec-mod-cookies.c @@ -100,6 +100,7 @@ void handle_secm_list_cookies_reply(void *pool, int fd, sec_mod_st *sec) cookies[msg.n_cookies].remote_ip = t->acct_info.remote_ip; cookies[msg.n_cookies].status = t->status; cookies[msg.n_cookies].in_use = t->in_use; + cookies[msg.n_cookies].vhost = VHOSTNAME(t->vhost); msg.cookies[msg.n_cookies] = &cookies[msg.n_cookies]; msg.n_cookies++; diff --git a/src/sec-mod-db.c b/src/sec-mod-db.c index 26f9ef39..30e40620 100644 --- a/src/sec-mod-db.c +++ b/src/sec-mod-db.c @@ -85,7 +85,7 @@ struct htable *db = sec->client_db; return 0; } -client_entry_st *new_client_entry(sec_mod_st *sec, const char *ip, unsigned pid) +client_entry_st *new_client_entry(sec_mod_st *sec, struct vhost_cfg_st *vhost, const char *ip, unsigned pid) { struct htable *db = sec->client_db; client_entry_st *e, *te; @@ -100,6 +100,7 @@ client_entry_st *new_client_entry(sec_mod_st *sec, const char *ip, unsigned pid) strlcpy(e->acct_info.remote_ip, ip, sizeof(e->acct_info.remote_ip)); e->acct_info.id = pid; + e->vhost = vhost; do { ret = gnutls_rnd(GNUTLS_RND_RANDOM, e->sid, sizeof(e->sid)); @@ -120,7 +121,7 @@ client_entry_st *new_client_entry(sec_mod_st *sec, const char *ip, unsigned pid) calc_safe_id(e->sid, SID_SIZE, (char *)e->acct_info.safe_id, sizeof(e->acct_info.safe_id)); now = time(0); - e->exptime = now + sec->config->cookie_timeout + AUTH_SLACK_TIME; + e->exptime = now + vhost->perm_config.config->cookie_timeout + AUTH_SLACK_TIME; e->created = now; if (htable_add(db, rehash(e, NULL), e) == 0) { @@ -196,7 +197,7 @@ void expire_client_entry(sec_mod_st *sec, client_entry_st * e) if (e->in_use > 0) e->in_use--; if (e->in_use == 0) { - if (sec->config->persistent_cookies == 0 && (e->discon_reason == REASON_SERVER_DISCONNECT || + if (e->vhost->perm_config.config->persistent_cookies == 0 && (e->discon_reason == REASON_SERVER_DISCONNECT || e->discon_reason == REASON_SESSION_TIMEOUT)) { seclog(sec, LOG_INFO, "invalidating session of user '%s' "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); @@ -209,10 +210,10 @@ void expire_client_entry(sec_mod_st *sec, client_entry_st * e) * explicitly disconnect with the intention to reconnect * seconds later. */ if (e->discon_reason == REASON_USER_DISCONNECT) { - if (!sec->config->persistent_cookies || (now+AUTH_SLACK_TIME >= e->exptime)) + if (!e->vhost->perm_config.config->persistent_cookies || (now+AUTH_SLACK_TIME >= e->exptime)) e->exptime = now + AUTH_SLACK_TIME; } else { - e->exptime = now + sec->config->cookie_timeout + AUTH_SLACK_TIME; + e->exptime = now + e->vhost->perm_config.config->cookie_timeout + AUTH_SLACK_TIME; } seclog(sec, LOG_INFO, "temporarily closing session for %s "SESSION_STR, e->acct_info.username, e->acct_info.safe_id); } diff --git a/src/sec-mod-resume.c b/src/sec-mod-resume.c index ca3b4ee4..8a621d15 100644 --- a/src/sec-mod-resume.c +++ b/src/sec-mod-resume.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,14 +33,14 @@ #include #include -#include +#include #include #include #include #include int handle_resume_delete_req(sec_mod_st *sec, - const SessionResumeFetchMsg * req) + const SessionResumeFetchMsg *req) { tls_cache_st *cache; struct htable_iter iter; @@ -70,8 +70,8 @@ int handle_resume_delete_req(sec_mod_st *sec, } int handle_resume_fetch_req(sec_mod_st *sec, - const SessionResumeFetchMsg * req, - SessionResumeReplyMsg * rep) + const SessionResumeFetchMsg *req, + SessionResumeReplyMsg *rep) { tls_cache_st *cache; struct htable_iter iter; @@ -87,6 +87,11 @@ int handle_resume_fetch_req(sec_mod_st *sec, memcmp(req->session_id.data, cache->session_id, req->session_id.len) == 0) { + if (req->vhost && cache->vhostname && c_strcasecmp(req->vhost, cache->vhostname) != 0) + return 0; + else if (req->vhost != cache->vhostname) + return 0; + if (req->cli_addr.len == cache->remote_addr_len && ip_cmp((struct sockaddr_storage *)req->cli_addr.data, &cache->remote_addr) == 0) { @@ -116,7 +121,7 @@ int handle_resume_fetch_req(sec_mod_st *sec, } int handle_resume_store_req(sec_mod_st *sec, - const SessionResumeStoreReqMsg * req) + const SessionResumeStoreReqMsg *req) { tls_cache_st *cache; size_t key; @@ -127,7 +132,7 @@ int handle_resume_store_req(sec_mod_st *sec, if (req->session_data.len > MAX_SESSION_DATA_SIZE) return -1; - max = MAX(2 * sec->config->max_clients, DEFAULT_MAX_CACHED_TLS_SESSIONS); + max = MAX(2 * GETCONFIG(sec)->max_clients, DEFAULT_MAX_CACHED_TLS_SESSIONS); if (sec->tls_db.entries >= max) { seclog(sec, LOG_INFO, "maximum number of stored TLS sessions reached (%u)", @@ -150,6 +155,10 @@ int handle_resume_store_req(sec_mod_st *sec, cache->session_id_size = req->session_id.len; cache->session_data_size = req->session_data.len; cache->remote_addr_len = req->cli_addr.len; + if (req->vhost) + cache->vhostname = talloc_strdup(cache, req->vhost); + else + cache->vhostname = NULL; memcpy(cache->session_id, req->session_id.data, req->session_id.len); memcpy(cache->session_data, req->session_data.data, @@ -183,7 +192,7 @@ void expire_tls_sessions(sec_mod_st *sec) exp = gnutls_db_check_entry_time(&d); - if (now - exp > TLS_SESSION_EXPIRATION_TIME(sec->config)) { + if (now - exp > TLS_SESSION_EXPIRATION_TIME(GETCONFIG(sec))) { cache->session_id_size = 0; htable_delval(sec->tls_db.ht, &iter); diff --git a/src/sec-mod-sup-config.c b/src/sec-mod-sup-config.c index 9b2ad933..0e0ddd8d 100644 --- a/src/sec-mod-sup-config.c +++ b/src/sec-mod-sup-config.c @@ -32,14 +32,18 @@ void sup_config_init(sec_mod_st *sec) { - if (sec->perm_config->sup_config_type == SUP_CONFIG_FILE) { - seclog(sec, LOG_INFO, "reading supplemental config from files"); - sec->config_module = &file_sup_config; + vhost_cfg_st *vhost = NULL; + + list_for_each(sec->vconfig, vhost, list) { + if (vhost->perm_config.sup_config_type == SUP_CONFIG_FILE) { + seclog(sec, LOG_INFO, "%sreading supplemental config from files", PREFIX_VHOST(vhost)); + vhost->config_module = &file_sup_config; #ifdef HAVE_RADIUS - } else if (sec->perm_config->sup_config_type == SUP_CONFIG_RADIUS) { - seclog(sec, LOG_INFO, "reading supplemental config from radius"); - sec->config_module = &radius_sup_config; + } else if (vhost->perm_config.sup_config_type == SUP_CONFIG_RADIUS) { + seclog(sec, LOG_INFO, "%sreading supplemental config from radius", PREFIX_VHOST(vhost)); + vhost->config_module = &radius_sup_config; #endif + } } } diff --git a/src/sec-mod.c b/src/sec-mod.c index c4fc5875..de0242e8 100644 --- a/src/sec-mod.c +++ b/src/sec-mod.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2014, 2015 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * * This file is part of ocserv. * @@ -36,30 +36,25 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include #include #include -#define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN #define MAINTAINANCE_TIME 310 static int need_maintainance = 0; static int need_reload = 0; static int need_exit = 0; -struct pin_st { - char pin[MAX_PIN_SIZE]; - char srk_pin[MAX_PIN_SIZE]; -}; - static int load_keys(sec_mod_st *sec, unsigned force); static @@ -199,13 +194,14 @@ static int handle_op(void *pool, int cfd, sec_mod_st * sec, uint8_t type, uint8_ } static -int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_request_t cmd, +int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st *sec, cmd_request_t cmd, uint8_t * buffer, size_t buffer_size) { unsigned i; gnutls_datum_t data, out; int ret; SecOpMsg *op; + vhost_cfg_st *vhost; #if GNUTLS_VERSION_NUMBER >= 0x030600 SecGetPkMsg *pkm; #endif @@ -225,14 +221,18 @@ int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_ return -1; } + vhost = find_vhost(sec->vconfig, pkm->vhost); + /* check for static analyzer - find_vhost is always non-NULL */ + assert(vhost != NULL); + i = pkm->key_idx; - if (i >= sec->key_size) { + if (i >= vhost->key_size) { seclog(sec, LOG_INFO, - "received out-of-bounds key index (%d)", i); + "%sreceived out-of-bounds key index (%d); have %d keys", PREFIX_VHOST(vhost), i, vhost->key_size); return -1; } - pkm->pk = gnutls_privkey_get_pk_algorithm(sec->key[i], NULL); + pkm->pk = gnutls_privkey_get_pk_algorithm(vhost->key[i], NULL); ret = send_msg(pool, cfd, CMD_SEC_GET_PK, pkm, (pack_size_func) sec_get_pk_msg__get_packed_size, @@ -256,10 +256,13 @@ int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_ return -1; } + vhost = find_vhost(sec->vconfig, op->vhost); + assert(vhost != NULL); + i = op->key_idx; - if (op->has_key_idx == 0 || i >= sec->key_size) { + if (op->has_key_idx == 0 || i >= vhost->key_size) { seclog(sec, LOG_INFO, - "received out-of-bounds key index (%d)", i); + "%sreceived out-of-bounds key index (%d); have %d keys", PREFIX_VHOST(vhost), i, vhost->key_size); return -1; } @@ -267,9 +270,9 @@ int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_ data.size = op->data.len; if (cmd == CMD_SEC_SIGN_DATA) { - ret = gnutls_privkey_sign_data2(sec->key[i], op->sig, 0, &data, &out); + ret = gnutls_privkey_sign_data2(vhost->key[i], op->sig, 0, &data, &out); } else { - ret = gnutls_privkey_sign_hash2(sec->key[i], op->sig, 0, &data, &out); + ret = gnutls_privkey_sign_hash2(vhost->key[i], op->sig, 0, &data, &out); } sec_op_msg__free_unpacked(op, &pa); @@ -292,10 +295,13 @@ int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_ return -1; } + vhost = find_vhost(sec->vconfig, op->vhost); + assert(vhost != NULL); + i = op->key_idx; - if (op->has_key_idx == 0 || i >= sec->key_size) { + if (op->has_key_idx == 0 || i >= vhost->key_size) { seclog(sec, LOG_INFO, - "received out-of-bounds key index (%d)", i); + "%sreceived out-of-bounds key index (%d); have %d keys", PREFIX_VHOST(vhost), i, vhost->key_size); return -1; } @@ -304,17 +310,17 @@ int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_ if (cmd == CMD_SEC_DECRYPT) { ret = - gnutls_privkey_decrypt_data(sec->key[i], 0, &data, + gnutls_privkey_decrypt_data(vhost->key[i], 0, &data, &out); } else { #if GNUTLS_VERSION_NUMBER >= 0x030200 ret = - gnutls_privkey_sign_hash(sec->key[i], 0, + gnutls_privkey_sign_hash(vhost->key[i], 0, GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA, &data, &out); #else ret = - gnutls_privkey_sign_raw_data(sec->key[i], 0, &data, + gnutls_privkey_sign_raw_data(vhost->key[i], 0, &data, &out); #endif } @@ -575,8 +581,8 @@ static void send_stats_to_main(sec_mod_st *sec) time_t now = time(0); SecmStatsMsg msg = SECM_STATS_MSG__INIT; - if (sec->perm_config->stats_reset_time != 0 && - now - sec->last_stats_reset > sec->perm_config->stats_reset_time) { + if (GETPCONFIG(sec)->stats_reset_time != 0 && + now - sec->last_stats_reset > GETPCONFIG(sec)->stats_reset_time) { sec->auth_failures = 0; sec->avg_auth_time = 0; sec->max_auth_time = 0; @@ -608,24 +614,36 @@ static void send_stats_to_main(sec_mod_st *sec) static void check_other_work(sec_mod_st *sec) { + vhost_cfg_st *vhost = NULL; + if (need_exit) { unsigned i; - for (i = 0; i < sec->key_size; i++) { - gnutls_privkey_deinit(sec->key[i]); + list_for_each(sec->vconfig, vhost, list) { + for (i = 0; i < vhost->key_size; i++) { + gnutls_privkey_deinit(vhost->key[i]); + vhost->key[i] = NULL; + } + vhost->key_size = 0; } sec_mod_client_db_deinit(sec); tls_cache_deinit(&sec->tls_db); + talloc_free(sec->config_pool); talloc_free(sec); exit(0); } if (need_reload) { seclog(sec, LOG_DEBUG, "reloading configuration"); - reload_cfg_file(sec, sec->perm_config, 0); - sec->config = sec->perm_config->config; + reload_cfg_file(sec, sec->vconfig, 0); load_keys(sec, 0); + + list_for_each(sec->vconfig, vhost, list) { + sec_auth_init(vhost); + } + sup_config_init(sec); + need_reload = 0; } @@ -736,90 +754,96 @@ int serve_request_worker(sec_mod_st *sec, int cfd, pid_t pid, uint8_t *buffer, u #define CHECK_LOOP_ERR(x) \ if (force != 0) { GNUTLS_FATAL_ERR(x); } \ else { if (ret < 0) { \ - seclog(sec, LOG_ERR, "could not reload key %s", sec->perm_config->key[i]); \ + seclog(sec, LOG_ERR, "could not reload key %s", vhost->perm_config.key[i]); \ continue; } \ } static int load_keys(sec_mod_st *sec, unsigned force) { - unsigned i, need_reload = 0; + unsigned i, need_reload; int ret; - struct pin_st pins; - static time_t last_access = 0; + vhost_cfg_st *vhost = NULL; - for (i = 0; i < sec->perm_config->key_size; i++) { - if (need_file_reload(sec->perm_config->key[i], last_access) != 0) { - need_reload = 1; - break; - } - } + list_for_each_rev(sec->vconfig, vhost, list) { + if (force == 0) { + need_reload = 0; - if (need_reload == 0) - return 0; - - last_access = time(0); - - ret = load_pins(sec->perm_config, &pins); - if (ret < 0) { - seclog(sec, LOG_ERR, "error loading PIN files"); - exit(1); - } - - /* Reminder: the number of private keys or their filenames cannot be changed on reload - */ - if (sec->key == NULL) { - sec->key_size = sec->perm_config->key_size; - sec->key = talloc_zero_size(sec, sizeof(*sec->key) * sec->perm_config->key_size); - if (sec->key == NULL) { - seclog(sec, LOG_ERR, "error in memory allocation"); - exit(1); - } - } - - /* read private keys */ - for (i = 0; i < sec->key_size; i++) { - gnutls_privkey_t p; - - ret = gnutls_privkey_init(&p); - CHECK_LOOP_ERR(ret); - /* load the private key */ - if (gnutls_url_is_supported(sec->perm_config->key[i]) != 0) { - gnutls_privkey_set_pin_function(p, - pin_callback, &pins); - ret = - gnutls_privkey_import_url(p, - sec->perm_config->key[i], 0); - CHECK_LOOP_ERR(ret); - } else { - gnutls_datum_t data; - ret = gnutls_load_file(sec->perm_config->key[i], &data); - if (ret < 0) { - seclog(sec, LOG_ERR, "error loading file '%s'", - sec->perm_config->key[i]); - CHECK_LOOP_ERR(ret); + for (i = 0; i < vhost->perm_config.key_size; i++) { + if (need_file_reload(vhost->perm_config.key[i], vhost->cert_last_access) != 0) { + need_reload = 1; + break; + } } - ret = - gnutls_privkey_import_x509_raw(p, &data, - GNUTLS_X509_FMT_PEM, - NULL, 0); - if (ret == GNUTLS_E_DECRYPTION_FAILED && pins.pin[0]) { + if (need_reload == 0) + continue; + } + + vhost->cert_last_access = time(0); + + ret = load_pins(GETPCONFIG(sec), &vhost->pins); + if (ret < 0) { + seclog(sec, LOG_ERR, "error loading PIN files"); + exit(1); + } + + /* Reminder: the number of private keys or their filenames cannot be changed on reload + */ + if (vhost->key == NULL) { + vhost->key_size = vhost->perm_config.key_size; + vhost->key = talloc_zero_size(sec, sizeof(*vhost->key) * vhost->perm_config.key_size); + if (vhost->key == NULL) { + seclog(sec, LOG_ERR, "error in memory allocation"); + exit(1); + } + } + + /* read private keys */ + for (i = 0; i < vhost->key_size; i++) { + gnutls_privkey_t p; + + ret = gnutls_privkey_init(&p); + CHECK_LOOP_ERR(ret); + + /* load the private key */ + if (gnutls_url_is_supported(vhost->perm_config.key[i]) != 0) { + gnutls_privkey_set_pin_function(p, + pin_callback, &vhost->pins); + ret = + gnutls_privkey_import_url(p, + vhost->perm_config.key[i], 0); + CHECK_LOOP_ERR(ret); + } else { + gnutls_datum_t data; + ret = gnutls_load_file(vhost->perm_config.key[i], &data); + if (ret < 0) { + seclog(sec, LOG_ERR, "error loading file '%s'", + vhost->perm_config.key[i]); + CHECK_LOOP_ERR(ret); + } + ret = gnutls_privkey_import_x509_raw(p, &data, GNUTLS_X509_FMT_PEM, - pins.pin, 0); + NULL, 0); + if (ret == GNUTLS_E_DECRYPTION_FAILED && vhost->pins.pin[0]) { + ret = + gnutls_privkey_import_x509_raw(p, &data, + GNUTLS_X509_FMT_PEM, + vhost->pins.pin, 0); + } + CHECK_LOOP_ERR(ret); + + gnutls_free(data.data); } - CHECK_LOOP_ERR(ret); - gnutls_free(data.data); + if (vhost->key[i] != NULL) { + gnutls_privkey_deinit(vhost->key[i]); + } + vhost->key[i] = p; } - - if (sec->key[i] != NULL) { - gnutls_privkey_deinit(sec->key[i]); - } - sec->key[i] = p; + seclog(sec, LOG_DEBUG, "%sloaded %d keys\n", PREFIX_VHOST(vhost), vhost->key_size); } - return 0; } @@ -852,8 +876,8 @@ static int load_keys(sec_mod_st *sec, unsigned force) * clients fast without becoming a bottleneck due to private * key operations. */ -void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char *socket_file, - int cmd_fd, int cmd_fd_sync) +void sec_mod_server(void *main_pool, void *config_pool, struct list_head *vconfig, + const char *socket_file, int cmd_fd, int cmd_fd_sync) { struct sockaddr_un sa; socklen_t sa_len; @@ -864,6 +888,7 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char int sd; sec_mod_st *sec; void *sec_mod_pool; + vhost_cfg_st *vhost = NULL; fd_set rd_set; pid_t pid; #ifdef HAVE_PSELECT @@ -895,8 +920,8 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char exit(1); } - sec->perm_config = talloc_steal(sec, perm_config); - sec->config = sec->perm_config->config; + sec->vconfig = vconfig; + sec->config_pool = config_pool; tls_cache_init(sec, &sec->tls_db); sup_config_init(sec); @@ -916,7 +941,10 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char ocsignal(SIGTERM, handle_sigterm); ocsignal(SIGALRM, handle_alarm); - sec_auth_init(sec, perm_config); + list_for_each(sec->vconfig, vhost, list) { + sec_auth_init(vhost); + } + sec->cmd_fd = cmd_fd; sec->cmd_fd_sync = cmd_fd_sync; @@ -943,7 +971,7 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char exit(1); } - ret = chown(SOCKET_FILE, perm_config->uid, perm_config->gid); + ret = chown(SOCKET_FILE, GETPCONFIG(sec)->uid, GETPCONFIG(sec)->gid); if (ret == -1) { e = errno; seclog(sec, LOG_INFO, "could not chown socket '%s': %s", SOCKET_FILE, @@ -1049,7 +1077,9 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char /* do not allow unauthorized processes to issue commands */ - ret = check_upeer_id("sec-mod", sec->perm_config->debug, cfd, perm_config->uid, perm_config->gid, &uid, &pid); + ret = check_upeer_id("sec-mod", GETPCONFIG(sec)->debug, cfd, + GETPCONFIG(sec)->uid, GETPCONFIG(sec)->gid, + &uid, &pid); if (ret < 0) { seclog(sec, LOG_INFO, "rejected unauthorized connection"); } else { diff --git a/src/sec-mod.h b/src/sec-mod.h index 24de3914..18161bd9 100644 --- a/src/sec-mod.h +++ b/src/sec-mod.h @@ -27,14 +27,15 @@ #include #include "common/common.h" +#include "vhost.h" + #define SESSION_STR "(session: %.6s)" #define MAX_GROUPS 32 typedef struct sec_mod_st { - struct cfg_st *config; - struct perm_cfg_st *perm_config; - gnutls_privkey_t *key; - unsigned key_size; + struct list_head *vconfig; + void *config_pool; + struct htable *client_db; int cmd_fd; int cmd_fd_sync; @@ -45,8 +46,6 @@ typedef struct sec_mod_st { uint32_t avg_auth_time; /* the average time spent in (sucessful) authentication */ uint32_t total_authentications; /* successful authentications: to calculate the average above */ time_t last_stats_reset; - - struct config_mod_st *config_module; } sec_mod_st; typedef struct stats_st { @@ -86,7 +85,7 @@ typedef struct client_entry_st { */ uint8_t sid[SID_SIZE]; - void * auth_ctx; /* the context of authentication */ + void *auth_ctx; /* the context of authentication */ unsigned session_is_open; /* whether open_session was done */ unsigned in_use; /* counter of users of this structure */ unsigned tls_auth_ok; @@ -120,12 +119,17 @@ typedef struct client_entry_st { /* the module this entry is using */ const struct auth_mod_st *module; + void *vhost_auth_ctx; + void *vhost_acct_ctx; + + /* the vhost this user is associated with */ + vhost_cfg_st *vhost; } client_entry_st; void *sec_mod_client_db_init(sec_mod_st *sec); void sec_mod_client_db_deinit(sec_mod_st *sec); unsigned sec_mod_client_db_elems(sec_mod_st *sec); -client_entry_st * new_client_entry(sec_mod_st *sec, const char *ip, unsigned pid); +client_entry_st * new_client_entry(sec_mod_st *sec, struct vhost_cfg_st *, const char *ip, unsigned pid); client_entry_st * find_client_entry(sec_mod_st *sec, uint8_t sid[SID_SIZE]); void del_client_entry(sec_mod_st *sec, client_entry_st * e); void expire_client_entry(sec_mod_st *sec, client_entry_st * e); @@ -133,12 +137,12 @@ void cleanup_client_entries(sec_mod_st *sec); #ifdef __GNUC__ # define seclog(sec, prio, fmt, ...) \ - if (prio != LOG_DEBUG || sec->perm_config->debug >= 3) { \ + if (prio != LOG_DEBUG || GETPCONFIG(sec)->debug >= 3) { \ syslog(prio, "sec-mod: "fmt, ##__VA_ARGS__); \ } #else # define seclog(sec,prio,...) \ - if (prio != LOG_DEBUG || sec->config->debug >= 3) { \ + if (prio != LOG_DEBUG || GETPCONFIG(sec)->debug >= 3) { \ syslog(prio, __VA_ARGS__); \ } #endif @@ -146,7 +150,7 @@ void cleanup_client_entries(sec_mod_st *sec); void seclog_hex(const struct sec_mod_st* sec, int priority, const char *prefix, uint8_t* bin, unsigned bin_size, unsigned b64); -void sec_auth_init(sec_mod_st *sec, struct perm_cfg_st *config); +void sec_auth_init(struct vhost_cfg_st *vhost); void handle_secm_list_cookies_reply(void *pool, int fd, sec_mod_st *sec); void handle_sec_auth_ban_ip_reply(sec_mod_st *sec, const BanIpReplyMsg *msg); @@ -155,9 +159,10 @@ int handle_sec_auth_cont(int cfd, sec_mod_st *sec, const SecAuthContMsg * req); int handle_secm_session_open_cmd(sec_mod_st *sec, int fd, const SecmSessionOpenMsg *req); int handle_secm_session_close_cmd(sec_mod_st *sec, int fd, const SecmSessionCloseMsg *req); int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req, pid_t pid); -void sec_auth_user_deinit(sec_mod_st * sec, client_entry_st * e); +void sec_auth_user_deinit(sec_mod_st *sec, client_entry_st *e); -void sec_mod_server(void *main_pool, struct perm_cfg_st *config, const char *socket_file, +void sec_mod_server(void *main_pool, void *config_pool, struct list_head *vconfig, + const char *socket_file, int cmd_fd, int cmd_fd_sync); #endif diff --git a/src/subconfig.c b/src/subconfig.c index baaed6ac..fa9175ce 100644 --- a/src/subconfig.c +++ b/src/subconfig.c @@ -103,18 +103,18 @@ unsigned expand_brackets_string(void *pool, const char *str, subcfg_val_st out[M } #ifdef HAVE_GSSAPI -void *gssapi_get_brackets_string(struct perm_cfg_st *config, const char *str) +void *gssapi_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str) { subcfg_val_st vals[MAX_SUBOPTIONS]; unsigned vals_size, i; gssapi_cfg_st *additional; - additional = talloc_zero(config, gssapi_cfg_st); + additional = talloc_zero(pool, gssapi_cfg_st); if (additional == NULL) { return NULL; } - vals_size = expand_brackets_string(config, str, vals); + vals_size = expand_brackets_string(pool, str, vals); for (i=0;ikeytab = vals[i].value; @@ -205,14 +205,14 @@ static void *get_brackets_string2(void *pool, const char *str) return talloc_strndup(pool, p, len); } -void *radius_get_brackets_string(struct perm_cfg_st *config, const char *str) +void *radius_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str) { char *p; subcfg_val_st vals[MAX_SUBOPTIONS]; unsigned vals_size, i; radius_cfg_st *additional; - additional = talloc_zero(config, radius_cfg_st); + additional = talloc_zero(pool, radius_cfg_st); if (additional == NULL) { return NULL; } @@ -220,7 +220,7 @@ void *radius_get_brackets_string(struct perm_cfg_st *config, const char *str) if (str && str[0] == '[' && (str[1] == '/' || str[1] == '.')) { /* legacy format */ fprintf(stderr, "Parsing radius auth method subconfig using legacy format\n"); - additional->config = get_brackets_string1(config, str); + additional->config = get_brackets_string1(pool, str); p = get_brackets_string2(config, str); if (p != NULL) { @@ -232,7 +232,7 @@ void *radius_get_brackets_string(struct perm_cfg_st *config, const char *str) } } else { /* new format */ - vals_size = expand_brackets_string(config, str, vals); + vals_size = expand_brackets_string(pool, str, vals); for (i=0;iconfig = vals[i].value; @@ -261,19 +261,19 @@ void *radius_get_brackets_string(struct perm_cfg_st *config, const char *str) #endif #ifdef HAVE_PAM -void *pam_get_brackets_string(struct perm_cfg_st *config, const char *str) +void *pam_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str) { subcfg_val_st vals[MAX_SUBOPTIONS]; unsigned vals_size, i; pam_cfg_st *additional; - additional = talloc_zero(config, pam_cfg_st); + additional = talloc_zero(pool, pam_cfg_st); if (additional == NULL) { return NULL; } /* new format */ - vals_size = expand_brackets_string(config, str, vals); + vals_size = expand_brackets_string(pool, str, vals); for (i=0;igid_min = atoi(vals[i].value); @@ -292,22 +292,22 @@ void *pam_get_brackets_string(struct perm_cfg_st *config, const char *str) } #endif -void *plain_get_brackets_string(struct perm_cfg_st *config, const char *str) +void *plain_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str) { subcfg_val_st vals[MAX_SUBOPTIONS]; unsigned vals_size, i; plain_cfg_st *additional; - additional = talloc_zero(config, plain_cfg_st); + additional = talloc_zero(pool, plain_cfg_st); if (additional == NULL) { return NULL; } if (str && str[0] == '[' && (str[1] == '/' || str[1] == '.')) { /* legacy format */ fprintf(stderr, "Parsing plain auth method subconfig using legacy format\n"); - additional->passwd = get_brackets_string1(config, str); + additional->passwd = get_brackets_string1(pool, str); } else { - vals_size = expand_brackets_string(config, str, vals); + vals_size = expand_brackets_string(pool, str, vals); for (i=0;ipasswd = vals[i].value; diff --git a/src/tlslib.c b/src/tlslib.c index 7d50c5bf..5f7db07b 100644 --- a/src/tlslib.c +++ b/src/tlslib.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos * Copyright (C) 2015-2016 Red Hat, Inc. * * This file is part of ocserv. @@ -51,7 +51,7 @@ #include #include -static void tls_reload_ocsp(main_server_st* s, tls_st *creds); +static void tls_reload_ocsp(main_server_st* s, struct vhost_cfg_st *vhost); void cstp_cork(worker_st *ws) { @@ -185,9 +185,9 @@ static ssize_t _cstp_recv_packet(worker_st *ws, void *data, size_t data_size) uint8_t *p = data; /* read the header */ - ret = recv_remaining(ws->conn_fd, p, 8); - if (ret <= 0) - return ret; + ret = recv_remaining(ws->conn_fd, p, 8); + if (ret <= 0) + return ret; /* get the actual length from headers */ pktlen = (p[4] << 8) + p[5]; @@ -274,7 +274,7 @@ unsigned tls_has_session_cert(struct worker_st * ws) if (ws->cert_auth_ok) return 1; - if (ws->config->cisco_client_compat == 0) { + if (WSCONFIG(ws)->cisco_client_compat == 0) { return 0; } @@ -383,7 +383,7 @@ void dtls_close(worker_st *ws) static size_t rehash(const void *_e, void *unused) { -const tls_cache_st *e = _e; + const tls_cache_st *e = _e; return hash_any(e->session_id, e->session_id_size, 0); } @@ -400,19 +400,19 @@ void tls_cache_init(void *pool, tls_sess_db_st* db) void tls_cache_deinit(tls_sess_db_st* db) { -tls_cache_st* cache; -struct htable_iter iter; + tls_cache_st* cache; + struct htable_iter iter; cache = htable_first(db->ht, &iter); while(cache != NULL) { if (cache->session_data_size > 0) { - safe_memset(cache->session_data, 0, cache->session_data_size); - cache->session_data_size = 0; - cache->session_id_size = 0; + safe_memset(cache->session_data, 0, cache->session_data_size); + cache->session_data_size = 0; + cache->session_id_size = 0; } - talloc_free(cache); + talloc_free(cache); - cache = htable_next(db->ht, &iter); + cache = htable_next(db->ht, &iter); } htable_clear(db->ht); db->entries = 0; @@ -428,7 +428,7 @@ static void tls_log_func(int level, const char *str) static void tls_audit_log_func(gnutls_session_t session, const char *str) { -worker_st * ws; + worker_st * ws; if (session == NULL) syslog(LOG_AUTH, "warning: %s", str); @@ -516,111 +516,131 @@ static int verify_certificate_cb(gnutls_session_t session) /* notify gnutls to continue handshake normally */ return 0; no_cert: - if (ws->config->cisco_client_compat != 0 || ws->config->cert_req != GNUTLS_CERT_REQUIRE) + if (WSCONFIG(ws)->cisco_client_compat != 0 || WSCONFIG(ws)->cert_req != GNUTLS_CERT_REQUIRE) return 0; fail: return GNUTLS_E_CERTIFICATE_ERROR; } -void tls_global_init(tls_st *creds) +void tls_global_init(void) { -int ret; - gnutls_global_set_audit_log_function(tls_audit_log_func); - - ret = gnutls_global_init(); - GNUTLS_FATAL_ERR(ret); - - ret = gnutls_psk_allocate_server_credentials(&creds->pskcred); - GNUTLS_FATAL_ERR(ret); - - return; } -void tls_global_deinit(tls_st *creds) +void tls_vhost_init(struct vhost_cfg_st *vhost) { - if (creds->xcred != NULL) - gnutls_certificate_free_credentials(creds->xcred); - if (creds->pskcred != NULL) - gnutls_psk_free_server_credentials(creds->pskcred); - if (creds->cprio != NULL) - gnutls_priority_deinit(creds->cprio); + int ret; - gnutls_global_deinit(); + ret = gnutls_psk_allocate_server_credentials(&vhost->creds.pskcred); + GNUTLS_FATAL_ERR(ret); +} + +void tls_vhost_deinit(struct vhost_cfg_st *vhost) +{ + if (vhost->creds.xcred != NULL) + gnutls_certificate_free_credentials(vhost->creds.xcred); + if (vhost->creds.pskcred != NULL) + gnutls_psk_free_server_credentials(vhost->creds.pskcred); + if (vhost->creds.cprio != NULL) + gnutls_priority_deinit(vhost->creds.cprio); + + gnutls_free(vhost->creds.ocsp_response.data); + vhost->creds.ocsp_response.data = NULL; + vhost->creds.xcred = NULL; + vhost->creds.pskcred = NULL; + vhost->creds.cprio = NULL; return; } /* Checks, if there is a single certificate specified, whether it * is compatible with all ciphersuites */ -static void certificate_check(main_server_st *s) +static void certificate_check(main_server_st *s, const char *vhostname, gnutls_pcert_st *pcert) { -gnutls_datum_t data = {NULL, 0}; -gnutls_x509_crt_t crt = NULL; -int ret; -unsigned usage; + gnutls_datum_t data = {NULL, 0}; + gnutls_x509_crt_t crt = NULL; + int ret; + unsigned usage; + gnutls_datum_t dn = {NULL, 0}; + const char *cert_name = "unnamed"; + time_t t; - if (s->perm_config->cert_size > 1) - return; + ret = gnutls_x509_crt_init(&crt); + GNUTLS_FATAL_ERR(ret); - if (gnutls_url_is_supported(s->perm_config->cert[0]) == 0) { - /* no URL */ - ret = gnutls_load_file(s->perm_config->cert[0], &data); - if (ret < 0) - return; + ret = gnutls_x509_crt_import(crt, &pcert->cert, GNUTLS_X509_FMT_DER); + GNUTLS_FATAL_ERR(ret); - ret = gnutls_x509_crt_init(&crt); - GNUTLS_FATAL_ERR(ret); + ret = gnutls_x509_crt_get_pk_algorithm(crt, NULL); + if (ret != GNUTLS_PK_RSA) + goto cleanup; - ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_PEM); - GNUTLS_FATAL_ERR(ret); - - ret = gnutls_x509_crt_get_pk_algorithm(crt, NULL); - if (ret != GNUTLS_PK_RSA) - goto cleanup; - - ret = gnutls_x509_crt_get_key_usage(crt, &usage, NULL); - if (ret >= 0) { - if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { - mslog(s, NULL, LOG_WARNING, "server certificate key usage prevents key encipherment; unable to support the RSA ciphersuites; " - "if that is not intentional, regenerate the server certificate with the key usage flag 'key encipherment' set."); -#if GNUTLS_VERSION_NUMBER < 0x030506 - if (s->perm_config->dh_params_file == NULL) - mslog(s, NULL, LOG_WARNING, "no DH-params file specified; server will be limited to ECDHE ciphersuites\n"); +#if GNUTLS_VERSION_NUMBER >= 0x030507 + ret = gnutls_x509_crt_get_dn3(crt, &dn, 0); +#else + ret = gnutls_x509_crt_get_dn2(crt, &dn); #endif - } + if (ret >= 0) { + cert_name = (char*)dn.data; + } + + ret = gnutls_x509_crt_get_key_usage(crt, &usage, NULL); + if (ret >= 0) { + if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + mslog(s, NULL, LOG_WARNING, "%s certificate key usage prevents key encipherment; unable to support the RSA ciphersuites; " + "if that is not intentional, regenerate the server certificate with the key usage flag 'key encipherment' set.", + cert_name); } } + if (vhostname) { + /* check whether the hostname matches our vhost */ + if (!gnutls_x509_crt_check_hostname(crt, vhostname)) { + mslog(s, NULL, LOG_WARNING, "The %s certificate's name doesn't match for vhost %s", + cert_name, vhostname); + } + } + + t = gnutls_x509_crt_get_expiration_time(crt); + if (t < time(0)) { + mslog(s, NULL, LOG_WARNING, "The %s certificate set is expired!", cert_name); + } + + t = gnutls_x509_crt_get_activation_time(crt); + if (t > time(0)) { + mslog(s, NULL, LOG_WARNING, "The %s certificate set is not yet active!", cert_name); + } + cleanup: if (crt != NULL) - gnutls_x509_crt_deinit(crt); + gnutls_x509_crt_deinit(crt); gnutls_free(data.data); + gnutls_free(dn.data); return; } -static void set_dh_params(main_server_st* s, tls_st *creds) +static void set_dh_params(main_server_st* s, struct vhost_cfg_st *vhost) { -gnutls_datum_t data; -int ret; + gnutls_datum_t data; + int ret; - if (s->perm_config->dh_params_file != NULL) { - ret = gnutls_dh_params_init (&creds->dh_params); + if (vhost->perm_config.dh_params_file != NULL) { + ret = gnutls_dh_params_init (&vhost->creds.dh_params); GNUTLS_FATAL_ERR(ret); - ret = gnutls_load_file(s->perm_config->dh_params_file, &data); + ret = gnutls_load_file(vhost->perm_config.dh_params_file, &data); GNUTLS_FATAL_ERR(ret); - ret = gnutls_dh_params_import_pkcs3(creds->dh_params, &data, GNUTLS_X509_FMT_PEM); + ret = gnutls_dh_params_import_pkcs3(vhost->creds.dh_params, &data, GNUTLS_X509_FMT_PEM); GNUTLS_FATAL_ERR(ret); gnutls_free(data.data); - gnutls_certificate_set_dh_params(creds->xcred, creds->dh_params); + gnutls_certificate_set_dh_params(vhost->creds.xcred, vhost->creds.dh_params); } else { #if GNUTLS_VERSION_NUMBER >= 0x030506 /* use pre-generated parameters */ - gnutls_certificate_set_known_dh_params(creds->xcred, GNUTLS_SEC_PARAM_MEDIUM); + gnutls_certificate_set_known_dh_params(vhost->creds.xcred, GNUTLS_SEC_PARAM_MEDIUM); #endif } } @@ -631,6 +651,7 @@ struct key_cb_data { unsigned idx; /* the index of the key */ struct sockaddr_un sa; unsigned sa_len; + const char *vhost; }; static @@ -665,6 +686,7 @@ int key_cb_common_func (gnutls_privkey_t key, void* userdata, const gnutls_datum msg.sig = sigalgo; msg.data.data = raw_data->data; msg.data.len = raw_data->size; + msg.vhost = (char*)cdata->vhost; ret = send_msg(userdata, sd, type, &msg, (pack_size_func)sec_op_msg__get_packed_size, @@ -736,6 +758,7 @@ int key_cb_get_pk (gnutls_privkey_t key, void* userdata) } msg.key_idx = cdata->idx; + msg.vhost = cdata->vhost; msg.pk = 0; ret = send_msg(userdata, sd, CMD_SEC_GET_PK, &msg, @@ -833,25 +856,26 @@ static void key_cb_deinit_func(gnutls_privkey_t key, void* userdata) } static -int load_cert_files(main_server_st *s, tls_st *creds) +int load_cert_files(main_server_st *s, struct vhost_cfg_st *vhost) { int ret; gnutls_pcert_st *pcert_list; unsigned pcert_list_size, i; gnutls_privkey_t key; gnutls_datum_t data; - struct key_cb_data * cdata; + struct key_cb_data *cdata; unsigned flags; - for (i=0;iperm_config->key_size;i++) { + for (i=0;iperm_config.key_size;i++) { /* load the certificate */ - if (gnutls_url_is_supported(s->perm_config->cert[i]) != 0) { - mslog(s, NULL, LOG_ERR, "Loading a certificate from '%s' is unsupported", s->perm_config->cert[i]); + + if (gnutls_url_is_supported(vhost->perm_config.cert[i]) != 0) { + mslog(s, NULL, LOG_ERR, "Loading a certificate from '%s' is unsupported", vhost->perm_config.cert[i]); return -1; } else { - ret = gnutls_load_file(s->perm_config->cert[i], &data); + ret = gnutls_load_file(vhost->perm_config.cert[i], &data); if (ret < 0) { - mslog(s, NULL, LOG_ERR, "error loading file[%d] '%s'", i, s->perm_config->cert[i]); + mslog(s, NULL, LOG_ERR, "error loading file[%d] '%s'", i, vhost->perm_config.cert[i]); return -1; } @@ -874,23 +898,27 @@ int load_cert_files(main_server_st *s, tls_st *creds) gnutls_free(data.data); } + /* sanity checks on the loaded certificate and key */ + certificate_check(s, vhost->name, &pcert_list[0]); + ret = gnutls_privkey_init(&key); GNUTLS_FATAL_ERR(ret); - /* use use the main pool rather than main, to allow usage of the credentials + /* use use the vhost/config pool rather than main, to allow usage of the credentials * after freeing s. */ - cdata = talloc(s->main_pool, struct key_cb_data); + cdata = talloc_zero(vhost->pool, struct key_cb_data); if (cdata == NULL) { mslog(s, NULL, LOG_ERR, "error allocating memory"); return -1; } cdata->idx = i; + cdata->vhost = vhost->name; - memset(&cdata->sa, 0, sizeof(cdata->sa)); + /* when called here configuration may not be populated, so avoid using it */ cdata->sa.sun_family = AF_UNIX; - strlcpy(cdata->sa.sun_path, s->socket_file, sizeof(cdata->sa.sun_path)); + secmod_socket_file_name(&vhost->perm_config, cdata->sa.sun_path, sizeof(cdata->sa.sun_path)); cdata->sa_len = SUN_LEN(&cdata->sa); /* load the private key */ @@ -906,7 +934,7 @@ int load_cert_files(main_server_st *s, tls_st *creds) #endif GNUTLS_FATAL_ERR(ret); - ret = gnutls_certificate_set_key(creds->xcred, NULL, 0, pcert_list, + ret = gnutls_certificate_set_key(vhost->creds.xcred, NULL, 0, pcert_list, pcert_list_size, key); GNUTLS_FATAL_ERR(ret); } @@ -939,25 +967,26 @@ unsigned need_file_reload(const char *file, time_t last_access) return 0; } -/* reload key files etc. */ -void tls_load_files(main_server_st *s, tls_st *creds) +/* reload key files etc. + * @s may be %NULL, and should be used for mslog() purposes only. + */ +void tls_load_files(main_server_st *s, struct vhost_cfg_st *vhost) { int ret; unsigned i; - static time_t last_access = 0; unsigned need_reload = 0; - if (last_access != 0) { - for (i=0;iperm_config->key_size;i++) { - if (need_file_reload(s->perm_config->cert[i], last_access) != 0) { + if (vhost->params_last_access != 0) { + for (i=0;iperm_config.key_size;i++) { + if (need_file_reload(vhost->perm_config.cert[i], vhost->params_last_access) != 0) { need_reload = 1; break; } } - if (need_file_reload(s->perm_config->ca, last_access) || - need_file_reload(s->config->ocsp_response, last_access) || - need_file_reload(s->perm_config->dh_params_file, last_access)) { + if (need_file_reload(vhost->perm_config.ca, vhost->params_last_access) || + need_file_reload(vhost->perm_config.config->ocsp_response, vhost->params_last_access) || + need_file_reload(vhost->perm_config.dh_params_file, vhost->params_last_access)) { need_reload = 1; } @@ -967,22 +996,22 @@ void tls_load_files(main_server_st *s, tls_st *creds) mslog(s, NULL, LOG_INFO, "reloading server certificates"); } - if (s->perm_config->debug >= DEBUG_TLS) { + if (vhost->perm_config.debug >= DEBUG_TLS) { gnutls_global_set_log_function(tls_log_func); gnutls_global_set_log_level(9); } - last_access = time(0); + vhost->params_last_access = time(0); - if (creds->xcred != NULL) - gnutls_certificate_free_credentials(creds->xcred); + if (vhost->creds.xcred != NULL) + gnutls_certificate_free_credentials(vhost->creds.xcred); - ret = gnutls_certificate_allocate_credentials(&creds->xcred); + ret = gnutls_certificate_allocate_credentials(&vhost->creds.xcred); GNUTLS_FATAL_ERR(ret); - set_dh_params(s, creds); + set_dh_params(s, vhost); - if (s->perm_config->key_size == 0 || s->perm_config->cert_size == 0) { + if (vhost->perm_config.key_size == 0 || vhost->perm_config.cert_size == 0) { mslog(s, NULL, LOG_ERR, "no certificate or key files were specified"); exit(1); } @@ -990,85 +1019,87 @@ void tls_load_files(main_server_st *s, tls_st *creds) /* on reload reduce any checks done */ if (need_reload) { #if GNUTLS_VERSION_NUMBER >= 0x030407 - gnutls_certificate_set_flags(creds->xcred, GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH); + gnutls_certificate_set_flags(vhost->creds.xcred, GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH); #endif - certificate_check(s); } - ret = load_cert_files(s, creds); + ret = load_cert_files(s, vhost); if (ret < 0) { mslog(s, NULL, LOG_ERR, "error loading the certificate or key file"); exit(1); } - if (s->config->cert_req != GNUTLS_CERT_IGNORE) { - if (s->perm_config->ca != NULL) { + if (vhost->perm_config.config->cert_req != GNUTLS_CERT_IGNORE) { + if (vhost->perm_config.ca != NULL) { ret = - gnutls_certificate_set_x509_trust_file(creds->xcred, - s->perm_config->ca, + gnutls_certificate_set_x509_trust_file(vhost->creds.xcred, + vhost->perm_config.ca, GNUTLS_X509_FMT_PEM); if (ret < 0) { mslog(s, NULL, LOG_ERR, "error setting the CA (%s) file", - s->perm_config->ca); + vhost->perm_config.ca); exit(1); } mslog(s, NULL, LOG_INFO, "processed %d CA certificate(s)", ret); } - tls_reload_crl(s, creds, 1); + tls_reload_crl(s, vhost, 1); - gnutls_certificate_set_verify_function(creds->xcred, + gnutls_certificate_set_verify_function(vhost->creds.xcred, verify_certificate_cb); } - tls_reload_ocsp(s, creds); + tls_reload_ocsp(s, vhost); return; } static int ocsp_get_func(gnutls_session_t session, void *ptr, gnutls_datum_t *response) { - tls_st *creds = ptr; + struct vhost_cfg_st *vhost = ptr; - if (ptr == NULL || creds->ocsp_response.size == 0) + if (ptr == NULL || vhost->creds.ocsp_response.size == 0) return GNUTLS_E_NO_CERTIFICATE_STATUS; - response->data = gnutls_malloc(creds->ocsp_response.size); + response->data = gnutls_malloc(vhost->creds.ocsp_response.size); if (response->data == NULL) return GNUTLS_E_NO_CERTIFICATE_STATUS; - memcpy(response->data, creds->ocsp_response.data, creds->ocsp_response.size); - response->size = creds->ocsp_response.size; + memcpy(response->data, vhost->creds.ocsp_response.data, vhost->creds.ocsp_response.size); + response->size = vhost->creds.ocsp_response.size; return 0; } -static void tls_reload_ocsp(main_server_st* s, tls_st *creds) +static void tls_reload_ocsp(main_server_st* s, struct vhost_cfg_st *vhost) { int ret; - gnutls_free(creds->ocsp_response.data); - creds->ocsp_response.data = NULL; + gnutls_free(vhost->creds.ocsp_response.data); + vhost->creds.ocsp_response.data = NULL; - if (s->config->ocsp_response != NULL) { - ret = gnutls_load_file(s->config->ocsp_response, &creds->ocsp_response); + if (vhost->perm_config.config->ocsp_response != NULL) { + ret = gnutls_load_file(vhost->perm_config.config->ocsp_response, &vhost->creds.ocsp_response); if (ret < 0) return; - gnutls_certificate_set_ocsp_status_request_function(creds->xcred, - ocsp_get_func, creds); + gnutls_certificate_set_ocsp_status_request_function(vhost->creds.xcred, + ocsp_get_func, vhost); } else { - gnutls_certificate_set_ocsp_status_request_function(creds->xcred, NULL, 0); + gnutls_certificate_set_ocsp_status_request_function(vhost->creds.xcred, NULL, 0); } } -void tls_load_prio(main_server_st *s, tls_st *creds) +/* + * @s may be %NULL, and should be used for mslog() purposes only. + */ +void tls_load_prio(main_server_st *s, struct vhost_cfg_st *vhost) { -int ret; -const char* perr; + int ret; + const char* perr; - ret = gnutls_priority_init(&creds->cprio, s->config->priorities, &perr); + ret = gnutls_priority_init(&vhost->creds.cprio, vhost->perm_config.config->priorities, &perr); if (ret == GNUTLS_E_PARSING_ERROR) mslog(s, NULL, LOG_ERR, "error in TLS priority string: %s", perr); GNUTLS_FATAL_ERR(ret); @@ -1076,33 +1107,35 @@ const char* perr; return; } -void tls_reload_crl(main_server_st* s, tls_st *creds, unsigned force) +/* + * @s may be %NULL, and should be used for mslog() purposes only. + */ +void tls_reload_crl(main_server_st* s, struct vhost_cfg_st *vhost, unsigned force) { -int ret, saved_ret; -static time_t last_access = 0; -static unsigned crl_type = GNUTLS_X509_FMT_PEM; + int ret, saved_ret; + static unsigned crl_type = GNUTLS_X509_FMT_PEM; if (force) - last_access = 0; + vhost->crl_last_access = 0; - if (s->config->cert_req != GNUTLS_CERT_IGNORE && s->config->crl != NULL) { - if (need_file_reload(s->config->crl, last_access) == 0) { - mslog(s, NULL, LOG_DEBUG, "skipping already loaded CRL: %s", s->config->crl); + if (vhost->perm_config.config->cert_req != GNUTLS_CERT_IGNORE && vhost->perm_config.config->crl != NULL) { + if (need_file_reload(vhost->perm_config.config->crl, vhost->crl_last_access) == 0) { + mslog(s, NULL, LOG_DEBUG, "skipping already loaded CRL: %s", vhost->perm_config.config->crl); return; } - last_access = time(0); + vhost->crl_last_access = time(0); ret = - gnutls_certificate_set_x509_crl_file(creds->xcred, - s->config->crl, + gnutls_certificate_set_x509_crl_file(vhost->creds.xcred, + vhost->perm_config.config->crl, crl_type); if (ret == GNUTLS_E_BASE64_DECODING_ERROR && crl_type == GNUTLS_X509_FMT_PEM) { crl_type = GNUTLS_X509_FMT_DER; saved_ret = ret; ret = - gnutls_certificate_set_x509_crl_file(creds->xcred, - s->config->crl, + gnutls_certificate_set_x509_crl_file(vhost->creds.xcred, + vhost->perm_config.config->crl, crl_type); if (ret < 0) ret = saved_ret; @@ -1110,10 +1143,10 @@ static unsigned crl_type = GNUTLS_X509_FMT_PEM; if (ret < 0) { /* ignore the CRL file when empty */ mslog(s, NULL, LOG_ERR, "error reading the CRL (%s) file: %s", - s->config->crl, gnutls_strerror(ret)); + vhost->perm_config.config->crl, gnutls_strerror(ret)); exit(1); } - mslog(s, NULL, LOG_INFO, "loaded CRL: %s", s->config->crl); + mslog(s, NULL, LOG_INFO, "loaded CRL: %s", vhost->perm_config.config->crl); } } #endif @@ -1130,12 +1163,12 @@ int tls_uncork(gnutls_session_t session) void *calc_sha1_hash(void *pool, char* file, unsigned cert) { -int ret; -gnutls_datum_t data; -uint8_t digest[20]; -char * retval; -gnutls_x509_crt_t crt; -unsigned i; + int ret; + gnutls_datum_t data; + uint8_t digest[20]; + char * retval; + gnutls_x509_crt_t crt; + unsigned i; ret = gnutls_load_file(file, &data); if (ret < 0) { @@ -1148,7 +1181,7 @@ unsigned i; ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_PEM); if (ret == GNUTLS_E_BASE64_DECODING_ERROR) - ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_DER); + ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_DER); GNUTLS_FATAL_ERR(ret); gnutls_free(data.data); @@ -1195,8 +1228,8 @@ size_t tls_get_overhead(gnutls_protocol_t version, gnutls_cipher_algorithm_t cip #if GNUTLS_VERSION_NUMBER >= 0x030207 return gnutls_est_record_overhead_size(version, cipher, mac, GNUTLS_COMP_NULL, 0); #else -unsigned overhead = 0, t; -unsigned block_size; + unsigned overhead = 0, t; + unsigned block_size; block_size = gnutls_cipher_get_block_size(cipher); switch(version) { diff --git a/src/tlslib.h b/src/tlslib.h index b444e64d..c96c6377 100644 --- a/src/tlslib.h +++ b/src/tlslib.h @@ -55,11 +55,14 @@ typedef struct tls_st { gnutls_datum_t ocsp_response; } tls_st; -void tls_reload_crl(struct main_server_st* s, struct tls_st *creds, unsigned force); -void tls_global_init(struct tls_st *creds); -void tls_global_deinit(struct tls_st *creds); -void tls_load_files(struct main_server_st* s, struct tls_st *creds); -void tls_load_prio(struct main_server_st *s, tls_st *creds); +struct vhost_cfg_st; + +void tls_reload_crl(struct main_server_st* s, struct vhost_cfg_st *vhost, unsigned force); +void tls_global_init(void); +void tls_vhost_init(struct vhost_cfg_st *vhost); +void tls_vhost_deinit(struct vhost_cfg_st *vhost); +void tls_load_files(struct main_server_st* s, struct vhost_cfg_st *vhost); +void tls_load_prio(struct main_server_st *s, struct vhost_cfg_st *vhost); size_t tls_get_overhead(gnutls_protocol_t, gnutls_cipher_algorithm_t, gnutls_mac_algorithm_t); @@ -114,6 +117,8 @@ typedef struct char session_data[MAX_SESSION_DATA_SIZE]; unsigned int session_data_size; + + char *vhostname; } tls_cache_st; #define TLS_SESSION_EXPIRATION_TIME(config) ((config)->cookie_timeout) diff --git a/src/tun.c b/src/tun.c index 60756dfe..2af42973 100644 --- a/src/tun.c +++ b/src/tun.c @@ -541,7 +541,7 @@ int open_tun(main_server_st * s, struct proc_st *proc) if (ret < 0) return ret; ret = snprintf(proc->tun_lease.name, sizeof(proc->tun_lease.name), "%s%%d", - s->config->network.name); + GETCONFIG(s)->network.name); if (ret != strlen(proc->tun_lease.name)) { mslog(s, NULL, LOG_ERR, "Truncation error in tun name: %s; adjust 'device' option\n", proc->tun_lease.name); @@ -586,8 +586,8 @@ int open_tun(main_server_st * s, struct proc_st *proc) goto fail; } - if (s->perm_config->uid != -1) { - t = s->perm_config->uid; + if (GETPCONFIG(s)->uid != -1) { + t = GETPCONFIG(s)->uid; ret = ioctl(tunfd, TUNSETOWNER, t); if (ret < 0) { e = errno; @@ -597,8 +597,8 @@ int open_tun(main_server_st * s, struct proc_st *proc) } } #ifdef TUNSETGROUP - if (s->perm_config->gid != -1) { - t = s->perm_config->gid; + if (GETPCONFIG(s)->gid != -1) { + t = GETPCONFIG(s)->gid; ret = ioctl(tunfd, TUNSETGROUP, t); if (ret < 0) { e = errno; diff --git a/src/vhost.h b/src/vhost.h new file mode 100644 index 00000000..32c5b789 --- /dev/null +++ b/src/vhost.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of ocserv. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef VHOST_H +#define VHOST_H + +/* Virtual host entries; common between main and sec-mod */ +#include +#include "tlslib.h" + +#define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN +typedef struct pin_st { + char pin[MAX_PIN_SIZE]; + char srk_pin[MAX_PIN_SIZE]; +} pin_st; + +typedef struct vhost_cfg_st { + struct list_node list; + char *name; + struct perm_cfg_st perm_config; + + tls_st creds; + /* set to non-zero if authentication/accounting is initialized */ + unsigned auth_init; + + /* vhost is pool by itself on current implementation, + * but made explicit to avoid future breakage due to changes */ + void *pool; + + /* sec-mod accessed items */ + pin_st pins; + time_t cert_last_access; /* last reload/access of certs in certs */ + time_t crl_last_access; /* last reload/access of crls in creds */ + time_t params_last_access; /* last reload/access of params in creds */ + struct config_mod_st *config_module; + + gnutls_privkey_t *key; + unsigned key_size; + + /* temporary values used during config loading + */ + char *acct; + char **auth; + size_t auth_size; + char **eauth; + size_t eauth_size; + unsigned expose_iroutes; + unsigned auto_select_group; +#ifdef HAVE_GSSAPI + char **urlfw; + size_t urlfw_size; +#endif +} vhost_cfg_st; + +#define DEFAULT_VHOST_NAME "default" + +/* macros to retrieve the default vhost configuration; they + * are non-null as there is always a configured host. */ +#ifdef __clang_analyzer__ +static volatile void *v = 0xffffffff; + +static inline vhost_cfg_st *default_vhost(void * s) __attribute__((returns_nonnull)); +static inline vhost_cfg_st *default_vhost(void * s) +{ + return v; +} + +static inline struct vhost_cfg_st *GETVHOST(void *s) __attribute__((returns_nonnull)); +static inline struct vhost_cfg_st *GETVHOST(void *s) +{ + return v; +} + +static inline struct cfg_st *GETCONFIG(void *s) __attribute__((returns_nonnull)); +static inline struct cfg_st *GETCONFIG(void *s) +{ + return v; +} + +static inline struct perm_cfg_st* GETPCONFIG(void *s) __attribute__((returns_nonnull)); +static inline struct perm_cfg_st* GETPCONFIG(void *s) +{ + return v; +} +#else +# define GETVHOST(s) default_vhost((s)->vconfig) +# define GETCONFIG(s) GETVHOST(s)->perm_config.config +# define GETPCONFIG(s) (&(GETVHOST(s)->perm_config)) + +inline static vhost_cfg_st *default_vhost(struct list_head *vconfig) +{ + return list_tail(vconfig, struct vhost_cfg_st, list); +} +#endif + +#define VHOSTNAME(vhost) (vhost!=NULL)?(vhost->name?vhost->name:DEFAULT_VHOST_NAME):("unknown") +#define PREFIX_VHOST(vhost) (vhost!=NULL)?(vhost->name?_vhost_prefix(vhost->name):""):("") +#define HAVE_VHOSTS(s) (list_tail(s->vconfig, struct vhost_cfg_st, list) == list_top(s->vconfig, struct vhost_cfg_st, list))?0:1 + +#include + +/* always returns a vhost */ +inline static vhost_cfg_st *find_vhost(struct list_head *vconfig, const char *name) +{ + vhost_cfg_st *vhost = NULL; + if (name == NULL) + return default_vhost(vconfig); + + list_for_each(vconfig, vhost, list) { + if (vhost->name != NULL && c_strcasecmp(vhost->name, name) == 0) + return vhost; + } + + return default_vhost(vconfig); +} + +#endif diff --git a/src/vpn.h b/src/vpn.h index d4812818..42f5e2fb 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -192,12 +192,15 @@ typedef struct auth_struct_st { char *additional; unsigned type; const struct auth_mod_st *amod; + void *auth_ctx; + bool enabled; } auth_struct_st; typedef struct acct_struct_st { const char *name; char *additional; + void *acct_ctx; const struct acct_mod_st *amod; } acct_struct_st; @@ -424,9 +427,8 @@ enum option_types { OPTION_NUMERIC, OPTION_STRING, OPTION_BOOLEAN, OPTION_MULTI_ #include -void reload_cfg_file(void *pool, struct perm_cfg_st* config, unsigned archive); -void clear_old_configs(struct perm_cfg_st* config); -void clear_cfg(struct perm_cfg_st* config); +void reload_cfg_file(void *pool, struct list_head *configs, unsigned archive); +void clear_old_configs(struct list_head *configs); void write_pid_file(void); void remove_pid_file(void); diff --git a/src/worker-auth.c b/src/worker-auth.c index 8540d9f1..feafc3e3 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -116,9 +116,9 @@ int ws_switch_auth_to(struct worker_st *ws, unsigned auth) ws->selected_auth->type & auth) return 1; - for (i=0;iperm_config->auth_methods;i++) { - if (ws->perm_config->auth[i].enabled && (ws->perm_config->auth[i].type & auth) != 0) { - ws->selected_auth = &ws->perm_config->auth[i]; + for (i=0;iauth_methods;i++) { + if (WSPCONFIG(ws)->auth[i].enabled && (WSPCONFIG(ws)->auth[i].type & auth) != 0) { + ws->selected_auth = &WSPCONFIG(ws)->auth[i]; return 1; } } @@ -138,11 +138,11 @@ int ws_switch_auth_to_next(struct worker_st *ws) ws->selected_auth->enabled = 0; - for (i=0;iperm_config->auth_methods;i++) { - if (&ws->perm_config->auth[i] != ws->selected_auth && - ws->perm_config->auth[i].enabled != 0) { + for (i=0;iauth_methods;i++) { + if (&WSPCONFIG(ws)->auth[i] != ws->selected_auth && + WSPCONFIG(ws)->auth[i].enabled != 0) { - ws->selected_auth = &ws->perm_config->auth[i]; + ws->selected_auth = &WSPCONFIG(ws)->auth[i]; return 1; } } @@ -155,11 +155,11 @@ static int append_group_idx(worker_st * ws, str_st *str, unsigned i) const char *name; const char *value; - value = ws->config->group_list[i]; - if (ws->config->friendly_group_list != NULL && ws->config->friendly_group_list[i] != NULL) - name = ws->config->friendly_group_list[i]; + value = WSCONFIG(ws)->group_list[i]; + if (WSCONFIG(ws)->friendly_group_list != NULL && WSCONFIG(ws)->friendly_group_list[i] != NULL) + name = WSCONFIG(ws)->friendly_group_list[i]; else - name = ws->config->group_list[i]; + name = WSCONFIG(ws)->group_list[i]; snprintf(temp, sizeof(temp), "\n", value, name); if (str_append_str(str, temp) < 0) @@ -177,11 +177,11 @@ static int append_group_str(worker_st * ws, str_st *str, const char *group) value = name = group; - if (ws->config->friendly_group_list) { - for (i=0;iconfig->group_list_size;i++) { - if (strcmp(ws->config->group_list[i], group) == 0) { - if (ws->config->friendly_group_list[i] != NULL) - name = ws->config->friendly_group_list[i]; + if (WSCONFIG(ws)->friendly_group_list) { + for (i=0;igroup_list_size;i++) { + if (strcmp(WSCONFIG(ws)->group_list[i], group) == 0) { + if (WSCONFIG(ws)->friendly_group_list[i] != NULL) + name = WSCONFIG(ws)->friendly_group_list[i]; break; } } @@ -241,7 +241,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsig ret = cstp_printf(ws, "Set-Cookie: webvpncontext=%s; Max-Age=%u; Secure\r\n", - context, (unsigned)ws->config->cookie_timeout); + context, (unsigned)WSCONFIG(ws)->cookie_timeout); if (ret < 0) return -1; @@ -315,7 +315,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, unsig } /* send groups */ - if (ws->config->group_list_size > 0 || ws->cert_groups_size > 0) { + if (WSCONFIG(ws)->group_list_size > 0 || ws->cert_groups_size > 0) { ret = str_append_str(&str, "