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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
430ce8bd |
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> |
49979459 |
* Copyright (C) 2016-2018 David Sommerseth <davids@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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "buffer.h"
#include "misc.h" |
3cf9dd88 |
#include "base64.h" |
6fbf66fa |
#include "tun.h"
#include "error.h"
#include "otime.h"
#include "plugin.h"
#include "options.h"
#include "manage.h" |
5f31881e |
#include "crypto.h"
#include "route.h" |
72c7b12c |
#include "console.h" |
73b7e698 |
#include "win32.h" |
6fbf66fa |
#include "memdbg.h"
|
51bd56f4 |
#ifdef ENABLE_IPROUTE |
b4073a76 |
const char *iproute_path = IPROUTE_PATH; /* GLOBAL */ |
0aee9ca7 |
#endif
|
6fbf66fa |
/*
* Set standard file descriptors to /dev/null
*/
void |
81d882d5 |
set_std_files_to_null(bool stdin_only) |
6fbf66fa |
{
#if defined(HAVE_DUP) && defined(HAVE_DUP2) |
81d882d5 |
int fd;
if ((fd = open("/dev/null", O_RDWR, 0)) != -1)
{
dup2(fd, 0);
if (!stdin_only)
{
dup2(fd, 1);
dup2(fd, 2);
}
if (fd > 2)
{
close(fd);
} |
6fbf66fa |
}
#endif
}
/*
* dup inetd/xinetd socket descriptor and save
*/
int inetd_socket_descriptor = SOCKET_UNDEFINED; /* GLOBAL */
void |
81d882d5 |
save_inetd_socket_descriptor(void) |
6fbf66fa |
{ |
81d882d5 |
inetd_socket_descriptor = INETD_SOCKET_DESCRIPTOR; |
6fbf66fa |
#if defined(HAVE_DUP) && defined(HAVE_DUP2) |
81d882d5 |
/* use handle passed by inetd/xinetd */
if ((inetd_socket_descriptor = dup(INETD_SOCKET_DESCRIPTOR)) < 0)
{
msg(M_ERR, "INETD_SOCKET_DESCRIPTOR dup(%d) failed", INETD_SOCKET_DESCRIPTOR);
}
set_std_files_to_null(true); |
6fbf66fa |
#endif
}
/* |
7de8f3f3 |
* Prepend a random string to hostname to prevent DNS caching. |
8e9666d5 |
* For example, foo.bar.gov would be modified to <random-chars>.foo.bar.gov. |
7de8f3f3 |
* Of course, this requires explicit support in the DNS server (wildcard). |
8e9666d5 |
*/
const char *
hostname_randomize(const char *hostname, struct gc_arena *gc)
{ |
81d882d5 |
#define n_rnd_bytes 6 |
8e9666d5 |
|
81d882d5 |
uint8_t rnd_bytes[n_rnd_bytes];
const char *rnd_str;
struct buffer hname = alloc_buf_gc(strlen(hostname)+sizeof(rnd_bytes)*2+4, gc); |
8e9666d5 |
|
81d882d5 |
prng_bytes(rnd_bytes, sizeof(rnd_bytes));
rnd_str = format_hex_ex(rnd_bytes, sizeof(rnd_bytes), 40, 0, NULL, gc);
buf_printf(&hname, "%s.%s", rnd_str, hostname);
return BSTR(&hname);
#undef n_rnd_bytes |
8e9666d5 |
}
|
6fbf66fa |
/*
* Get and store a username/password
*/
|
1d89886e |
bool |
81d882d5 |
get_user_pass_cr(struct user_pass *up,
const char *auth_file,
const char *prefix,
const unsigned int flags,
const char *auth_challenge) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
6fbf66fa |
|
81d882d5 |
if (!up->defined) |
6fbf66fa |
{ |
81d882d5 |
bool from_authfile = (auth_file && !streq(auth_file, "stdin"));
bool username_from_stdin = false;
bool password_from_stdin = false;
bool response_from_stdin = true; |
6fbf66fa |
|
81d882d5 |
if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
{
msg(M_WARN, "Note: previous '%s' credentials failed", prefix);
} |
3cf6c932 |
|
6fbf66fa |
#ifdef ENABLE_MANAGEMENT |
81d882d5 |
/*
* Get username/password from management interface?
*/
if (management
&& (!from_authfile && (flags & GET_USER_PASS_MANAGEMENT))
&& management_query_user_pass_enabled(management))
{
const char *sc = NULL;
response_from_stdin = false;
if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
{
management_auth_failure(management, prefix, "previous auth credentials failed");
} |
3cf6c932 |
|
eab3e22f |
#ifdef ENABLE_CLIENT_CR |
81d882d5 |
if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE))
{
sc = auth_challenge;
} |
6fbf66fa |
#endif |
81d882d5 |
if (!management_query_user_pass(management, up, prefix, flags, sc)) |
430ce8bd |
{ |
81d882d5 |
if ((flags & GET_USER_PASS_NOFATAL) != 0)
{
return false;
}
else
{
msg(M_FATAL, "ERROR: could not read %s username/password/ok/string from management interface", prefix);
} |
430ce8bd |
} |
81d882d5 |
}
else
#endif /* ifdef ENABLE_MANAGEMENT */
/*
* Get NEED_OK confirmation from the console
*/
if (flags & GET_USER_PASS_NEED_OK) |
6e9373c8 |
{ |
81d882d5 |
struct buffer user_prompt = alloc_buf_gc(128, &gc); |
6e9373c8 |
|
81d882d5 |
buf_printf(&user_prompt, "NEED-OK|%s|%s:", prefix, up->username);
if (!query_user_SINGLE(BSTR(&user_prompt), BLEN(&user_prompt),
up->password, USER_PASS_LEN, false))
{
msg(M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix);
}
if (!strlen(up->password))
{
strcpy(up->password, "ok");
}
}
else if (flags & GET_USER_PASS_INLINE_CREDS)
{
struct buffer buf;
buf_set_read(&buf, (uint8_t *) auth_file, strlen(auth_file) + 1);
if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
{
buf_parse(&buf, '\n', up->username, USER_PASS_LEN);
}
buf_parse(&buf, '\n', up->password, USER_PASS_LEN);
}
/*
* Read from auth file unless this is a dynamic challenge request.
*/
else if (from_authfile && !(flags & GET_USER_PASS_DYNAMIC_CHALLENGE))
{
/*
* Try to get username/password from a file.
*/
FILE *fp;
char password_buf[USER_PASS_LEN] = { '\0' };
fp = platform_fopen(auth_file, "r");
if (!fp)
{
msg(M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file);
} |
6e9373c8 |
|
81d882d5 |
if ((flags & GET_USER_PASS_PASSWORD_ONLY) == 0) |
6e9373c8 |
{ |
81d882d5 |
/* Read username first */
if (fgets(up->username, USER_PASS_LEN, fp) == NULL)
{
msg(M_FATAL, "Error reading username from %s authfile: %s",
prefix,
auth_file);
}
}
chomp(up->username);
if (fgets(password_buf, USER_PASS_LEN, fp) != NULL) |
6e9373c8 |
{ |
81d882d5 |
chomp(password_buf); |
6e9373c8 |
}
|
81d882d5 |
if (flags & GET_USER_PASS_PASSWORD_ONLY && !password_buf[0])
{
msg(M_FATAL, "Error reading password from %s authfile: %s", prefix, auth_file);
} |
6e9373c8 |
|
81d882d5 |
if (password_buf[0])
{
strncpy(up->password, password_buf, USER_PASS_LEN);
}
else
{
password_from_stdin = 1;
} |
6e9373c8 |
|
81d882d5 |
fclose(fp); |
6e9373c8 |
|
81d882d5 |
if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen(up->username) == 0)
{
msg(M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file);
} |
6e9373c8 |
} |
81d882d5 |
else |
cdd69bb7 |
{ |
81d882d5 |
username_from_stdin = true;
password_from_stdin = true; |
cdd69bb7 |
} |
6e9373c8 |
|
81d882d5 |
/*
* Get username/password from standard input?
*/
if (username_from_stdin || password_from_stdin || response_from_stdin)
{ |
3cf9dd88 |
#ifdef ENABLE_CLIENT_CR |
81d882d5 |
if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE) && response_from_stdin)
{
struct auth_challenge_info *ac = get_auth_challenge(auth_challenge, &gc);
if (ac)
{
char *response = (char *) gc_malloc(USER_PASS_LEN, false, &gc);
struct buffer packed_resp, challenge;
challenge = alloc_buf_gc(14+strlen(ac->challenge_text), &gc);
buf_printf(&challenge, "CHALLENGE: %s", ac->challenge_text);
buf_set_write(&packed_resp, (uint8_t *)up->password, USER_PASS_LEN);
if (!query_user_SINGLE(BSTR(&challenge), BLEN(&challenge),
response, USER_PASS_LEN, BOOL_CAST(ac->flags&CR_ECHO)))
{
msg(M_FATAL, "ERROR: could not read challenge response from stdin");
}
strncpynt(up->username, ac->user, USER_PASS_LEN);
buf_printf(&packed_resp, "CRV1::%s::%s", ac->state_id, response);
}
else
{
msg(M_FATAL, "ERROR: received malformed challenge request from server");
}
}
else
#endif /* ifdef ENABLE_CLIENT_CR */
{
struct buffer user_prompt = alloc_buf_gc(128, &gc);
struct buffer pass_prompt = alloc_buf_gc(128, &gc); |
6fbf66fa |
|
81d882d5 |
query_user_clear();
buf_printf(&user_prompt, "Enter %s Username:", prefix);
buf_printf(&pass_prompt, "Enter %s Password:", prefix); |
3cf9dd88 |
|
81d882d5 |
if (username_from_stdin && !(flags & GET_USER_PASS_PASSWORD_ONLY))
{
query_user_add(BSTR(&user_prompt), BLEN(&user_prompt),
up->username, USER_PASS_LEN, true);
} |
430ce8bd |
|
81d882d5 |
if (password_from_stdin) |
430ce8bd |
{ |
81d882d5 |
query_user_add(BSTR(&pass_prompt), BLEN(&pass_prompt),
up->password, USER_PASS_LEN, false); |
430ce8bd |
}
|
81d882d5 |
if (!query_user_exec() )
{
msg(M_FATAL, "ERROR: Failed retrieving username or password");
} |
430ce8bd |
|
81d882d5 |
if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
{
if (strlen(up->username) == 0)
{
msg(M_FATAL, "ERROR: %s username is empty", prefix);
}
} |
3cf9dd88 |
|
eab3e22f |
#ifdef ENABLE_CLIENT_CR |
81d882d5 |
if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE) && response_from_stdin)
{
char *response = (char *) gc_malloc(USER_PASS_LEN, false, &gc);
struct buffer packed_resp, challenge;
char *pw64 = NULL, *resp64 = NULL;
challenge = alloc_buf_gc(14+strlen(auth_challenge), &gc);
buf_printf(&challenge, "CHALLENGE: %s", auth_challenge);
if (!query_user_SINGLE(BSTR(&challenge), BLEN(&challenge),
response, USER_PASS_LEN,
BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO)))
{
msg(M_FATAL, "ERROR: could not retrieve static challenge response");
}
if (openvpn_base64_encode(up->password, strlen(up->password), &pw64) == -1
|| openvpn_base64_encode(response, strlen(response), &resp64) == -1)
{
msg(M_FATAL, "ERROR: could not base64-encode password/static_response");
}
buf_set_write(&packed_resp, (uint8_t *)up->password, USER_PASS_LEN);
buf_printf(&packed_resp, "SCRV1:%s:%s", pw64, resp64);
string_clear(pw64);
free(pw64);
string_clear(resp64);
free(resp64);
}
#endif /* ifdef ENABLE_CLIENT_CR */
}
} |
6fbf66fa |
|
81d882d5 |
string_mod(up->username, CC_PRINT, CC_CRLF, 0);
string_mod(up->password, CC_PRINT, CC_CRLF, 0); |
6fbf66fa |
|
81d882d5 |
up->defined = true; |
6fbf66fa |
}
#if 0 |
81d882d5 |
msg(M_INFO, "GET_USER_PASS %s u='%s' p='%s'", prefix, up->username, up->password); |
6fbf66fa |
#endif
|
81d882d5 |
gc_free(&gc); |
1d89886e |
|
81d882d5 |
return true; |
6fbf66fa |
}
|
3cf9dd88 |
#ifdef ENABLE_CLIENT_CR
/* |
eab3e22f |
* See management/management-notes.txt for more info on the
* the dynamic challenge/response protocol implemented here. |
3cf9dd88 |
*/
struct auth_challenge_info * |
81d882d5 |
get_auth_challenge(const char *auth_challenge, struct gc_arena *gc)
{
if (auth_challenge)
{
struct auth_challenge_info *ac;
const int len = strlen(auth_challenge);
char *work = (char *) gc_malloc(len+1, false, gc);
char *cp;
struct buffer b;
buf_set_read(&b, (const uint8_t *)auth_challenge, len);
ALLOC_OBJ_CLEAR_GC(ac, struct auth_challenge_info, gc);
/* parse prefix */
if (!buf_parse(&b, ':', work, len))
{
return NULL;
}
if (strcmp(work, "CRV1"))
{
return NULL;
}
/* parse flags */
if (!buf_parse(&b, ':', work, len))
{
return NULL;
}
for (cp = work; *cp != '\0'; ++cp)
{
const char c = *cp;
if (c == 'E')
{
ac->flags |= CR_ECHO;
}
else if (c == 'R')
{
ac->flags |= CR_RESPONSE;
}
}
/* parse state ID */
if (!buf_parse(&b, ':', work, len))
{
return NULL;
}
ac->state_id = string_alloc(work, gc);
/* parse user name */
if (!buf_parse(&b, ':', work, len))
{
return NULL;
}
ac->user = (char *) gc_malloc(strlen(work)+1, true, gc);
openvpn_base64_decode(work, (void *)ac->user, -1);
/* parse challenge text */
ac->challenge_text = string_alloc(BSTR(&b), gc);
return ac;
}
else
{
return NULL;
} |
3cf9dd88 |
}
|
81d882d5 |
#endif /* ifdef ENABLE_CLIENT_CR */ |
3cf9dd88 |
|
5f31881e |
#if AUTO_USERID
void |
81d882d5 |
get_user_pass_auto_userid(struct user_pass *up, const char *tag) |
5f31881e |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
struct buffer buf;
uint8_t macaddr[6];
static uint8_t digest [MD5_DIGEST_LENGTH];
static const uint8_t hashprefix[] = "AUTO_USERID_DIGEST"; |
5f31881e |
|
81d882d5 |
const md_kt_t *md5_kt = md_kt_get("MD5"); |
c481ef00 |
md_ctx_t *ctx; |
d5f44617 |
|
81d882d5 |
CLEAR(*up);
buf_set_write(&buf, (uint8_t *)up->username, USER_PASS_LEN);
buf_printf(&buf, "%s", TARGET_PREFIX);
if (get_default_gateway_mac_addr(macaddr))
{
dmsg(D_AUTO_USERID, "GUPAU: macaddr=%s", format_hex_ex(macaddr, sizeof(macaddr), 0, 1, ":", &gc)); |
c481ef00 |
ctx = md_ctx_new();
md_ctx_init(ctx, md5_kt);
md_ctx_update(ctx, hashprefix, sizeof(hashprefix) - 1);
md_ctx_update(ctx, macaddr, sizeof(macaddr));
md_ctx_final(ctx, digest);
md_ctx_cleanup(ctx);
md_ctx_free(ctx); |
81d882d5 |
buf_printf(&buf, "%s", format_hex_ex(digest, sizeof(digest), 0, 256, " ", &gc));
}
else |
5f31881e |
{ |
81d882d5 |
buf_printf(&buf, "UNKNOWN"); |
5f31881e |
} |
81d882d5 |
if (tag && strcmp(tag, "stdin")) |
5f31881e |
{ |
81d882d5 |
buf_printf(&buf, "-%s", tag); |
5f31881e |
} |
81d882d5 |
up->defined = true;
gc_free(&gc); |
5f31881e |
|
81d882d5 |
dmsg(D_AUTO_USERID, "GUPAU: AUTO_USERID: '%s'", up->username); |
5f31881e |
}
|
81d882d5 |
#endif /* if AUTO_USERID */ |
5f31881e |
|
6fbf66fa |
void |
81d882d5 |
purge_user_pass(struct user_pass *up, const bool force) |
6fbf66fa |
{ |
81d882d5 |
const bool nocache = up->nocache;
static bool warn_shown = false;
if (nocache || force) |
6fbf66fa |
{ |
81d882d5 |
secure_memzero(up, sizeof(*up));
up->nocache = nocache; |
6fbf66fa |
} |
57116536 |
/*
* don't show warning if the pass has been replaced by a token: this is an
* artificial "auth-nocache"
*/
else if (!warn_shown && (!up->tokenized)) |
70899be8 |
{ |
81d882d5 |
msg(M_WARN, "WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this");
warn_shown = true; |
70899be8 |
} |
6fbf66fa |
}
|
0db046f2 |
void |
81d882d5 |
set_auth_token(struct user_pass *up, const char *token) |
0db046f2 |
{ |
81d882d5 |
if (token && strlen(token) && up && up->defined && !up->nocache) |
0db046f2 |
{ |
81d882d5 |
CLEAR(up->password);
strncpynt(up->password, token, USER_PASS_LEN); |
57116536 |
up->tokenized = true; |
0db046f2 |
}
}
|
6fbf66fa |
/*
* Process string received by untrusted peer before
* printing to console or log file.
*
* Assumes that string has been null terminated.
*/
const char * |
81d882d5 |
safe_print(const char *str, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
return string_mod_const(str, CC_PRINT, CC_CRLF, '.', gc); |
6fbf66fa |
}
const char ** |
81d882d5 |
make_arg_array(const char *first, const char *parms, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
char **ret = NULL;
int base = 0;
const int max_parms = MAX_PARMS + 2;
int n = 0; |
6fbf66fa |
|
81d882d5 |
/* alloc return array */
ALLOC_ARRAY_CLEAR_GC(ret, char *, max_parms, gc); |
6fbf66fa |
|
81d882d5 |
/* process first parameter, if provided */
if (first) |
6fbf66fa |
{ |
81d882d5 |
ret[base++] = string_alloc(first, gc); |
6fbf66fa |
}
|
81d882d5 |
if (parms) |
6fbf66fa |
{ |
81d882d5 |
n = parse_line(parms, &ret[base], max_parms - base - 1, "make_arg_array", 0, M_WARN, gc);
ASSERT(n >= 0 && n + base + 1 <= max_parms); |
6fbf66fa |
} |
81d882d5 |
ret[base + n] = NULL; |
6fbf66fa |
|
81d882d5 |
return (const char **)ret; |
6fbf66fa |
}
|
eadf16a6 |
static const char ** |
81d882d5 |
make_inline_array(const char *str, struct gc_arena *gc) |
eadf16a6 |
{ |
81d882d5 |
char line[OPTION_LINE_SIZE];
struct buffer buf;
int len = 0;
char **ret = NULL;
int i = 0; |
eadf16a6 |
|
81d882d5 |
buf_set_read(&buf, (const uint8_t *) str, strlen(str));
while (buf_parse(&buf, '\n', line, sizeof(line))) |
4cd4899e |
{ |
81d882d5 |
++len; |
4cd4899e |
} |
eadf16a6 |
|
81d882d5 |
/* alloc return array */
ALLOC_ARRAY_CLEAR_GC(ret, char *, len + 1, gc); |
eadf16a6 |
|
81d882d5 |
buf_set_read(&buf, (const uint8_t *) str, strlen(str));
while (buf_parse(&buf, '\n', line, sizeof(line))) |
eadf16a6 |
{ |
81d882d5 |
chomp(line);
ASSERT(i < len);
ret[i] = string_alloc(skip_leading_whitespace(line), gc);
++i;
}
ASSERT(i <= len);
ret[i] = NULL;
return (const char **)ret; |
eadf16a6 |
}
static const char ** |
81d882d5 |
make_arg_copy(char **p, struct gc_arena *gc) |
eadf16a6 |
{ |
81d882d5 |
char **ret = NULL;
const int len = string_array_len((const char **)p);
const int max_parms = len + 1;
int i; |
eadf16a6 |
|
81d882d5 |
/* alloc return array */
ALLOC_ARRAY_CLEAR_GC(ret, char *, max_parms, gc); |
eadf16a6 |
|
81d882d5 |
for (i = 0; i < len; ++i) |
4cd4899e |
{ |
81d882d5 |
ret[i] = p[i]; |
4cd4899e |
} |
eadf16a6 |
|
81d882d5 |
return (const char **)ret; |
eadf16a6 |
}
const char ** |
81d882d5 |
make_extended_arg_array(char **p, struct gc_arena *gc)
{
const int argc = string_array_len((const char **)p);
if (!strcmp(p[0], INLINE_FILE_TAG) && argc == 2)
{
return make_inline_array(p[1], gc);
}
else if (argc == 0)
{
return make_arg_array(NULL, NULL, gc);
}
else if (argc == 1)
{
return make_arg_array(p[0], NULL, gc);
}
else if (argc == 2)
{
return make_arg_array(p[0], p[1], gc);
}
else
{
return make_arg_copy(p, gc);
} |
eadf16a6 |
}
|
c67d59cd |
/* |
a296f99b |
* Remove security-sensitive strings from control message
* so that they will not be output to log file. |
0db046f2 |
*/
const char * |
a296f99b |
sanitize_control_message(const char *src, struct gc_arena *gc) |
0db046f2 |
{ |
81d882d5 |
char *ret = gc_malloc(strlen(src)+1, false, gc);
char *dest = ret;
bool redact = false;
int skip = 0;
for (;; )
{
const char c = *src;
if (c == '\0')
{
break;
}
if (c == 'S' && !strncmp(src, "SESS_ID_", 8))
{
skip = 7;
redact = true;
}
else if (c == 'e' && !strncmp(src, "echo ", 5))
{
skip = 4;
redact = true;
}
else if (!check_debug_level(D_SHOW_KEYS)
&& (c == 'a' && !strncmp(src, "auth-token ", 11)))
{
/* Unless --verb is 7 or higher (D_SHOW_KEYS), hide
* the auth-token value coming in the src string
*/
skip = 10;
redact = true;
}
if (c == ',') /* end of redacted item? */
{
skip = 0;
redact = false;
}
if (redact)
{
if (skip > 0)
{
--skip;
*dest++ = c;
}
}
else
{
*dest++ = c;
}
++src;
}
*dest = '\0';
return ret; |
0db046f2 |
} |
e7412ca3 |
/**
* Will set or query for a global compat flag. To modify the compat flags
* the COMPAT_FLAG_SET must be bitwise ORed together with the flag to set.
* If no "operator" flag is given it defaults to COMPAT_FLAG_QUERY,
* which returns the flag state.
*
* @param flag Flag to be set/queried for bitwise ORed with the operator flag
* @return Returns 0 if the flag is not set, otherwise the 'flag' value is returned
*/
bool |
81d882d5 |
compat_flag(unsigned int flag) |
e7412ca3 |
{ |
81d882d5 |
static unsigned int compat_flags = 0; |
e7412ca3 |
|
81d882d5 |
if (flag & COMPAT_FLAG_SET)
{
compat_flags |= (flag >> 1);
} |
e7412ca3 |
|
81d882d5 |
return (compat_flags & (flag >> 1)); |
e7412ca3 |
} |
46e02127 |
#if P2MP_SERVER
/* helper to parse peer_info received from multi client, validate
* (this is untrusted data) and put into environment
*/
bool
validate_peer_info_line(char *line)
{ |
81d882d5 |
uint8_t c;
int state = 0;
while (*line)
{
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:
/* after the '=', replace non-printable or shell meta with '_' */
if (!isprint(c) || isspace(c)
|| c == '$' || c == '(' || c == '`')
{
*line = '_';
}
}
line++;
}
return (state == 2); |
46e02127 |
}
void |
81d882d5 |
output_peer_info_env(struct env_set *es, const char *peer_info)
{
char line[256];
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)
&& (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) )
{
msg(M_INFO, "peer info: %s", line);
env_set_add(es, line);
}
else
{
msg(M_WARN, "validation failed on peer_info line received from client");
} |
46e02127 |
}
}
#endif /* P2MP_SERVER */ |