diff options
author | David Sommerseth <dazo@users.sourceforge.net> | 2010-06-04 23:18:03 +0200 |
---|---|---|
committer | David Sommerseth <dazo@users.sourceforge.net> | 2010-06-04 23:18:03 +0200 |
commit | e0ca5fdf8f54c3c264fcb1ddaf0adbde212d858a (patch) | |
tree | 202c201b860f996ccf754d3bfc256658c3694dc4 | |
parent | OCSP_check.sh: new check logic (diff) | |
parent | Merge branch 'svn-BETA21' (diff) | |
download | openvpn-e0ca5fdf8f54c3c264fcb1ddaf0adbde212d858a.tar.xz |
Merge branch 'master' into bugfix2.1
-rw-r--r-- | buffer.h | 6 | ||||
-rw-r--r-- | common.h | 9 | ||||
-rw-r--r-- | forward.c | 17 | ||||
-rw-r--r-- | init.c | 224 | ||||
-rw-r--r-- | manage.c | 171 | ||||
-rw-r--r-- | manage.h | 10 | ||||
-rw-r--r-- | misc.c | 6 | ||||
-rw-r--r-- | misc.h | 5 | ||||
-rw-r--r-- | multi.c | 15 | ||||
-rw-r--r-- | openvpn.c | 2 | ||||
-rw-r--r-- | options.c | 184 | ||||
-rw-r--r-- | options.h | 40 | ||||
-rw-r--r-- | proxy.c | 15 | ||||
-rw-r--r-- | proxy.h | 9 | ||||
-rw-r--r-- | push.c | 6 | ||||
-rw-r--r-- | route.c | 2 | ||||
-rw-r--r-- | route.h | 2 | ||||
-rw-r--r-- | ssl.c | 124 | ||||
-rw-r--r-- | ssl.h | 15 | ||||
-rw-r--r-- | syshead.h | 22 | ||||
-rw-r--r-- | version.m4 | 2 |
21 files changed, 809 insertions, 77 deletions
@@ -724,6 +724,12 @@ void gc_transfer (struct gc_arena *dest, struct gc_arena *src); void x_gc_free (struct gc_arena *a); +static inline bool +gc_defined (struct gc_arena *a) +{ + return a->list != NULL; +} + static inline void gc_init (struct gc_arena *a) { @@ -76,8 +76,15 @@ typedef unsigned long ptr_type; /* * This parameter controls the TLS channel buffer size and the * maximum size of a single TLS message (cleartext). + * This parameter must be >= PUSH_BUNDLE_SIZE */ -#define TLS_CHANNEL_BUF_SIZE 1024 +#define TLS_CHANNEL_BUF_SIZE 2048 + +/* + * This parameter controls the maximum size of a bundle + * of pushed options. + */ +#define PUSH_BUNDLE_SIZE 1024 /* * A sort of pseudo-filename for data provided inline within @@ -687,14 +687,25 @@ read_incoming_link (struct context *c) if (c->options.inetd) { c->sig->signal_received = SIGTERM; + c->sig->signal_text = "connection-reset-inetd"; msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status); } else { - c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */ - msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); +#ifdef ENABLE_OCC + if (event_timeout_defined(&c->c2.explicit_exit_notification_interval)) + { + msg (D_STREAM_ERRORS, "Connection reset during exit notification period, ignoring [%d]", status); + openvpn_sleep(1); + } + else +#endif + { + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */ + c->sig->signal_text = "connection-reset"; + msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); + } } - c->sig->signal_text = "connection-reset"; } perf_pop (); return; @@ -111,6 +111,103 @@ update_options_ce_post (struct options *options) #endif } +#if HTTP_PROXY_FALLBACK + +static bool +ce_http_proxy_fallback_defined(const struct context *c) +{ + const struct connection_list *l = c->options.connection_list; + if (l && l->current == 0) + { + int i; + for (i = 0; i < l->len; ++i) + { + if (l->array[i]->flags & CE_HTTP_PROXY_FALLBACK) + return true; + } + } + return false; +} + +static void +ce_http_proxy_fallback_start(struct context *c, const char *remote_ip_hint) +{ + const struct connection_list *l = c->options.connection_list; + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + ce->http_proxy_options = NULL; + ce->ce_http_proxy_fallback_timestamp = 0; + if (!remote_ip_hint) + remote_ip_hint = ce->remote; + } + } + } + + if (management) + management_http_proxy_fallback_notify(management, "NEED_LATER", remote_ip_hint); +} + +static bool +ce_http_proxy_fallback (struct context *c, volatile const struct connection_entry *ce) +{ + const int proxy_info_expire = 120; /* seconds before proxy info expires */ + + update_time(); + if (management) + { + if (!ce->ce_http_proxy_fallback_timestamp) + { + management_http_proxy_fallback_notify(management, "NEED_NOW", NULL); + while (!ce->ce_http_proxy_fallback_timestamp) + { + management_event_loop_n_seconds (management, 1); + if (IS_SIG (c)) + return false; + } + } + return (now < ce->ce_http_proxy_fallback_timestamp + proxy_info_expire && ce->http_proxy_options); + } + return false; +} + +static bool +management_callback_http_proxy_fallback_cmd (void *arg, const char *server, const char *port, const char *flags) +{ + struct context *c = (struct context *) arg; + const struct connection_list *l = c->options.connection_list; + int ret = false; + struct http_proxy_options *ho = parse_http_proxy_fallback (c, server, port, flags, M_WARN); + + update_time(); + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + if (ho) + { + ce->http_proxy_options = ho; + ret = true; + } + ce->ce_http_proxy_fallback_timestamp = now; + } + } + } + + return ret; +} + +#endif + /* * Initialize and possibly randomize connection list. */ @@ -141,6 +238,30 @@ init_connection_list (struct context *c) #endif } +#if 0 /* fixme -- disable for production */ +static void +show_connection_list (const struct connection_list *l) +{ + int i; + dmsg (M_INFO, "CONNECTION_LIST len=%d current=%d", + l->len, l->current); + for (i = 0; i < l->len; ++i) + { + dmsg (M_INFO, "[%d] %s:%d proto=%s http_proxy=%d", + i, + l->array[i]->remote, + l->array[i]->remote_port, + proto2ascii(l->array[i]->proto, true), + BOOL_CAST(l->array[i]->http_proxy_options)); + } +} +#else +static inline void +show_connection_list (const struct connection_list *l) +{ +} +#endif + /* * Increment to next connection entry */ @@ -151,27 +272,65 @@ next_connection_entry (struct context *c) struct connection_list *l = c->options.connection_list; if (l) { - if (l->no_advance && l->current >= 0) - { - l->no_advance = false; - } - else - { - int i; - if (++l->current >= l->len) - l->current = 0; + bool ce_defined; + struct connection_entry *ce; + int n_cycles = 0; - dmsg (D_CONNECTION_LIST, "CONNECTION_LIST len=%d current=%d", - l->len, l->current); - for (i = 0; i < l->len; ++i) - { - dmsg (D_CONNECTION_LIST, "[%d] %s:%d", - i, - l->array[i]->remote, - l->array[i]->remote_port); - } - } - c->options.ce = *l->array[l->current]; + do { + const char *remote_ip_hint = NULL; + bool advanced = false; + + ce_defined = true; + if (l->no_advance && l->current >= 0) + { + l->no_advance = false; + } + else + { + if (++l->current >= l->len) + { + l->current = 0; + ++l->n_cycles; + if (++n_cycles >= 2) + msg (M_FATAL, "No usable connection profiles are present"); + } + + advanced = true; + show_connection_list(l); + } + + ce = l->array[l->current]; + + if (c->options.remote_ip_hint && !l->n_cycles) + remote_ip_hint = c->options.remote_ip_hint; + +#if HTTP_PROXY_FALLBACK + if (advanced && ce_http_proxy_fallback_defined(c)) + ce_http_proxy_fallback_start(c, remote_ip_hint); + + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + ce_defined = ce_http_proxy_fallback(c, ce); + if (IS_SIG (c)) + break; + } +#endif + + if (ce->flags & CE_DISABLED) + ce_defined = false; + + c->options.ce = *ce; + + if (remote_ip_hint) + c->options.ce.remote = remote_ip_hint; + +#if 0 /* fixme -- disable for production, this code simulates a network where proxy fallback is the only method to reach the OpenVPN server */ + if (!(c->options.ce.flags & CE_HTTP_PROXY_FALLBACK)) + { + c->options.ce.remote = "10.10.0.1"; /* use an unreachable address here */ + } +#endif + } while (!ce_defined); } #endif update_options_ce_post (&c->options); @@ -1848,6 +2007,9 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.renegotiate_packets = options->renegotiate_packets; to.renegotiate_seconds = options->renegotiate_seconds; to.single_session = options->single_session; +#ifdef ENABLE_PUSH_PEER_INFO + to.push_peer_info = options->push_peer_info; +#endif /* should we not xmit any packets until we get an initial response from client? */ @@ -2774,6 +2936,9 @@ init_management_callback_p2p (struct context *c) cb.arg = c; cb.status = management_callback_status_p2p; cb.show_net = management_show_net_callback; +#if HTTP_PROXY_FALLBACK + cb.http_proxy_fallback_cmd = management_callback_http_proxy_fallback_cmd; +#endif management_set_callback (management, &cb); } #endif @@ -2898,6 +3063,17 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int c->sig->signal_text = NULL; c->sig->hard = false; + if (c->mode == CM_P2P) + init_management_callback_p2p (c); + + /* possible sleep or management hold if restart */ + if (c->mode == CM_P2P || c->mode == CM_TOP) + { + do_startup_pause (c); + if (IS_SIG (c)) + goto sig; + } + /* map in current connection entry */ next_connection_entry (c); @@ -2916,14 +3092,6 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int if (c->first_time && options->mlock) do_mlockall (true); - /* possible sleep or management hold if restart */ - if (c->mode == CM_P2P || c->mode == CM_TOP) - { - do_startup_pause (c); - if (IS_SIG (c)) - goto sig; - } - #if P2MP /* get passwords if undefined */ if (auth_retry_get () == AR_INTERACT) @@ -110,6 +110,10 @@ man_help () msg (M_CLIENT, "username type u : Enter username u for a queried OpenVPN username."); msg (M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent."); msg (M_CLIENT, "version : Show current version number."); +#if HTTP_PROXY_FALLBACK + msg (M_CLIENT, "http-proxy-fallback <server> <port> [flags] : Enter dynamic HTTP proxy fallback info."); + msg (M_CLIENT, "http-proxy-fallback-disable : Disable HTTP proxy fallback."); +#endif msg (M_CLIENT, "END"); } @@ -204,12 +208,10 @@ man_update_io_state (struct management *man) } static void -man_output_list_push (struct management *man, const char *str) +man_output_list_push_finalize (struct management *man) { if (management_connected (man)) { - if (str) - buffer_list_push (man->connection.out, (const unsigned char *) str); man_update_io_state (man); if (!man->persist.standalone_disabled) { @@ -220,6 +222,22 @@ man_output_list_push (struct management *man, const char *str) } static void +man_output_list_push_str (struct management *man, const char *str) +{ + if (management_connected (man) && str) + { + buffer_list_push (man->connection.out, (const unsigned char *) str); + } +} + +static void +man_output_list_push (struct management *man, const char *str) +{ + man_output_list_push_str (man, str); + man_output_list_push_finalize (man); +} + +static void man_prompt (struct management *man) { if (man_password_needed (man)) @@ -256,12 +274,13 @@ man_close_socket (struct management *man, const socket_descriptor_t sd) static void virtual_output_callback_func (void *arg, const unsigned int flags, const char *str) { + struct management *man = (struct management *) arg; static int recursive_level = 0; /* GLOBAL */ + bool did_push = false; if (!recursive_level) /* don't allow recursion */ { struct gc_arena gc = gc_new (); - struct management *man = (struct management *) arg; struct log_entry e; const char *out = NULL; @@ -289,13 +308,17 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s | LOG_PRINT_LOG_PREFIX | LOG_PRINT_CRLF, &gc); if (out) - man_output_list_push (man, out); + { + man_output_list_push_str (man, out); + did_push = true; + } if (flags & M_FATAL) { out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc); if (out) { - man_output_list_push (man, out); + man_output_list_push_str (man, out); + did_push = true; man_reset_client_socket (man, true); } } @@ -304,6 +327,9 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s --recursive_level; gc_free (&gc); } + + if (did_push) + man_output_list_push_finalize (man); } /* @@ -998,6 +1024,31 @@ man_need (struct management *man, const char **p, const int n, unsigned int flag return true; } +#if HTTP_PROXY_FALLBACK + +static void +man_http_proxy_fallback (struct management *man, const char *server, const char *port, const char *flags) +{ + if (man->persist.callback.http_proxy_fallback_cmd) + { + const bool status = (*man->persist.callback.http_proxy_fallback_cmd)(man->persist.callback.arg, server, port, flags); + if (status) + { + msg (M_CLIENT, "SUCCESS: proxy-fallback command succeeded"); + } + else + { + msg (M_CLIENT, "ERROR: proxy-fallback command failed"); + } + } + else + { + msg (M_CLIENT, "ERROR: The proxy-fallback command is not supported by the current daemon mode"); + } +} + +#endif + static void man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms) { @@ -1210,6 +1261,17 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch man_pkcs11_id_get (man, atoi(p[1])); } #endif +#if HTTP_PROXY_FALLBACK + else if (streq (p[0], "http-proxy-fallback")) + { + if (man_need (man, p, 2, MN_AT_LEAST)) + man_http_proxy_fallback (man, p[1], p[2], p[3]); + } + else if (streq (p[0], "http-proxy-fallback-disable")) + { + man_http_proxy_fallback (man, NULL, NULL, NULL); + } +#endif #if 1 else if (streq (p[0], "test")) { @@ -2103,7 +2165,7 @@ management_clear_callback (struct management *man) man->persist.standalone_disabled = false; man->persist.hold_release = false; CLEAR (man->persist.callback); - man_output_list_push (man, NULL); /* flush output queue */ + man_output_list_push_finalize (man); /* flush output queue */ } void @@ -2213,6 +2275,58 @@ man_output_extra_env (struct management *man) gc_free (&gc); } +static bool +validate_peer_info_line(const char *line) +{ + uint8_t c; + int state = 0; + while ((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: + if (isprint(c)) + ; + else + return false; + } + } + return (state == 2); +} + +static void +man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac) +{ + char line[256]; + if (man->persist.callback.get_peer_info) + { + const char *peer_info = (*man->persist.callback.get_peer_info) (man->persist.callback.arg, mdac->cid); + if (peer_info) + { + 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)) + { + msg (M_CLIENT, ">CLIENT:ENV,%s", line); + } + else + msg (D_MANAGEMENT, "validation failed on peer_info line received from client"); + } + } + } +} + void management_notify_client_needing_auth (struct management *management, const unsigned int mda_key_id, @@ -2226,6 +2340,7 @@ management_notify_client_needing_auth (struct management *management, mode = "REAUTH"; msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id); man_output_extra_env (management); + man_output_peer_info_env(management, mdac); man_output_env (es, true, management->connection.env_filter_level); mdac->flags |= DAF_INITIAL_AUTH; } @@ -2402,7 +2517,7 @@ management_io (struct management *man) net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT); } } - else if (man->connection.state == MS_CC_WAIT_READ) + else if (man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE) { if (net_events & FD_READ) { @@ -2410,18 +2525,13 @@ management_io (struct management *man) ; net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ); } - } - if (man->connection.state == MS_CC_WAIT_WRITE) - { if (net_events & FD_WRITE) { int status; - /* dmsg (M_INFO, "FD_WRITE set"); */ status = man_write (man); if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK) { - /* dmsg (M_INFO, "FD_WRITE cleared"); */ net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE); } } @@ -2512,7 +2622,7 @@ man_block (struct management *man, volatile int *signal_received, const time_t e if (man_standalone_ok (man)) { - do + while (true) { event_reset (man->connection.es); management_socket_set (man, man->connection.es, NULL, NULL); @@ -2530,15 +2640,18 @@ man_block (struct management *man, volatile int *signal_received, const time_t e status = -1; break; } - /* set SIGINT signal if expiration time exceeded */ - if (expire && now >= expire) + + if (status > 0) + break; + else if (expire && now >= expire) { + /* set SIGINT signal if expiration time exceeded */ status = 0; if (signal_received) *signal_received = SIGINT; break; } - } while (status != 1); + } } return status; } @@ -2615,28 +2728,29 @@ management_event_loop_n_seconds (struct management *man, int sec) { volatile int signal_received = 0; const bool standalone_disabled_save = man->persist.standalone_disabled; - time_t expire; + time_t expire = 0; man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */ /* set expire time */ update_time (); - expire = now + sec; + if (sec) + expire = now + sec; /* if no client connection, wait for one */ man_wait_for_client_connection (man, &signal_received, expire, 0); if (signal_received) return; - /* run command processing event loop until we get our username/password */ - while (true) + /* run command processing event loop */ + do { man_standalone_event_loop (man, &signal_received, expire); if (!signal_received) man_check_for_signals (&signal_received); if (signal_received) return; - } + } while (expire); /* revert state */ man->persist.standalone_disabled = standalone_disabled_save; @@ -3028,6 +3142,19 @@ log_history_ref (const struct log_history *h, const int index) return NULL; } +#if HTTP_PROXY_FALLBACK + +void +management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint) +{ + if (remote_ip_hint) + msg (M_CLIENT, ">PROXY:%s,%s", type, remote_ip_hint); + else + msg (M_CLIENT, ">PROXY:%s", type); +} + +#endif /* HTTP_PROXY_FALLBACK */ + #else static void dummy(void) {} #endif /* ENABLE_MANAGEMENT */ @@ -164,12 +164,16 @@ struct management_callback const char *reason, const char *client_reason, struct buffer_list *cc_config); /* ownership transferred */ + char *(*get_peer_info) (void *arg, const unsigned long cid); #endif #ifdef MANAGEMENT_PF bool (*client_pf) (void *arg, const unsigned long cid, struct buffer_list *pf_config); /* ownership transferred */ #endif +#if HTTP_PROXY_FALLBACK + bool (*http_proxy_fallback_cmd) (void *arg, const char *server, const char *port, const char *flags); +#endif }; /* @@ -502,5 +506,11 @@ management_bytes_server (struct management *man, #endif /* MANAGEMENT_DEF_AUTH */ +#if HTTP_PROXY_FALLBACK + +void management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint); + +#endif /* HTTP_PROXY_FALLBACK */ + #endif #endif @@ -1406,6 +1406,9 @@ get_user_pass (struct user_pass *up, { const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin")); + if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) + msg (M_WARN, "Note: previous '%s' credentials failed", prefix); + #ifdef ENABLE_MANAGEMENT /* * Get username/password from standard input? @@ -1414,6 +1417,9 @@ get_user_pass (struct user_pass *up, && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT))) && management_query_user_pass_enabled (management)) { + if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) + management_auth_failure (management, prefix, "previous auth credentials failed"); + if (!management_query_user_pass (management, up, prefix, flags)) { if ((flags & GET_USER_PASS_NOFATAL) != 0) @@ -263,12 +263,17 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const #define GET_USER_PASS_NEED_OK (1<<3) #define GET_USER_PASS_NOFATAL (1<<4) #define GET_USER_PASS_NEED_STR (1<<5) +#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6) bool get_user_pass (struct user_pass *up, const char *auth_file, const char *prefix, const unsigned int flags); +void fail_user_pass (const char *prefix, + const unsigned int flags, + const char *reason); + void purge_user_pass (struct user_pass *up, const bool force); /* @@ -2605,6 +2605,20 @@ management_client_auth (void *arg, buffer_list_free (cc_config); return ret; } + +static char * +management_get_peer_info (void *arg, const unsigned long cid) +{ + struct multi_context *m = (struct multi_context *) arg; + struct multi_instance *mi = lookup_by_cid (m, cid); + char *ret = NULL; + + if (mi) + ret = tls_get_peer_info (mi->context.c2.tls_multi); + + return ret; +} + #endif #ifdef MANAGEMENT_PF @@ -2645,6 +2659,7 @@ init_management_callback_multi (struct multi_context *m) #ifdef MANAGEMENT_DEF_AUTH cb.kill_by_cid = management_kill_by_cid; cb.client_auth = management_client_auth; + cb.get_peer_info = management_get_peer_info; #endif #ifdef MANAGEMENT_PF cb.client_pf = management_client_pf; @@ -55,8 +55,6 @@ tunnel_point_to_point (struct context *c) if (IS_SIG (c)) return; - init_management_callback_p2p (c); - /* main event loop */ while (true) { @@ -196,6 +196,9 @@ static const char usage_message[] = " Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n" "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n" " the default gateway. Useful when pushing private subnets.\n" +#ifdef ENABLE_PUSH_PEER_INFO + "--push-peer-info : (client only) push client info to server.\n" +#endif "--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" @@ -761,7 +764,9 @@ void uninit_options (struct options *o) { if (o->gc_owned) - gc_free (&o->gc); + { + gc_free (&o->gc); + } } #ifdef ENABLE_DEBUG @@ -1346,6 +1351,9 @@ show_settings (const struct options *o) SHOW_INT (transition_window); SHOW_BOOL (single_session); +#ifdef ENABLE_PUSH_PEER_INFO + SHOW_BOOL (push_peer_info); +#endif SHOW_BOOL (tls_exit); SHOW_STR (tls_auth_file); @@ -1412,6 +1420,137 @@ init_http_options_if_undefined (struct options *o) #endif +#if HTTP_PROXY_FALLBACK + +static struct http_proxy_options * +parse_http_proxy_override (const char *server, + const char *port, + const char *flags, + const int msglevel, + struct gc_arena *gc) +{ + if (server && port) + { + struct http_proxy_options *ho; + const int int_port = atoi(port); + + if (!legal_ipv4_port (int_port)) + { + msg (msglevel, "Bad http-proxy port number: %s", port); + return NULL; + } + + ALLOC_OBJ_CLEAR_GC (ho, struct http_proxy_options, gc); + ho->server = string_alloc(server, gc); + ho->port = int_port; + ho->retry = true; + ho->timeout = 5; + if (flags && !strcmp(flags, "nct")) + ho->auth_retry = PAR_NCT; + else + ho->auth_retry = PAR_ALL; + ho->http_version = "1.0"; + ho->user_agent = "OpenVPN-Autoproxy/1.0"; + return ho; + } + else + return NULL; +} + +struct http_proxy_options * +parse_http_proxy_fallback (struct context *c, + const char *server, + const char *port, + const char *flags, + const int msglevel) +{ + struct gc_arena gc = gc_new (); + struct http_proxy_options *hp = parse_http_proxy_override(server, port, flags, msglevel, &gc); + struct hpo_store *hpos = c->options.hpo_store; + if (!hpos) + { + ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc); + c->options.hpo_store = hpos; + } + hpos->hpo = *hp; + hpos->hpo.server = hpos->server; + strncpynt(hpos->server, hp->server, sizeof(hpos->server)); + gc_free (&gc); + return &hpos->hpo; +} + +static void +http_proxy_warn(const char *name) +{ + msg (M_WARN, "Note: option %s ignored because no TCP-based connection profiles are defined", name); +} + +void +options_postprocess_http_proxy_fallback (struct options *o) +{ + struct connection_list *l = o->connection_list; + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4) + { + if (l->len < CONNECTION_LIST_SIZE) + { + struct connection_entry *newce; + ALLOC_OBJ_GC (newce, struct connection_entry, &o->gc); + *newce = *ce; + newce->flags |= CE_HTTP_PROXY_FALLBACK; + newce->http_proxy_options = NULL; + newce->ce_http_proxy_fallback_timestamp = 0; + l->array[l->len++] = newce; + } + return; + } + } + } + http_proxy_warn("http-proxy-fallback"); +} + +void +options_postprocess_http_proxy_override (struct options *o) +{ + const struct connection_list *l = o->connection_list; + if (l) + { + int i; + bool succeed = false; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4) + { + ce->http_proxy_options = o->http_proxy_override; + succeed = true; + } + } + if (succeed) + { + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_UDPv4) + { + ce->flags |= CE_DISABLED; + } + } + } + else + { + http_proxy_warn("http-proxy-override"); + } + } +} + +#endif + #if ENABLE_CONNECTION static struct connection_list * @@ -1924,6 +2063,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (transition_window); MUST_BE_UNDEF (tls_auth_file); MUST_BE_UNDEF (single_session); +#ifdef ENABLE_PUSH_PEER_INFO + MUST_BE_UNDEF (push_peer_info); +#endif MUST_BE_UNDEF (tls_exit); MUST_BE_UNDEF (crl_file); MUST_BE_UNDEF (key_method); @@ -2095,7 +2237,7 @@ options_postprocess_mutate (struct options *o) * For compatibility with 2.0.x, map multiple --remote options * into connection list (connection lists added in 2.1). */ - if (o->remote_list->len > 1) + if (o->remote_list->len > 1 || o->force_connection_list) { const struct remote_list *rl = o->remote_list; int i; @@ -2112,7 +2254,7 @@ options_postprocess_mutate (struct options *o) *ace = ce; } } - else if (o->remote_list->len == 1) /* one --remote option specfied */ + else if (o->remote_list->len == 1) /* one --remote option specified */ { connection_entry_load_re (&o->ce, o->remote_list->array[0]); } @@ -2126,6 +2268,13 @@ options_postprocess_mutate (struct options *o) int i; for (i = 0; i < o->connection_list->len; ++i) options_postprocess_mutate_ce (o, o->connection_list->array[i]); + +#if HTTP_PROXY_FALLBACK + if (o->http_proxy_override) + options_postprocess_http_proxy_override(o); + else if (o->http_proxy_fallback) + options_postprocess_http_proxy_fallback(o); +#endif } else #endif @@ -3633,11 +3782,29 @@ add_option (struct options *options, } } #endif +#ifdef ENABLE_CONNECTION else if (streq (p[0], "remote-ip-hint") && p[1]) { - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - // fixme + VERIFY_PERMISSION (OPT_P_GENERAL); + options->remote_ip_hint = p[1]; + } +#endif +#if HTTP_PROXY_FALLBACK + else if (streq (p[0], "http-proxy-fallback")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->http_proxy_fallback = true; + options->force_connection_list = true; + } + else if (streq (p[0], "http-proxy-override") && p[1] && p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->http_proxy_override = parse_http_proxy_override(p[1], p[2], p[3], msglevel, &options->gc); + if (!options->http_proxy_override) + goto err; + options->force_connection_list = true; } +#endif else if (streq (p[0], "remote") && p[1]) { struct remote_entry re; @@ -5514,6 +5681,13 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_GENERAL); options->single_session = true; } +#ifdef ENABLE_PUSH_PEER_INFO + else if (streq (p[0], "push-peer-info")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->push_peer_info = true; + } +#endif else if (streq (p[0], "tls-exit")) { VERIFY_PERMISSION (OPT_P_GENERAL); @@ -97,6 +97,14 @@ struct connection_entry int socks_proxy_port; bool socks_proxy_retry; #endif + +# define CE_DISABLED (1<<0) +#if HTTP_PROXY_FALLBACK +# define CE_HTTP_PROXY_FALLBACK (1<<1) + time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */ +#endif + + unsigned int flags; }; struct remote_entry @@ -114,6 +122,7 @@ struct connection_list { int len; int current; + int n_cycles; bool no_advance; struct connection_entry *array[CONNECTION_LIST_SIZE]; }; @@ -126,6 +135,14 @@ struct remote_list #endif +#if HTTP_PROXY_FALLBACK +struct hpo_store +{ + struct http_proxy_options hpo; + char server[80]; +}; +#endif + /* Command line options */ struct options { @@ -162,14 +179,22 @@ struct options struct connection_entry ce; #ifdef ENABLE_CONNECTION + char *remote_ip_hint; struct connection_list *connection_list; struct remote_list *remote_list; + bool force_connection_list; #endif #ifdef GENERAL_PROXY_SUPPORT struct auto_proxy_info *auto_proxy_info; #endif +#if HTTP_PROXY_FALLBACK + bool http_proxy_fallback; + struct http_proxy_options *http_proxy_override; + struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */ +#endif + bool remote_random; const char *ipchange; const char *dev; @@ -495,6 +520,10 @@ struct options /* Allow only one session */ bool single_session; +#ifdef ENABLE_PUSH_PEER_INFO + bool push_peer_info; +#endif + bool tls_exit; #endif /* USE_SSL */ @@ -710,4 +739,15 @@ connection_list_set_no_advance (struct options *o) #endif } +#if HTTP_PROXY_FALLBACK + +struct http_proxy_options * +parse_http_proxy_fallback (struct context *c, + const char *server, + const char *port, + const char *flags, + const int msglevel); + +#endif /* HTTP_PROXY_FALLBACK */ + #endif @@ -224,10 +224,14 @@ get_user_pass_http (struct http_proxy_info *p, const bool force) { if (!static_proxy_user_pass.defined || force) { + unsigned int flags = GET_USER_PASS_MANAGEMENT; + if (p->queried_creds) + flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED; get_user_pass (&static_proxy_user_pass, p->options.auth_file, UP_TYPE_PROXY, - GET_USER_PASS_MANAGEMENT); + flags); + p->queried_creds = true; p->up = static_proxy_user_pass; } } @@ -755,12 +759,12 @@ establish_http_proxy_passthru (struct http_proxy_info *p, realm, password, nonce, - cnonce, + (char *)cnonce, session_key); DigestCalcResponse(session_key, nonce, nonce_count, - cnonce, + (char *)cnonce, qop, http_method, uri, @@ -877,6 +881,8 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; } + /* SUCCESS */ + /* receive line from proxy and discard */ if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) goto error; @@ -888,6 +894,9 @@ establish_http_proxy_passthru (struct http_proxy_info *p, while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received)) ; + /* reset queried_creds so that we don't think that the next creds request is due to an auth error */ + p->queried_creds = false; + #if 0 if (lookahead && BLEN (lookahead)) msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0)); @@ -71,7 +71,7 @@ struct http_proxy_options { # define PAR_NO 0 /* don't support any auth retries */ # define PAR_ALL 1 /* allow all proxy auth protocols */ # define PAR_NCT 2 /* disable cleartext proxy auth protocols */ - bool auth_retry; + int auth_retry; const char *auth_method_string; const char *auth_file; @@ -79,12 +79,19 @@ struct http_proxy_options { const char *user_agent; }; +struct http_proxy_options_simple { + const char *server; + int port; + int auth_retry; +}; + struct http_proxy_info { bool defined; int auth_method; struct http_proxy_options options; struct user_pass up; char *proxy_authenticate; + bool queried_creds; }; struct http_proxy_info *http_proxy_new (const struct http_proxy_options *o, @@ -102,8 +102,8 @@ send_auth_failed (struct context *c, const char *client_reason) schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM); len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed); - if (len > TLS_CHANNEL_BUF_SIZE) - len = TLS_CHANNEL_BUF_SIZE; + if (len > PUSH_BUNDLE_SIZE) + len = PUSH_BUNDLE_SIZE; { struct buffer buf = alloc_buf_gc (len, &gc); @@ -171,7 +171,7 @@ bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); - struct buffer buf = alloc_buf_gc (TLS_CHANNEL_BUF_SIZE, &gc); + struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); struct push_entry *e = c->options.push_list.head; bool multi_push = false; static char cmd[] = "PUSH_REPLY"; @@ -2186,7 +2186,7 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA #endif -#if AUTO_USERID +#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO) #if defined(TARGET_LINUX) @@ -174,7 +174,7 @@ bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask); #define TLA_LOCAL 2 int test_local_addr (const in_addr_t addr); -#if AUTO_USERID +#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO) bool get_default_gateway_mac_addr (unsigned char *macaddr); #endif @@ -2580,6 +2580,8 @@ tls_multi_free (struct tls_multi *multi, bool clear) #ifdef MANAGEMENT_DEF_AUTH man_def_auth_set_client_reason(multi, NULL); + + free (multi->peer_info); #endif if (multi->locked_cn) @@ -3128,6 +3130,14 @@ write_string (struct buffer *buf, const char *str, const int maxlen) } static bool +write_empty_string (struct buffer *buf) +{ + if (!buf_write_u16 (buf, 0)) + return false; + return true; +} + +static bool read_string (struct buffer *buf, char *str, const unsigned int capacity) { const int len = buf_read_u16 (buf); @@ -3139,6 +3149,33 @@ read_string (struct buffer *buf, char *str, const unsigned int capacity) return true; } +static char * +read_string_alloc (struct buffer *buf) +{ + const int len = buf_read_u16 (buf); + char *str; + + if (len < 1) + return NULL; + str = (char *) malloc(len); + check_malloc_return(str); + if (!buf_read (buf, str, len)) + { + free (str); + return NULL; + } + str[len-1] = '\0'; + return str; +} + +void +read_string_discard (struct buffer *buf) +{ + char *data = read_string_alloc(buf); + if (data) + free (data); +} + /* * Authenticate a client using username/password. * Runs on server. @@ -3355,6 +3392,73 @@ key_method_1_write (struct buffer *buf, struct tls_session *session) } static bool +push_peer_info(struct buffer *buf, struct tls_session *session) +{ + struct gc_arena gc = gc_new (); + bool ret = false; + +#ifdef ENABLE_PUSH_PEER_INFO + if (session->opt->push_peer_info) /* write peer info */ + { + struct env_set *es = session->opt->es; + struct env_item *e; + struct buffer out = alloc_buf_gc (512*3, &gc); + + /* push version */ + buf_printf (&out, "IV_VER=%s\n", PACKAGE_VERSION); + + /* push platform */ +#if defined(TARGET_LINUX) + buf_printf (&out, "IV_PLAT=linux\n"); +#elif defined(TARGET_SOLARIS) + buf_printf (&out, "IV_PLAT=solaris\n"); +#elif defined(TARGET_OPENBSD) + buf_printf (&out, "IV_PLAT=openbsd\n"); +#elif defined(TARGET_DARWIN) + buf_printf (&out, "IV_PLAT=mac\n"); +#elif defined(TARGET_NETBSD) + buf_printf (&out, "IV_PLAT=netbsd\n"); +#elif defined(TARGET_FREEBSD) + buf_printf (&out, "IV_PLAT=freebsd\n"); +#elif defined(WIN32) + buf_printf (&out, "IV_PLAT=win\n"); +#endif + + /* push mac addr */ + { + bool get_default_gateway_mac_addr (unsigned char *macaddr); + uint8_t macaddr[6]; + get_default_gateway_mac_addr (macaddr); + buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc)); + } + + /* push env vars that begin with UV_ */ + for (e=es->list; e != NULL; e=e->next) + { + if (e->string) + { + if (!strncmp(e->string, "UV_", 3) && buf_safe(&out, strlen(e->string)+1)) + buf_printf (&out, "%s\n", e->string); + } + } + + if (!write_string(buf, BSTR(&out), -1)) + goto error; + } + else +#endif + { + if (!write_empty_string (buf)) /* no peer info */ + goto error; + } + ret = true; + + error: + gc_free (&gc); + return ret; +} + +static bool key_method_2_write (struct buffer *buf, struct tls_session *session) { ASSERT (session->opt->key_method == 2); @@ -3388,6 +3492,16 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) goto error; purge_user_pass (&auth_user_pass, false); } + else + { + if (!write_empty_string (buf)) /* no username */ + goto error; + if (!write_empty_string (buf)) /* no password */ + goto error; + } + + if (!push_peer_info (buf, session)) + goto error; /* * generate tunnel keys if server @@ -3534,11 +3648,13 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS; bool s2 = true; char *raw_username; + bool username_status, password_status; /* get username/password from plaintext buffer */ ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc); - if (!read_string (buf, up->username, USER_PASS_LEN) - || !read_string (buf, up->password, USER_PASS_LEN)) + username_status = read_string (buf, up->username, USER_PASS_LEN); + password_status = read_string (buf, up->password, USER_PASS_LEN); + if (!username_status || !password_status) { CLEAR (*up); if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL)) @@ -3559,6 +3675,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi /* call plugin(s) and/or script */ #ifdef MANAGEMENT_DEF_AUTH + /* get peer info from control channel */ + free (multi->peer_info); + multi->peer_info = read_string_alloc (buf); + if (man_def_auth == KMDA_DEF) man_def_auth = verify_user_pass_management (session, up, raw_username); #endif @@ -432,6 +432,9 @@ struct tls_options #ifdef ENABLE_OCC bool disable_occ; #endif +#ifdef ENABLE_PUSH_PEER_INFO + bool push_peer_info; +#endif int transition_window; int handshake_window; interval_t packet_timeout; @@ -618,6 +621,12 @@ struct tls_multi */ char *client_reason; + /* + * A multi-line string of general-purpose info received from peer + * over control channel. + */ + char *peer_info; + /* Time of last call to tls_authentication_status */ time_t tas_last; #endif @@ -721,6 +730,12 @@ void tls_deauthenticate (struct tls_multi *multi); #ifdef MANAGEMENT_DEF_AUTH bool tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason); + +static inline char * +tls_get_peer_info(const struct tls_multi *multi) +{ + return multi->peer_info; +} #endif /* @@ -629,6 +629,22 @@ socket_defined (const socket_descriptor_t sd) #define ENABLE_INLINE_FILES 1 /* + * Support "connection" directive + */ +#if ENABLE_INLINE_FILES +#define ENABLE_CONNECTION 1 +#endif + +/* + * Should we include http proxy fallback functionality + */ +#if defined(ENABLE_CONNECTION) && defined(ENABLE_MANAGEMENT) && defined(ENABLE_HTTP_PROXY) +#define HTTP_PROXY_FALLBACK 1 +#else +#define HTTP_PROXY_FALLBACK 0 +#endif + +/* * Reduce sensitivity to system clock instability * and backtracks. */ @@ -651,10 +667,8 @@ socket_defined (const socket_descriptor_t sd) #endif /* - * Support "connection" directive + * Do we support pushing peer info? */ -#if ENABLE_INLINE_FILES -#define ENABLE_CONNECTION 1 -#endif +#define ENABLE_PUSH_PEER_INFO #endif @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1.1g]) +define(PRODUCT_VERSION,[2.1.1i]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) |