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-2026 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2010-2026 Sentyron B.V. <openvpn@sentyron.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 char *lasts = NULL;
52 const char *token = strtok_r(m, "[,", &lasts);
53 while (token)
54 {
55 if (!strncmp(token, "backoff ", strlen("backoff ")))
56 {
57 if (sscanf(token, "backoff %d", &o->server_backoff_time) != 1)
58 {
59 msg(D_PUSH, "invalid AUTH_FAIL,TEMP flag: %s", token);
61 }
62 }
63 else if (!strncmp(token, "advance ", strlen("advance ")))
64 {
65 token += strlen("advance ");
66 if (!strcmp(token, "no"))
67 {
68 o->no_advance = true;
69 }
70 else if (!strcmp(token, "remote"))
71 {
72 o->advance_next_remote = true;
73 o->no_advance = false;
74 }
75 else if (!strcmp(token, "addr"))
76 {
77 /* Go on to the next remote */
78 o->no_advance = false;
79 }
80 }
81 else
82 {
83 msg(D_PUSH_ERRORS, "WARNING: unknown AUTH_FAIL,TEMP flag: %s", token);
84 }
85 token = strtok_r(NULL, "[,", &lasts);
86 }
87 }
88
89 /* Look for the message in the original buffer to safely be
90 * able to return it */
91 if (!message || message[0] != ':')
92 {
93 message = "";
94 }
95 else
96 {
97 /* Skip the : at the beginning */
98 message += 1;
99 }
100 gc_free(&gc);
101 return message;
102}
103
104bool
105valid_integer(const char *str, bool positive)
106{
107 char *endptr;
108 long long i = strtoll(str, &endptr, 10);
109
110 if (i < INT_MIN || (positive && i < 0) || *endptr != '\0' || i > INT_MAX)
111 {
112 return false;
113 }
114 else
115 {
116 return true;
117 }
118}
119
120int
121positive_atoi(const char *str, msglvl_t msglevel)
122{
123 char *endptr;
124 long long i = strtoll(str, &endptr, 10);
125
126 if (i < 0 || *endptr != '\0' || i > INT_MAX)
127 {
128 msg(msglevel, "Cannot parse argument '%s' as non-negative integer", str);
129 i = 0;
130 }
131
132 return (int)i;
133}
134
135bool
136positive_atoll(const char *str, int64_t *value, const char *name, msglvl_t msglevel)
137{
138 char *endptr;
139 long long ll = strtoll(str, &endptr, 10);
140
141 if (ll < 0 || *endptr != '\0')
142 {
143 msg(msglevel, "%s: Cannot parse '%s' as non-negative integer", name, str);
144 return false;
145 }
146
147 *value = (int64_t)ll;
148 return true;
149}
150
151int
152atoi_warn(const char *str, msglvl_t msglevel)
153{
154 char *endptr;
155 long long i = strtoll(str, &endptr, 10);
156
157 if (i < INT_MIN || *endptr != '\0' || i > INT_MAX)
158 {
159 msg(msglevel, "Cannot parse argument '%s' as integer", str);
160 i = 0;
161 }
162
163 return (int)i;
164}
165
166bool
167atoi_constrained(const char *str, int *value, const char *name, int min, int max, msglvl_t msglevel)
168{
169 ASSERT(min < max);
170
171 char *endptr;
172 long long i = strtoll(str, &endptr, 10);
173 if (i < INT_MIN || *endptr != '\0' || i > INT_MAX)
174 {
175 msg(msglevel, "%s: Cannot parse '%s' as integer", name, str);
176 return false;
177 }
178 if (i < min || i > max)
179 {
180 if (max == INT_MAX) /* nicer message for common case */
181 {
182 msg(msglevel, "%s: Must be an integer >= %d, not %lld",
183 name, min, i);
184 }
185 else
186 {
187 msg(msglevel, "%s: Must be an integer between %d and %d, not %lld",
188 name, min, max, i);
189 }
190 return false;
191 }
192
193 *value = (int)i;
194 return true;
195}
196
197bool
198boolean_flag(const char *str, const char *name, msglvl_t msglevel)
199{
200 int number = 0;
201 atoi_constrained(str, &number, name, 0, 1, msglevel);
202 return (bool)number;
203}
204
205static const char *updatable_options[] = { "block-ipv6", "block-outside-dns",
206 "dhcp-option", "dns",
207 "ifconfig", "ifconfig-ipv6",
208 "push-continuation", "redirect-gateway",
209 "redirect-private", "route",
210 "route-gateway", "route-ipv6",
211 "route-metric", "topology",
212 "tun-mtu", "keepalive" };
213
214bool
215check_push_update_option_flags(char *line, int *i, uint64_t *flags)
216{
217 *flags = 0;
218 bool opt_is_updatable = false;
219 char c = line[*i];
220
221 /* We check for '?' and '-' and
222 * if they are present we skip them.
223 */
224 if (c == '-')
225 {
226 if (!(line)[*i + 1])
227 {
228 return false;
229 }
230 *flags |= PUSH_OPT_TO_REMOVE;
231 c = (line)[++(*i)];
232 }
233 if (c == '?')
234 {
235 if (!(line)[*i + 1] || (line)[*i + 1] == '-')
236 {
237 return false;
238 }
239 *flags |= PUSH_OPT_OPTIONAL;
240 c = (line)[++(*i)];
241 }
242
243 size_t len = strlen(&line[*i]);
244 int count = sizeof(updatable_options) / sizeof(char *);
245 for (int j = 0; j < count; ++j)
246 {
247 size_t opt_len = strlen(updatable_options[j]);
248 if (len < opt_len)
249 {
250 continue;
251 }
252 if (!strncmp(&line[*i], updatable_options[j], opt_len)
253 && (!line[*i + opt_len] || line[*i + opt_len] == ' '))
254 {
255 opt_is_updatable = true;
256 break;
257 }
258 }
259
260 if (!opt_is_updatable)
261 {
262 if (*flags & PUSH_OPT_OPTIONAL)
263 {
264 msg(D_PUSH, "Pushed dispensable option is not updatable: '%s'. Ignoring.", line);
265 }
266 else
267 {
268 msg(M_WARN, "Pushed option is not updatable: '%s'.", line);
269 return false;
270 }
271 }
272
273 return true;
274}
275
276bool
277apply_pull_filter(const struct options *o, char *line)
278{
279 if (!o->pull_filter_list)
280 {
281 return true;
282 }
283
284 const struct pull_filter *f;
285
286 for (f = o->pull_filter_list->head; f; f = f->next)
287 {
288 if (f->type == PUF_TYPE_ACCEPT && strncmp(line, f->pattern, f->size) == 0)
289 {
290 msg(D_LOW, "Pushed option accepted by filter: '%s'", line);
291 return true;
292 }
293 else if (f->type == PUF_TYPE_IGNORE && strncmp(line, f->pattern, f->size) == 0)
294 {
295 msg(D_PUSH, "Pushed option removed by filter: '%s'", line);
296 *line = '\0';
297 return true;
298 }
299 else if (f->type == PUF_TYPE_REJECT && strncmp(line, f->pattern, f->size) == 0)
300 {
301 msg(M_WARN, "Pushed option rejected by filter: '%s'.", line);
302 return false;
303 }
304 }
305 return true;
306}
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:601
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1965
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1949
char * strtok_r(char *s, const char *delim, char **last)
#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:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:221
#define M_WARN
Definition error.h:92
#define PUF_TYPE_ACCEPT
filter type to accept a matching option
Definition options.h:816
#define PUF_TYPE_IGNORE
filter type to ignore a matching option
Definition options.h:817
#define PUF_TYPE_REJECT
filter type to reject and trigger SIGUSR1
Definition options.h:818
static const char * updatable_options[]
bool boolean_flag(const char *str, const char *name, msglvl_t msglevel)
Converts a str to an boolean if the string can be parsed as either 0 or 1.
int atoi_warn(const char *str, msglvl_t msglevel)
Converts a str to an integer if the string can be represented as an integer number.
int positive_atoi(const char *str, msglvl_t msglevel)
Converts a str to a positive number if the string represents a postive integer number.
bool check_push_update_option_flags(char *line, int *i, uint64_t *flags)
Checks the formatting and validity of options inside push-update messages.
bool positive_atoll(const char *str, int64_t *value, const char *name, msglvl_t msglevel)
Converts a str to an integer if the string can be represented as an integer number and is >= 0.
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.
bool atoi_constrained(const char *str, int *value, const char *name, int min, int max, msglvl_t msglevel)
Converts a str to an integer if the string can be represented as an integer number and is between min...
#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:127
int server_backoff_time
Definition options.h:310
struct pull_filter_list * pull_filter_list
Definition options.h:732
bool no_advance
Definition options.h:299
bool advance_next_remote
Definition options.h:302
struct pull_filter * head
Definition options.h:827
struct pull_filter * next
Definition options.h:822
char * pattern
Definition options.h:821
size_t size
Definition options.h:820
struct gc_arena gc
Definition test_ssl.c:138