/* * 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 * * 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 (c), &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_options (struct options *o, char **p, int msglevel, struct gc_arena *gc) { const char **argv = make_extended_arg_array (p, gc); char *opt = print_argv (argv, gc, 0); push_option (o, opt, msglevel); } 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] && !strcmp (p[0], "route") && !p[3]) { /* 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] ? p[2] : "255.255.255.255", 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 >= 0 ? ir->netbits : 32)) { 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