6fbf66fa |
/*
* 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.
* |
564a2109 |
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
6fbf66fa |
*
* 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
*/
#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"); |
ba30bc22 |
connection_list_set_no_advance(&c->options); |
6fbf66fa |
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) |
5733ef66 |
{ |
8c7c6be4 |
const char *reason = NULL; |
5733ef66 |
struct buffer buf = *buffer;
if (buf_string_compare_advance (&buf, "AUTH_FAILED,") && BLEN (&buf))
reason = BSTR (&buf); |
8c7c6be4 |
management_auth_failure (management, UP_TYPE_AUTH, reason); |
5733ef66 |
} |
6fbf66fa |
#endif
}
}
|
f25071b6 |
/*
* Act on received restart message from server
*/
void
server_pushed_restart (struct context *c, const struct buffer *buffer)
{
if (c->options.pull)
{
msg (D_STREAM_ERRORS, "Connection reset command was pushed by server");
c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
c->sig->signal_text = "server-pushed-connection-reset";
}
}
|
6fbf66fa |
#if P2MP_SERVER |
f25071b6 |
|
6fbf66fa |
/*
* Send auth failed message from server to client.
*/ |
344ee918 |
void |
5733ef66 |
send_auth_failed (struct context *c, const char *client_reason) |
6fbf66fa |
{ |
5733ef66 |
struct gc_arena gc = gc_new ();
static const char auth_failed[] = "AUTH_FAILED";
size_t len;
|
f25071b6 |
schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM); |
5733ef66 |
len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed); |
aaf72974 |
if (len > PUSH_BUNDLE_SIZE)
len = PUSH_BUNDLE_SIZE; |
5733ef66 |
{
struct buffer buf = alloc_buf_gc (len, &gc);
buf_printf (&buf, auth_failed);
if (client_reason)
buf_printf (&buf, ",%s", client_reason);
send_control_channel_string (c, BSTR (&buf), D_PUSH);
}
gc_free (&gc); |
6fbf66fa |
} |
f25071b6 |
/*
* Send restart message from server to client.
*/
void
send_restart (struct context *c)
{
schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
send_control_channel_string (c, "RESTART", D_PUSH);
}
|
6fbf66fa |
#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, |
3c7f2f55 |
pull_permission_mask (c), |
6fbf66fa |
&option_types_found);
if (status == PUSH_MSG_ERROR)
msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", BSTR (buffer)); |
3eee126e |
else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION) |
6fbf66fa |
{ |
3eee126e |
if (status == PUSH_MSG_REPLY)
do_up (c, true, option_types_found); /* delay bringing tun/tap up until --push parms received from remote */ |
6fbf66fa |
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 |
3eee126e |
|
6fbf66fa |
bool
send_push_reply (struct context *c)
{
struct gc_arena gc = gc_new (); |
aaf72974 |
struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); |
3eee126e |
struct push_entry *e = c->options.push_list.head;
bool multi_push = false;
static char cmd[] = "PUSH_REPLY";
const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
const int safe_cap = BCAP (&buf) - extra; |
cb56a3eb |
bool push_sent = false; |
6fbf66fa |
|
3eee126e |
buf_printf (&buf, cmd); |
6fbf66fa |
|
3eee126e |
while (e)
{
if (e->enable)
{
const int l = strlen (e->option);
if (BLEN (&buf) + l >= safe_cap)
{
buf_printf (&buf, ",push-continuation 2");
{
const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
if (!status)
goto fail; |
cb56a3eb |
push_sent = true; |
3eee126e |
multi_push = true;
buf_reset_len (&buf);
buf_printf (&buf, cmd);
}
}
if (BLEN (&buf) + l >= safe_cap)
{
msg (M_WARN, "--push option is too long");
goto fail;
}
buf_printf (&buf, ",%s", e->option);
}
e = e->next;
} |
6fbf66fa |
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)); |
3eee126e |
if (multi_push)
buf_printf (&buf, ",push-continuation 1"); |
6fbf66fa |
|
3eee126e |
if (BLEN (&buf) > sizeof(cmd)-1)
{
const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
if (!status) |
cb56a3eb |
goto fail;
push_sent = true;
}
/* If nothing have been pushed, send an empty push,
* as the client is expecting a response
*/
if (!push_sent)
{
bool status = false;
buf_reset_len (&buf);
buf_printf (&buf, cmd);
status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
if (!status) |
3eee126e |
goto fail;
} |
6fbf66fa |
gc_free (&gc); |
3eee126e |
return true;
fail:
gc_free (&gc);
return false; |
6fbf66fa |
}
|
3eee126e |
static void
push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) |
6fbf66fa |
{
if (!string_class (opt, CC_ANY, CC_COMMA))
{
msg (msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt);
}
else
{ |
3eee126e |
struct push_entry *e;
ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
e->enable = true;
e->option = opt;
if (o->push_list.head) |
6fbf66fa |
{ |
3eee126e |
ASSERT(o->push_list.tail);
o->push_list.tail->next = e;
o->push_list.tail = e; |
6fbf66fa |
} |
3eee126e |
else |
6fbf66fa |
{ |
3eee126e |
ASSERT(!o->push_list.tail);
o->push_list.head = e;
o->push_list.tail = e; |
6fbf66fa |
} |
3eee126e |
}
}
void
push_option (struct options *o, const char *opt, int msglevel)
{
push_option_ex (o, opt, true, msglevel);
}
void
clone_push_list (struct options *o)
{
if (o->push_list.head)
{
const struct push_entry *e = o->push_list.head;
push_reset (o);
while (e) |
6fbf66fa |
{ |
3eee126e |
push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
e = e->next; |
6fbf66fa |
}
}
}
void |
eadf16a6 |
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 |
6fbf66fa |
push_reset (struct options *o)
{ |
3eee126e |
CLEAR (o->push_list); |
6fbf66fa |
}
#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"))
{ |
344ee918 |
if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED) |
6fbf66fa |
{ |
5733ef66 |
const char *client_reason = tls_client_reason (c->c2.tls_multi);
send_auth_failed (c, client_reason); |
6fbf66fa |
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 == ',')
{ |
3eee126e |
struct buffer buf_orig = buf;
if (!c->c2.did_pre_pull_restore)
{
pre_pull_restore (&c->options);
md5_state_init (&c->c2.pulled_options_state);
c->c2.did_pre_pull_restore = true;
} |
6fbf66fa |
if (apply_push_options (&c->options,
&buf,
permission_mask,
option_types_found,
c->c2.es)) |
3eee126e |
switch (c->options.push_continuation)
{
case 0:
case 1:
md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
md5_state_final (&c->c2.pulled_options_state, &c->c2.pulled_options_digest);
ret = PUSH_MSG_REPLY;
break;
case 2:
md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
ret = PUSH_MSG_CONTINUATION;
break;
} |
6fbf66fa |
}
else if (ch == '\0')
{
ret = PUSH_MSG_REPLY;
}
/* show_settings (&c->options); */
}
return ret;
}
#if P2MP_SERVER |
3eee126e |
|
6fbf66fa |
/*
* Remove iroutes from the push_list.
*/
void
remove_iroutes_from_push_route_list (struct options *o)
{ |
3eee126e |
if (o && o->push_list.head && o->iroutes) |
6fbf66fa |
{
struct gc_arena gc = gc_new (); |
3eee126e |
struct push_entry *e = o->push_list.head; |
6fbf66fa |
/* cycle through the push list */ |
3eee126e |
while (e) |
6fbf66fa |
{
char *p[MAX_PARMS]; |
3eee126e |
bool enable = true; |
6fbf66fa |
/* parse the push item */
CLEAR (p); |
3eee126e |
if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) |
6fbf66fa |
{
/* is the push item a route directive? */ |
c70caa7f |
if (p[0] && !strcmp (p[0], "route") && !p[3]) |
6fbf66fa |
{
/* get route parameters */
bool status1, status2;
const in_addr_t network = getaddr (GETADDR_HOST_ORDER, p[1], 0, &status1, NULL); |
c70caa7f |
const in_addr_t netmask = getaddr (GETADDR_HOST_ORDER, p[2] ? p[2] : "255.255.255.255", 0, &status2, NULL); |
6fbf66fa |
/* 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)
{ |
c70caa7f |
if (network == ir->network && netmask == netbits_to_netmask (ir->netbits >= 0 ? ir->netbits : 32)) |
6fbf66fa |
{ |
3eee126e |
enable = false; |
6fbf66fa |
break;
}
}
}
}
}
/* should we copy the push item? */ |
3eee126e |
e->enable = enable;
if (!enable)
msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option); |
6fbf66fa |
|
3eee126e |
e = e->next;
} |
6fbf66fa |
gc_free (&gc);
}
} |
3eee126e |
|
6fbf66fa |
#endif
#endif |