diff options
Diffstat (limited to 'push.c')
-rw-r--r-- | push.c | 326 |
1 files changed, 326 insertions, 0 deletions
@@ -0,0 +1,326 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef WIN32 +#include "config-win32.h" +#else +#include "config.h" +#endif + +#include "syshead.h" + +#include "push.h" +#include "options.h" +#include "ssl.h" +#include "manage.h" + +#include "memdbg.h" + +#if P2MP + +/* + * Auth username/password + * + * Client received an authentication failed message from server. + * Runs on client. + */ +void +receive_auth_failed (struct context *c, const struct buffer *buffer) +{ + msg (M_VERB0, "AUTH: Received AUTH_FAILED control message"); + if (c->options.pull) + { + switch (auth_retry_get ()) + { + case AR_NONE: + c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ + break; + case AR_INTERACT: + ssl_purge_auth (); + case AR_NOINTERACT: + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ + break; + default: + ASSERT (0); + } + c->sig->signal_text = "auth-failure"; +#ifdef ENABLE_MANAGEMENT + if (management) + management_auth_failure (management, UP_TYPE_AUTH); +#endif + } +} + +#if P2MP_SERVER +/* + * Send auth failed message from server to client. + */ +bool +send_auth_failed (struct context *c) +{ + return send_control_channel_string (c, "AUTH_FAILED", D_PUSH); +} +#endif + +/* + * Push/Pull + */ + +void +incoming_push_message (struct context *c, const struct buffer *buffer) +{ + struct gc_arena gc = gc_new (); + unsigned int option_types_found = 0; + int status; + + msg (D_PUSH, "PUSH: Received control message: '%s'", BSTR (buffer)); + + status = process_incoming_push_msg (c, + buffer, + c->options.pull, + pull_permission_mask (), + &option_types_found); + + if (status == PUSH_MSG_ERROR) + msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", BSTR (buffer)); + else if (status == PUSH_MSG_REPLY) + { + do_up (c, true, option_types_found); /* delay bringing tun/tap up until --push parms received from remote */ + event_timeout_clear (&c->c2.push_request_interval); + } + + gc_free (&gc); +} + +bool +send_push_request (struct context *c) +{ + return send_control_channel_string (c, "PUSH_REQUEST", D_PUSH); +} + +#if P2MP_SERVER +bool +send_push_reply (struct context *c) +{ + struct gc_arena gc = gc_new (); + struct buffer buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc); + bool ret = false; + + buf_printf (&buf, "PUSH_REPLY"); + + if (c->options.push_list && strlen (c->options.push_list->options)) + buf_printf (&buf, ",%s", c->options.push_list->options); + + if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask) + buf_printf (&buf, ",ifconfig %s %s", + print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc), + print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc)); + + if (strlen (BSTR (&buf)) < MAX_PUSH_LIST_LEN) + ret = send_control_channel_string (c, BSTR (&buf), D_PUSH); + else + msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN); + + gc_free (&gc); + return ret; +} + +void +push_option (struct options *o, const char *opt, int msglevel) +{ + int len; + bool first = false; + + if (!string_class (opt, CC_ANY, CC_COMMA)) + { + msg (msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt); + } + else + { + if (!o->push_list) + { + ALLOC_OBJ_CLEAR_GC (o->push_list, struct push_list, &o->gc); + first = true; + } + + len = strlen (o->push_list->options); + if (len + strlen (opt) + 2 >= MAX_PUSH_LIST_LEN) + { + msg (msglevel, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN); + } + else + { + if (!first) + strcat (o->push_list->options, ","); + strcat (o->push_list->options, opt); + } + } +} + +void +push_reset (struct options *o) +{ + o->push_list = NULL; +} +#endif + +int +process_incoming_push_msg (struct context *c, + const struct buffer *buffer, + bool honor_received_options, + unsigned int permission_mask, + unsigned int *option_types_found) +{ + int ret = PUSH_MSG_ERROR; + struct buffer buf = *buffer; + +#if P2MP_SERVER + if (buf_string_compare_advance (&buf, "PUSH_REQUEST")) + { + if (!tls_authenticated (c->c2.tls_multi) || c->c2.context_auth == CAS_FAILED) + { + send_auth_failed (c); + schedule_exit (c, c->options.scheduled_exit_interval); + ret = PUSH_MSG_AUTH_FAILURE; + } + else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED) + { + if (send_push_reply (c)) + ret = PUSH_MSG_REQUEST; + } + else + { + ret = PUSH_MSG_REQUEST_DEFERRED; + } + } + else +#endif + + if (honor_received_options && buf_string_compare_advance (&buf, "PUSH_REPLY")) + { + const uint8_t ch = buf_read_u8 (&buf); + if (ch == ',') + { + pre_pull_restore (&c->options); + c->c2.pulled_options_string = string_alloc (BSTR (&buf), &c->c2.gc); + if (apply_push_options (&c->options, + &buf, + permission_mask, + option_types_found, + c->c2.es)) + ret = PUSH_MSG_REPLY; + } + else if (ch == '\0') + { + ret = PUSH_MSG_REPLY; + } + /* show_settings (&c->options); */ + } + return ret; +} + +#if P2MP_SERVER +/* + * Remove iroutes from the push_list. + */ +void +remove_iroutes_from_push_route_list (struct options *o) +{ + if (o && o->push_list && o->iroutes) + { + struct gc_arena gc = gc_new (); + struct push_list *pl; + struct buffer in, out; + char *line; + bool first = true; + + /* prepare input and output buffers */ + ALLOC_OBJ_CLEAR_GC (pl, struct push_list, &gc); + ALLOC_ARRAY_CLEAR_GC (line, char, MAX_PUSH_LIST_LEN, &gc); + + buf_set_read (&in, (const uint8_t*) o->push_list->options, strlen (o->push_list->options)); + buf_set_write (&out, (uint8_t*) pl->options, sizeof (pl->options)); + + /* cycle through the push list */ + while (buf_parse (&in, ',', line, MAX_PUSH_LIST_LEN)) + { + char *p[MAX_PARMS]; + bool copy = true; + + /* parse the push item */ + CLEAR (p); + if (parse_line (line, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) + { + /* is the push item a route directive? */ + if (p[0] && p[1] && p[2] && !strcmp (p[0], "route")) + { + /* get route parameters */ + bool status1, status2; + const in_addr_t network = getaddr (GETADDR_HOST_ORDER, p[1], 0, &status1, NULL); + const in_addr_t netmask = getaddr (GETADDR_HOST_ORDER, p[2], 0, &status2, NULL); + + /* did route parameters parse correctly? */ + if (status1 && status2) + { + const struct iroute *ir; + + /* does route match an iroute? */ + for (ir = o->iroutes; ir != NULL; ir = ir->next) + { + if (network == ir->network && netmask == netbits_to_netmask (ir->netbits)) + { + copy = false; + break; + } + } + } + } + } + + /* should we copy the push item? */ + if (copy) + { + if (!first) + buf_printf (&out, ","); + buf_printf (&out, "%s", line); + first = false; + } + else + msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", line); + } + +#if 0 + msg (M_INFO, "BEFORE: '%s'", o->push_list->options); + msg (M_INFO, "AFTER: '%s'", pl->options); +#endif + + /* copy new push list back to options */ + *o->push_list = *pl; + + gc_free (&gc); + } +} +#endif + +#endif |