Skip to content

Commit 3a66045

Browse files
lstipakovcron2
authored andcommitted
Validate DNS parameters
This adds validation of following DNS options: --dns search-domains --dns server N resolve-domains --dns server N sni --dhcp-option DOMAIN --dhcp-option ADAPTER_DOMAIN_SUFFIX --dhcp-option DOMAIN-SEARCH On Linux (and similar platforms), those options are written to a tmp file, which is later sourced by a script running as root. Since options are controlled by the server, it is possible for a malicious server to execute script injection attack by pushing something like --dns search-domains x;id in which case "id" command will be executed as a root. On Windows, the value of DOMAIN/ADAPTER_DOMAIN_SUFFIX is passed to a powershell script. A malicious server could push: --dhcp-option DOMAIN a';Restart-Computer' and if openvpn is not using DHCP (this is the default, with dco-win driver) and running without interactive service, that powershell command will be executed. Validation is performed in a way that value only contains following symbols: [A-Za-z0-9.-_\x80-\0xff] Reported-By: Stanislav Fort <disclosure@aisle.com> CVE: 2025-10680 Change-Id: I09209ccd785cc368b2fcf467a3d211fbd41005c6 Signed-off-by: Lev Stipakov <lev@openvpn.net> Acked-by: Gert Doering <gert@greenie.muc.de> Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1213 Message-Id: <20250924201601.25304-1-gert@greenie.muc.de> URL: https://sourceforge.net/p/openvpn/mailman/message/59238367/ Signed-off-by: Gert Doering <gert@greenie.muc.de>
1 parent da1d006 commit 3a66045

File tree

5 files changed

+89
-7
lines changed

5 files changed

+89
-7
lines changed

src/openvpn/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ openvpn_SOURCES = \
6161
dco_win.c dco_win.h \
6262
dhcp.c dhcp.h \
6363
dns.c dns.h \
64+
domain_helper.h \
6465
env_set.c env_set.h \
6566
errlevel.h \
6667
error.c error.h \

src/openvpn/dns.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "socket_util.h"
3131
#include "options.h"
3232
#include "run_command.h"
33+
#include "domain_helper.h"
3334

3435
#ifdef _WIN32
3536
#include "win32.h"
@@ -143,7 +144,7 @@ dns_server_addr_parse(struct dns_server *server, const char *addr)
143144
return true;
144145
}
145146

146-
void
147+
bool
147148
dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_arena *gc)
148149
{
149150
/* Fast forward to the end of the list */
@@ -155,11 +156,19 @@ dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_aren
155156
/* Append all domains to the end of the list */
156157
while (*domains)
157158
{
159+
char *domain = *domains++;
160+
if (!validate_domain(domain))
161+
{
162+
return false;
163+
}
164+
158165
ALLOC_OBJ_CLEAR_GC(*entry, struct dns_domain, gc);
159166
struct dns_domain *new = *entry;
160-
new->name = *domains++;
167+
new->name = domain;
161168
entry = &new->next;
162169
}
170+
171+
return true;
163172
}
164173

165174
bool

src/openvpn/dns.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,14 @@ bool dns_server_priority_parse(long *priority, const char *str, bool pulled);
141141
struct dns_server *dns_server_get(struct dns_server **entry, long priority, struct gc_arena *gc);
142142

143143
/**
144-
* Appends DNS domain parameters to a linked list.
144+
* Appends safe DNS domain parameters to a linked list.
145145
*
146146
* @param entry Address of the first list entry pointer
147147
* @param domains Address of the first domain parameter
148148
* @param gc The gc the new list items should be allocated in
149+
* @return True if domains were appended and don't contain invalid characters
149150
*/
150-
void dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_arena *gc);
151+
bool dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_arena *gc);
151152

152153
/**
153154
* Parses a string IPv4 or IPv6 address and optional colon separated port,

src/openvpn/domain_helper.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* OpenVPN -- An application to securely tunnel IP networks
3+
* over a single UDP port, with support for SSL/TLS-based
4+
* session authentication and key exchange,
5+
* packet encryption, packet authentication, and
6+
* packet compression.
7+
*
8+
* Copyright (C) 2025 Lev Stipakov <lev@openvpn.net>
9+
*
10+
* This program is free software; you can redistribute it and/or modify
11+
* it under the terms of the GNU General Public License version 2
12+
* as published by the Free Software Foundation.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License along
20+
* with this program; if not, write to the Free Software Foundation, Inc.,
21+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22+
*/
23+
24+
static inline bool
25+
is_allowed_domain_ascii(unsigned char c)
26+
{
27+
return (c >= 'A' && c <= 'Z')
28+
|| (c >= 'a' && c <= 'z')
29+
|| (c >= '0' && c <= '9')
30+
|| c == '.' || c == '-' || c == '_' || c >= 0x80;
31+
}
32+
33+
static inline bool
34+
validate_domain(const char *domain)
35+
{
36+
for (const char *ch = domain; *ch; ++ch)
37+
{
38+
if (!is_allowed_domain_ascii((unsigned char)*ch))
39+
{
40+
return false;
41+
}
42+
}
43+
44+
return true;
45+
}

src/openvpn/options.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "dco.h"
6262
#include "options_util.h"
6363
#include "tun_afunix.h"
64+
#include "domain_helper.h"
6465

6566
#include <ctype.h>
6667

@@ -5877,8 +5878,12 @@ check_dns_option(struct options *options, char *p[], const msglvl_t msglevel, bo
58775878
{
58785879
if (streq(p[1], "search-domains") && p[2])
58795880
{
5880-
dns_domain_list_append(&options->dns_options.search_domains, &p[2],
5881-
&options->dns_options.gc);
5881+
if (!dns_domain_list_append(&options->dns_options.search_domains, &p[2],
5882+
&options->dns_options.gc))
5883+
{
5884+
msg(msglevel, "--dns %s contain invalid characters", p[1]);
5885+
return false;
5886+
}
58825887
}
58835888
else if (streq(p[1], "server") && p[2] && p[3] && p[4])
58845889
{
@@ -5906,7 +5911,11 @@ check_dns_option(struct options *options, char *p[], const msglvl_t msglevel, bo
59065911
}
59075912
else if (streq(p[3], "resolve-domains"))
59085913
{
5909-
dns_domain_list_append(&server->domains, &p[4], &options->dns_options.gc);
5914+
if (!dns_domain_list_append(&server->domains, &p[4], &options->dns_options.gc))
5915+
{
5916+
msg(msglevel, "--dns server %ld: %s contain invalid characters", priority, p[3]);
5917+
return false;
5918+
}
59105919
}
59115920
else if (streq(p[3], "dnssec") && !p[5])
59125921
{
@@ -5950,6 +5959,11 @@ check_dns_option(struct options *options, char *p[], const msglvl_t msglevel, bo
59505959
}
59515960
else if (streq(p[3], "sni") && !p[5])
59525961
{
5962+
if (!validate_domain(p[4]))
5963+
{
5964+
msg(msglevel, "--dns server %ld: %s contains invalid characters", priority, p[3]);
5965+
return false;
5966+
}
59535967
server->sni = p[4];
59545968
}
59555969
else
@@ -8551,11 +8565,23 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file,
85518565

85528566
if ((streq(p[1], "DOMAIN") || streq(p[1], "ADAPTER_DOMAIN_SUFFIX")) && p[2] && !p[3])
85538567
{
8568+
if (!validate_domain(p[2]))
8569+
{
8570+
msg(msglevel, "--dhcp-option %s contains invalid characters", p[1]);
8571+
goto err;
8572+
}
8573+
85548574
dhcp->domain = p[2];
85558575
dhcp_optional = true;
85568576
}
85578577
else if (streq(p[1], "DOMAIN-SEARCH") && p[2] && !p[3])
85588578
{
8579+
if (!validate_domain(p[2]))
8580+
{
8581+
msg(msglevel, "--dhcp-option %s contains invalid characters", p[1]);
8582+
goto err;
8583+
}
8584+
85598585
if (dhcp->domain_search_list_len < N_SEARCH_LIST_LEN)
85608586
{
85618587
dhcp->domain_search_list[dhcp->domain_search_list_len++] = p[2];

0 commit comments

Comments
 (0)