From 3a94f66677fe7b5ae3ecd5743462318d322010e4 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Sun, 18 Aug 2013 17:53:34 +0200 Subject: Rebase OpenVPN to -master --HG-- extra : rebase_source : 37025b323cf5e6497cbc92744a32b0b69e397af8 --- openvpn/doc/openvpn.8 | 26 +++++++++++++++++ openvpn/src/openvpn/init.c | 10 ++++--- openvpn/src/openvpn/misc.c | 56 ++++++++++++++++++++++++++++++++++++ openvpn/src/openvpn/misc.h | 7 +++++ openvpn/src/openvpn/multi.c | 52 --------------------------------- openvpn/src/openvpn/multi.h | 3 -- openvpn/src/openvpn/options.c | 60 +++++++++++++++++++++++++++++++++++++-- openvpn/src/openvpn/options.h | 2 ++ openvpn/src/openvpn/socket.c | 1 - openvpn/src/openvpn/socket.h | 2 +- openvpn/src/openvpn/ssl.c | 2 +- openvpn/src/openvpn/ssl_openssl.c | 27 ++++++++++++++---- openvpn/src/openvpn/win32.c | 6 ++-- 13 files changed, 182 insertions(+), 72 deletions(-) (limited to 'openvpn') diff --git a/openvpn/doc/openvpn.8 b/openvpn/doc/openvpn.8 index 868fb841..573d6a80 100644 --- a/openvpn/doc/openvpn.8 +++ b/openvpn/doc/openvpn.8 @@ -1895,6 +1895,13 @@ It is also possible to tag a single directive so as not to trigger a fatal error if the directive isn't recognized. To do this, prepend the following before the directive: .B setenv opt + +Versions prior to OpenVPN 2.3.3 will always ignore options set with the +.B setenv opt +directive. + +See also +.B \-\-ignore-unknown-option .\"********************************************************* .TP .B \-\-setenv-safe name value @@ -1908,6 +1915,25 @@ is a safety precaution to prevent a LD_PRELOAD style attack from a malicious or compromised server. .\"********************************************************* .TP +.B \-\-ignore-unknown-option opt1 opt2 opt3 ... optN +When one of options +.B opt1 ... optN +is encountered in the configuration file the configuration +file parsing does not fail if this OpenVPN version does not +support the option. Multiple +.B \-\-ignore-unknown-option +options can be given to support a larger number of options to ignore. + +This option should be used with caution, as there are good security +reasons for having OpenVPN fail if it detects problems in a +config file. Having said that, there are valid reasons for wanting +new software features to gracefully degrade when encountered by +older software versions. + +.B \-\-ignore-unknown-option +is available since OpenVPN 2.3.3. +.\"********************************************************* +.TP .B \-\-script-security level This directive offers policy-level control over OpenVPN's usage of external programs and scripts. Lower diff --git a/openvpn/src/openvpn/init.c b/openvpn/src/openvpn/init.c index c288e924..8cd136d5 100644 --- a/openvpn/src/openvpn/init.c +++ b/openvpn/src/openvpn/init.c @@ -174,10 +174,12 @@ ce_management_query_proxy (struct context *c) if (management) { gc = gc_new (); - struct buffer out = alloc_buf_gc (256, &gc); - buf_printf (&out, ">PROXY:%u,%s,%s", (l ? l->current : 0) + 1, - (proto_is_udp (ce->proto) ? "UDP" : "TCP"), np (ce->remote)); - management_notify_generic (management, BSTR (&out)); + { + struct buffer out = alloc_buf_gc (256, &gc); + buf_printf (&out, ">PROXY:%u,%s,%s", (l ? l->current : 0) + 1, + (proto_is_udp (ce->proto) ? "UDP" : "TCP"), np (ce->remote)); + management_notify_generic (management, BSTR (&out)); + } ce->flags |= CE_MAN_QUERY_PROXY; while (ce->flags & CE_MAN_QUERY_PROXY) { diff --git a/openvpn/src/openvpn/misc.c b/openvpn/src/openvpn/misc.c index 1120adc4..4688444e 100644 --- a/openvpn/src/openvpn/misc.c +++ b/openvpn/src/openvpn/misc.c @@ -2063,3 +2063,59 @@ compat_flag (unsigned int flag) return (compat_flags & (flag >> 1)); } + +#if P2MP_SERVER + +/* helper to parse peer_info received from multi client, validate + * (this is untrusted data) and put into environment + */ +bool +validate_peer_info_line(char *line) +{ + uint8_t c; + int state = 0; + while (*line) + { + c = *line; + switch (state) + { + case 0: + case 1: + if (c == '=' && state == 1) + state = 2; + else if (isalnum(c) || c == '_') + state = 1; + else + return false; + case 2: + /* after the '=', replace non-printable or shell meta with '_' */ + if (!isprint(c) || isspace(c) || + c == '$' || c == '(' || c == '`' ) + *line = '_'; + } + line++; + } + return (state == 2); +} + +void +output_peer_info_env (struct env_set *es, const char * peer_info) +{ + char line[256]; + struct buffer buf; + buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info)); + while (buf_parse (&buf, '\n', line, sizeof (line))) + { + chomp (line); + if (validate_peer_info_line(line) && + (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) ) + { + msg (M_INFO, "peer info: %s", line); + env_set_add(es, line); + } + else + msg (M_WARN, "validation failed on peer_info line received from client"); + } +} + +#endif /* P2MP_SERVER */ diff --git a/openvpn/src/openvpn/misc.h b/openvpn/src/openvpn/misc.h index 183898e3..41748bd8 100644 --- a/openvpn/src/openvpn/misc.h +++ b/openvpn/src/openvpn/misc.h @@ -369,4 +369,11 @@ void argv_printf_cat (struct argv *a, const char *format, ...) #define COMPAT_NO_NAME_REMAPPING (1<<2) /** compat flag: --compat-names without char remapping */ bool compat_flag (unsigned int flag); +#if P2MP_SERVER +/* helper to parse peer_info received from multi client, validate + * (this is untrusted data) and put into environment */ +bool validate_peer_info_line(char *line); +void output_peer_info_env (struct env_set *es, const char * peer_info); +#endif /* P2MP_SERVER */ + #endif diff --git a/openvpn/src/openvpn/multi.c b/openvpn/src/openvpn/multi.c index 50f398dd..f016b149 100644 --- a/openvpn/src/openvpn/multi.c +++ b/openvpn/src/openvpn/multi.c @@ -1562,58 +1562,6 @@ multi_client_connect_mda (struct multi_context *m, #endif -/* helper to parse peer_info received from multi client, validate - * (this is untrusted data) and put into environment - */ -bool -validate_peer_info_line(char *line) -{ - uint8_t c; - int state = 0; - while (*line) - { - c = *line; - switch (state) - { - case 0: - case 1: - if (c == '=' && state == 1) - state = 2; - else if (isalnum(c) || c == '_') - state = 1; - else - return false; - case 2: - /* after the '=', replace non-printable or shell meta with '_' */ - if (!isprint(c) || isspace(c) || - c == '$' || c == '(' || c == '`' ) - *line = '_'; - } - line++; - } - return (state == 2); -} - -void -multi_output_peer_info_env (struct env_set *es, const char * peer_info) -{ - char line[256]; - struct buffer buf; - buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info)); - while (buf_parse (&buf, '\n', line, sizeof (line))) - { - chomp (line); - if (validate_peer_info_line(line) && - (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) ) - { - msg (M_INFO, "peer info: %s", line); - env_set_add(es, line); - } - else - msg (M_WARN, "validation failed on peer_info line received from client"); - } -} - static void multi_client_connect_setenv (struct multi_context *m, struct multi_instance *mi) diff --git a/openvpn/src/openvpn/multi.h b/openvpn/src/openvpn/multi.h index 7b97b0d2..fc2ffb24 100644 --- a/openvpn/src/openvpn/multi.h +++ b/openvpn/src/openvpn/multi.h @@ -312,9 +312,6 @@ void multi_close_instance_on_signal (struct multi_context *m, struct multi_insta void init_management_callback_multi (struct multi_context *m); void uninit_management_callback_multi (struct multi_context *m); -bool validate_peer_info_line(char *line); -void multi_output_peer_info_env (struct env_set *es, const char * peer_info); - /* * Return true if our output queue is not full */ diff --git a/openvpn/src/openvpn/options.c b/openvpn/src/openvpn/options.c index 48065f99..8b98b45f 100644 --- a/openvpn/src/openvpn/options.c +++ b/openvpn/src/openvpn/options.c @@ -250,6 +250,8 @@ static const char usage_message[] = "--setenv name value : Set a custom environmental variable to pass to script.\n" "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to allow\n" " directives for future OpenVPN versions to be ignored.\n" + "--ignore-unkown-option opt1 opt2 ...: Relax config file syntax. Allow\n" + " these options to be ignored when unknown\n" "--script-security level: Where level can be:\n" " 0 -- strictly no calling of external programs\n" " 1 -- (default) only call built-ins such as ifconfig\n" @@ -4371,6 +4373,48 @@ add_option (struct options *options, uninit_options (&sub); } } + else if (streq (p[0], "ignore-unknown-option") && p[1]) + { + int i; + int j; + int numignored=0; + const char **ignore; + + VERIFY_PERMISSION (OPT_P_GENERAL); + /* Find out how many options to be ignored */ + for (i=1;p[i];i++) + numignored++; + + /* add number of options already ignored */ + for (i=0;options->ignore_unknown_option && + options->ignore_unknown_option[i]; i++) + numignored++; + + /* Allocate array */ + ALLOC_ARRAY_GC (ignore, const char*, numignored+1, &options->gc); + for (i=0;options->ignore_unknown_option && + options->ignore_unknown_option[i]; i++) + ignore[i]=options->ignore_unknown_option[i]; + + options->ignore_unknown_option=ignore; + + for (j=1;p[j];j++) + { + /* Allow the user to specify ignore-unknown-option --opt too */ + if (p[j][0]=='-' && p[j][1]=='-') + options->ignore_unknown_option[i] = (p[j]+2); + else + options->ignore_unknown_option[i] = p[j]; + i++; + } + + options->ignore_unknown_option[i] = NULL; + } + else if (streq (p[0], "remote-ip-hint") && p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->remote_ip_hint = p[1]; + } #if HTTP_PROXY_OVERRIDE else if (streq (p[0], "http-proxy-override") && p[1] && p[2]) { @@ -6815,10 +6859,22 @@ add_option (struct options *options, #endif else { + int i; + int msglevel= msglevel_fc; + /* Check if an option is in --ignore-unknown-option and + set warning level to non fatal */ + for(i=0; options->ignore_unknown_option && options->ignore_unknown_option[i]; i++) + { + if (streq(p[0], options->ignore_unknown_option[i])) + { + msglevel = M_WARN; + break; + } + } if (file) - msg (msglevel_fc, "Unrecognized option or missing parameter(s) in %s:%d: %s (%s)", file, line, p[0], PACKAGE_VERSION); + msg (msglevel, "Unrecognized option or missing parameter(s) in %s:%d: %s (%s)", file, line, p[0], PACKAGE_VERSION); else - msg (msglevel_fc, "Unrecognized option or missing parameter(s): --%s (%s)", p[0], PACKAGE_VERSION); + msg (msglevel, "Unrecognized option or missing parameter(s): --%s (%s)", p[0], PACKAGE_VERSION); } err: gc_free (&gc); diff --git a/openvpn/src/openvpn/options.h b/openvpn/src/openvpn/options.h index cc92da32..86760bbf 100644 --- a/openvpn/src/openvpn/options.h +++ b/openvpn/src/openvpn/options.h @@ -186,6 +186,8 @@ struct options /* enable forward compatibility for post-2.1 features */ bool forward_compatible; + /* list of options that should be ignored even if unkown */ + const char ** ignore_unknown_option; /* persist parms */ bool persist_config; diff --git a/openvpn/src/openvpn/socket.c b/openvpn/src/openvpn/socket.c index 115caaac..ee44ad3c 100644 --- a/openvpn/src/openvpn/socket.c +++ b/openvpn/src/openvpn/socket.c @@ -1191,7 +1191,6 @@ resolve_remote (struct link_socket *sock, unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags); int retry = 0; int status = -1; - if (proto_is_dgram(sock->info.proto)) flags |= GETADDR_DATAGRAM; diff --git a/openvpn/src/openvpn/socket.h b/openvpn/src/openvpn/socket.h index 2b134486..d077a153 100644 --- a/openvpn/src/openvpn/socket.h +++ b/openvpn/src/openvpn/socket.h @@ -1109,7 +1109,7 @@ static inline void link_socket_set_tos (struct link_socket *ls) { if (ls && ls->ptos_defined) - setsockopt (ls->sd, IPPROTO_IP, IP_TOS, &ls->ptos, sizeof (ls->ptos)); + setsockopt (ls->sd, IPPROTO_IP, IP_TOS, (const void *)&ls->ptos, sizeof (ls->ptos)); } #endif diff --git a/openvpn/src/openvpn/ssl.c b/openvpn/src/openvpn/ssl.c index f78cca88..60f5f67f 100644 --- a/openvpn/src/openvpn/ssl.c +++ b/openvpn/src/openvpn/ssl.c @@ -2065,7 +2065,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi free (multi->peer_info); multi->peer_info = read_string_alloc (buf); if ( multi->peer_info ) - multi_output_peer_info_env (session->opt->es, multi->peer_info); + output_peer_info_env (session->opt->es, multi->peer_info); #endif if (verify_user_pass_enabled(session)) diff --git a/openvpn/src/openvpn/ssl_openssl.c b/openvpn/src/openvpn/ssl_openssl.c index 12c725d9..f64177a8 100644 --- a/openvpn/src/openvpn/ssl_openssl.c +++ b/openvpn/src/openvpn/ssl_openssl.c @@ -242,8 +242,7 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) const tls_cipher_name_pair *cipher_pair; - const size_t openssl_ciphers_size = 4096; - char openssl_ciphers[openssl_ciphers_size]; + char openssl_ciphers[4096]; size_t openssl_ciphers_len = 0; openssl_ciphers[0] = '\0'; @@ -282,8 +281,8 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) } // Make sure new cipher name fits in cipher string - if (((openssl_ciphers_size-1) - openssl_ciphers_len) < current_cipher_len) { - msg(M_SSLERR, "Failed to set restricted TLS cipher list, too long (>%zu).", openssl_ciphers_size-1); + if (((sizeof(openssl_ciphers)-1) - openssl_ciphers_len) < current_cipher_len) { + msg(M_SSLERR, "Failed to set restricted TLS cipher list, too long (>%d).", (int)sizeof(openssl_ciphers)-1); } // Concatenate cipher name to OpenSSL cipher string @@ -413,16 +412,34 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, /* Set Certificate Verification chain */ if (load_ca_file) { + /* Add CAs from PKCS12 to the cert store and mark them as trusted. + * They're also used to fill in the chain of intermediate certs as + * necessary. + */ if (ca && sk_X509_num(ca)) { for (i = 0; i < sk_X509_num(ca); i++) { - if (!X509_STORE_add_cert(ctx->ctx->cert_store,sk_X509_value(ca, i))) + if (!X509_STORE_add_cert(ctx->ctx->cert_store,sk_X509_value(ca, i))) msg (M_SSLERR, "Cannot add certificate to certificate chain (X509_STORE_add_cert)"); if (!SSL_CTX_add_client_CA(ctx->ctx, sk_X509_value(ca, i))) msg (M_SSLERR, "Cannot add certificate to client CA list (SSL_CTX_add_client_CA)"); } } + } else { + /* If trusted CA certs were loaded from a PEM file, and we ignore the + * ones in PKCS12, do load PKCS12-provided certs to the client extra + * certs chain just in case they include intermediate CAs needed to + * prove my identity to the other end. This does not make them trusted. + */ + if (ca && sk_X509_num(ca)) + { + for (i = 0; i < sk_X509_num(ca); i++) + { + if (!SSL_CTX_add_extra_chain_cert(ctx->ctx,sk_X509_value(ca, i))) + msg (M_SSLERR, "Cannot add extra certificate to chain (SSL_CTX_add_extra_chain_cert)"); + } + } } return 0; } diff --git a/openvpn/src/openvpn/win32.c b/openvpn/src/openvpn/win32.c index 178e2c35..022eec5c 100644 --- a/openvpn/src/openvpn/win32.c +++ b/openvpn/src/openvpn/win32.c @@ -870,6 +870,9 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i WCHAR *cl = wide_cmd_line (a, &gc); WCHAR *cmd = wide_string (a->argv[0], &gc); + /* this allows console programs to run, and is ignored otherwise */ + DWORD proc_flags = CREATE_NO_WINDOW; + CLEAR (start_info); CLEAR (proc_info); @@ -879,9 +882,6 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i start_info.dwFlags = STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; - /* this allows console programs to run, and is ignored otherwise */ - DWORD proc_flags = CREATE_NO_WINDOW; - if (CreateProcessW (cmd, cl, NULL, NULL, FALSE, proc_flags, env, NULL, &start_info, &proc_info)) { DWORD exit_status = 0; -- cgit v1.2.3