From aaf72974672e4f2af2053247b63ef6f06bdc80c0 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Tue, 1 Jun 2010 07:12:27 +0000 Subject: Implemented a key/value auth channel from client to server. Version 2.1.1i git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5668 e7ae566f-a301-0410-adde-c780ea21d3b5 --- common.h | 9 ++++- init.c | 3 ++ manage.c | 53 ++++++++++++++++++++++++++ manage.h | 1 + multi.c | 15 ++++++++ options.c | 16 ++++++++ options.h | 4 ++ push.c | 6 +-- route.c | 2 +- route.h | 2 +- ssl.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- ssl.h | 15 ++++++++ syshead.h | 5 +++ version.m4 | 2 +- 14 files changed, 248 insertions(+), 9 deletions(-) diff --git a/common.h b/common.h index b1f5818..5548f7c 100644 --- a/common.h +++ b/common.h @@ -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 diff --git a/init.c b/init.c index a6f6bce..5e45ccd 100644 --- a/init.c +++ b/init.c @@ -2007,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? */ diff --git a/manage.c b/manage.c index 2094723..820621e 100644 --- a/manage.c +++ b/manage.c @@ -2275,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, @@ -2288,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; } diff --git a/manage.h b/manage.h index bb738ac..6a9ccd8 100644 --- a/manage.h +++ b/manage.h @@ -164,6 +164,7 @@ 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, diff --git a/multi.c b/multi.c index 5c80c46..2808c9b 100644 --- a/multi.c +++ b/multi.c @@ -2597,6 +2597,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 @@ -2637,6 +2651,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; diff --git a/options.c b/options.c index 2c823db..bb377f6 100644 --- a/options.c +++ b/options.c @@ -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" @@ -1348,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); @@ -2057,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); @@ -5672,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); diff --git a/options.h b/options.h index a000ccb..240f3bb 100644 --- a/options.h +++ b/options.h @@ -520,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 */ diff --git a/push.c b/push.c index d05cc1d..9ddc900 100644 --- a/push.c +++ b/push.c @@ -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"; diff --git a/route.c b/route.c index 7ab82ff..2635afd 100644 --- a/route.c +++ b/route.c @@ -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) diff --git a/route.h b/route.h index 2def1d2..c5cbb7c 100644 --- a/route.h +++ b/route.h @@ -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 diff --git a/ssl.c b/ssl.c index 3387943..9801b0e 100644 --- a/ssl.c +++ b/ssl.c @@ -2558,6 +2558,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) @@ -3105,6 +3107,14 @@ write_string (struct buffer *buf, const char *str, const int maxlen) return true; } +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) { @@ -3117,6 +3127,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. @@ -3327,6 +3364,73 @@ key_method_1_write (struct buffer *buf, struct tls_session *session) return true; } +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) { @@ -3361,6 +3465,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 @@ -3507,11 +3621,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)) @@ -3532,6 +3648,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 diff --git a/ssl.h b/ssl.h index 8302402..e895bb2 100644 --- a/ssl.h +++ b/ssl.h @@ -431,6 +431,9 @@ struct tls_options bool single_session; #ifdef ENABLE_OCC bool disable_occ; +#endif +#ifdef ENABLE_PUSH_PEER_INFO + bool push_peer_info; #endif int transition_window; int handshake_window; @@ -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 /* diff --git a/syshead.h b/syshead.h index b159bf9..d2ea8de 100644 --- a/syshead.h +++ b/syshead.h @@ -662,4 +662,9 @@ socket_defined (const socket_descriptor_t sd) #define AUTO_USERID 0 #endif +/* + * Do we support pushing peer info? + */ +#define ENABLE_PUSH_PEER_INFO + #endif diff --git a/version.m4 b/version.m4 index 4b2dcb9..8df30d5 100644 --- a/version.m4 +++ b/version.m4 @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1.1h]) +define(PRODUCT_VERSION,[2.1.1i]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) -- cgit v1.2.3