OpenVPN
options_util.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/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) 2002-2025 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29
30#include "options_util.h"
31
32#include "push.h"
33
34const char *
35parse_auth_failed_temp(struct options *o, const char *reason)
36{
37 struct gc_arena gc = gc_new();
38
39 const char *message = reason;
40 char *m = string_alloc(reason, &gc);
41
42 /* Check if the message uses the TEMP[flags]: message format*/
43 char *endofflags = strstr(m, "]");
44
45 /* Temporary failure from the server */
46 if (m[0] == '[' && endofflags)
47 {
48 message = strstr(reason, "]") + 1;
49 /* null terminate the substring to only looks for flags between [ and ] */
50 *endofflags = '\x00';
51 const char *token = strtok(m, "[,");
52 while (token)
53 {
54 if (!strncmp(token, "backoff ", strlen("backoff ")))
55 {
56 if (sscanf(token, "backoff %d", &o->server_backoff_time) != 1)
57 {
58 msg(D_PUSH, "invalid AUTH_FAIL,TEMP flag: %s", token);
60 }
61 }
62 else if (!strncmp(token, "advance ", strlen("advance ")))
63 {
64 token += strlen("advance ");
65 if (!strcmp(token, "no"))
66 {
67 o->no_advance = true;
68 }
69 else if (!strcmp(token, "remote"))
70 {
71 o->advance_next_remote = true;
72 o->no_advance = false;
73 }
74 else if (!strcmp(token, "addr"))
75 {
76 /* Go on to the next remote */
77 o->no_advance = false;
78 }
79 }
80 else
81 {
82 msg(D_PUSH_ERRORS, "WARNING: unknown AUTH_FAIL,TEMP flag: %s", token);
83 }
84 token = strtok(NULL, "[,");
85 }
86 }
87
88 /* Look for the message in the original buffer to safely be
89 * able to return it */
90 if (!message || message[0] != ':')
91 {
92 message = "";
93 }
94 else
95 {
96 /* Skip the : at the beginning */
97 message += 1;
98 }
99 gc_free(&gc);
100 return message;
101}
102
103bool
104valid_integer(const char *str, bool positive)
105{
106 char *endptr;
107 long long i = strtoll(str, &endptr, 10);
108
109 if (i < INT_MIN || (positive && i < 0) || *endptr != '\0' || i > INT_MAX)
110 {
111 return false;
112 }
113 else
114 {
115 return true;
116 }
117}
118
119int
120positive_atoi(const char *str, int msglevel)
121{
122 char *endptr;
123 long long i = strtoll(str, &endptr, 10);
124
125 if (i < 0 || *endptr != '\0' || i > INT_MAX)
126 {
127 msg(msglevel, "Cannot parse argument '%s' as non-negative integer", str);
128 i = 0;
129 }
130
131 return (int)i;
132}
133
134int
135atoi_warn(const char *str, int msglevel)
136{
137 char *endptr;
138 long long i = strtoll(str, &endptr, 10);
139
140 if (i < INT_MIN || *endptr != '\0' || i > INT_MAX)
141 {
142 msg(msglevel, "Cannot parse argument '%s' as integer", str);
143 i = 0;
144 }
145
146 return (int)i;
147}
148
149bool
150atoi_constrained(const char *str, int *value, const char *name, int min, int max, int msglevel)
151{
152 ASSERT(min < max);
153
154 char *endptr;
155 long long i = strtoll(str, &endptr, 10);
156 if (i < INT_MIN || *endptr != '\0' || i > INT_MAX)
157 {
158 msg(msglevel, "%s: Cannot parse '%s' as integer", name, str);
159 return false;
160 }
161 if (i < min || i > max)
162 {
163 if (max == INT_MAX) /* nicer message for common case */
164 {
165 msg(msglevel, "%s: Must be an integer >= %d, not %lld",
166 name, min, i);
167 }
168 else
169 {
170 msg(msglevel, "%s: Must be an integer between %d and %d, not %lld",
171 name, min, max, i);
172 }
173 return false;
174 }
175
176 *value = i;
177 return true;
178}
179
180static const char *updatable_options[] = { "block-ipv6", "block-outside-dns",
181 "dhcp-option", "dns",
182 "ifconfig", "ifconfig-ipv6",
183 "push-continuation", "redirect-gateway",
184 "redirect-private", "route",
185 "route-gateway", "route-ipv6",
186 "route-metric", "topology",
187 "tun-mtu", "keepalive" };
188
189bool
190check_push_update_option_flags(char *line, int *i, unsigned int *flags)
191{
192 *flags = 0;
193 bool opt_is_updatable = false;
194 char c = line[*i];
195
196 /* We check for '?' and '-' and
197 * if they are present we skip them.
198 */
199 if (c == '-')
200 {
201 if (!(line)[*i + 1])
202 {
203 return false;
204 }
205 *flags |= PUSH_OPT_TO_REMOVE;
206 c = (line)[++(*i)];
207 }
208 if (c == '?')
209 {
210 if (!(line)[*i + 1] || (line)[*i + 1] == '-')
211 {
212 return false;
213 }
214 *flags |= PUSH_OPT_OPTIONAL;
215 c = (line)[++(*i)];
216 }
217
218 size_t len = strlen(&line[*i]);
219 int count = sizeof(updatable_options) / sizeof(char *);
220 for (int j = 0; j < count; ++j)
221 {
222 size_t opt_len = strlen(updatable_options[j]);
223 if (len < opt_len)
224 {
225 continue;
226 }
227 if (!strncmp(&line[*i], updatable_options[j], opt_len)
228 && (!line[*i + opt_len] || line[*i + opt_len] == ' '))
229 {
230 opt_is_updatable = true;
231 break;
232 }
233 }
234
235 if (!opt_is_updatable)
236 {
237 if (*flags & PUSH_OPT_OPTIONAL)
238 {
239 msg(D_PUSH, "Pushed dispensable option is not updatable: '%s'. Ignoring.", line);
240 }
241 else
242 {
243 msg(M_WARN, "Pushed option is not updatable: '%s'.", line);
244 return false;
245 }
246 }
247
248 return true;
249}
250
251bool
252apply_pull_filter(const struct options *o, char *line)
253{
254 if (!o->pull_filter_list)
255 {
256 return true;
257 }
258
259 struct pull_filter *f;
260
261 for (f = o->pull_filter_list->head; f; f = f->next)
262 {
263 if (f->type == PUF_TYPE_ACCEPT && strncmp(line, f->pattern, f->size) == 0)
264 {
265 msg(D_LOW, "Pushed option accepted by filter: '%s'", line);
266 return true;
267 }
268 else if (f->type == PUF_TYPE_IGNORE && strncmp(line, f->pattern, f->size) == 0)
269 {
270 msg(D_PUSH, "Pushed option removed by filter: '%s'", line);
271 *line = '\0';
272 return true;
273 }
274 else if (f->type == PUF_TYPE_REJECT && strncmp(line, f->pattern, f->size) == 0)
275 {
276 msg(M_WARN, "Pushed option rejected by filter: '%s'.", line);
277 return false;
278 }
279 }
280 return true;
281}
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
#define D_PUSH
Definition errlevel.h:82
#define D_PUSH_ERRORS
Definition errlevel.h:66
#define D_LOW
Definition errlevel.h:96
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
#define M_WARN
Definition error.h:90
#define PUF_TYPE_ACCEPT
filter type to accept a matching option
Definition options.h:810
#define PUF_TYPE_IGNORE
filter type to ignore a matching option
Definition options.h:811
#define PUF_TYPE_REJECT
filter type to reject and trigger SIGUSR1
Definition options.h:812
bool check_push_update_option_flags(char *line, int *i, unsigned int *flags)
Checks the formatting and validity of options inside push-update messages.
bool atoi_constrained(const char *str, int *value, const char *name, int min, int max, int msglevel)
Converts a str to an integer if the string can be represented as an integer number and is between min...
static const char * updatable_options[]
int positive_atoi(const char *str, int msglevel)
Converts a str to a positive number if the string represents a postive integer number.
int atoi_warn(const char *str, int msglevel)
Converts a str to an integer if the string can be represented as an integer number.
bool apply_pull_filter(const struct options *o, char *line)
Filter an option line by all pull filters.
const char * parse_auth_failed_temp(struct options *o, const char *reason)
bool valid_integer(const char *str, bool positive)
Checks if the string is a valid integer by checking if it can be converted to an integer.
#define PUSH_OPT_TO_REMOVE
Definition push.h:41
#define PUSH_OPT_OPTIONAL
Definition push.h:42
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
int server_backoff_time
Definition options.h:306
struct pull_filter_list * pull_filter_list
Definition options.h:717
bool no_advance
Definition options.h:295
bool advance_next_remote
Definition options.h:298
struct pull_filter * head
Definition options.h:821
struct pull_filter * next
Definition options.h:816
char * pattern
Definition options.h:815
struct gc_arena gc
Definition test_ssl.c:154