OpenVPN
push_util.c
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "push.h"
6#include "buffer.h"
7
8#ifdef ENABLE_MANAGEMENT
9#include "multi.h"
10#include "ssl_util.h"
11#endif
12
13int
14process_push_update(struct context *c, struct options *o, unsigned int permission_mask,
15 unsigned int *option_types_found, struct buffer *buf, bool msg_sender)
16{
17 int ret = PUSH_MSG_ERROR;
18 const int ch = buf_read_u8(buf);
19 if (ch == ',')
20 {
21 if (apply_push_options(c, o, buf, permission_mask, option_types_found, c->c2.es,
22 true))
23 {
24 switch (o->push_continuation)
25 {
26 case 0:
27 case 1:
28 ret = PUSH_MSG_UPDATE;
29 break;
30
31 case 2:
33 break;
34 }
35 }
36 else if (!msg_sender)
37 {
38 throw_signal_soft(SIGUSR1, "Offending option received from server");
39 }
40 }
41 else if (ch == '\0')
42 {
43 ret = PUSH_MSG_UPDATE;
44 }
45
46 return ret;
47}
48
49#ifdef ENABLE_MANAGEMENT
54static size_t
55find_first_comma_of_next_bundle(const char *str, size_t ix)
56{
57 while (ix > 0)
58 {
59 if (str[ix] == ',')
60 {
61 return ix;
62 }
63 ix--;
64 }
65 return 0;
66}
67
68/* Allocate memory and assemble the final message */
69static struct buffer
71{
72 size_t src_len = strlen(src);
73 size_t con_len = continuation ? strlen(continuation) : 0;
74 struct buffer buf = alloc_buf_gc(src_len + sizeof(push_update_cmd) + con_len + 2, gc);
75
76 buf_printf(&buf, "%s,%s%s", push_update_cmd, src, continuation ? continuation : "");
77
78 return buf;
79}
80
81static char *
82gc_strdup(const char *src, struct gc_arena *gc)
83{
84 char *ret = gc_malloc((strlen(src) + 1) * sizeof(char), true, gc);
85
86 strcpy(ret, src);
87 return ret;
88}
89
90/* It split the messagge (if necessary) and fill msgs with the message chunks.
91 * Return `false` on failure an `true` on success.
92 */
93static bool
94message_splitter(const char *s, struct buffer *msgs, struct gc_arena *gc, const size_t safe_cap)
95{
96 if (!s || !*s)
97 {
98 return false;
99 }
100
101 char *str = gc_strdup(s, gc);
102 size_t i = 0;
103 int im = 0;
104
105 while (*str)
106 {
107 /* + ',' - '/0' */
108 if (strlen(str) > safe_cap)
109 {
111 if (!ci)
112 {
113 /* if no commas were found go to fail, do not send any message */
114 return false;
115 }
116 str[ci] = '\0';
117 /* copy from i to (ci -1) */
118 msgs[im] = forge_msg(str, ",push-continuation 2", gc);
119 i = ci + 1;
120 }
121 else
122 {
123 if (im)
124 {
125 msgs[im] = forge_msg(str, ",push-continuation 1", gc);
126 }
127 else
128 {
129 msgs[im] = forge_msg(str, NULL, gc);
130 }
131 i = strlen(str);
132 }
133 str = &str[i];
134 im++;
135 }
136 return true;
137}
138
139/* send the message(s) prepared to one single client */
140static bool
142{
143 if (!msgs[0].data || !*(msgs[0].data))
144 {
145 return false;
146 }
147
148 int i = -1;
149 unsigned int option_types_found = 0;
150 struct context *c = &mi->context;
151 struct options o;
152 CLEAR(o);
153
154 /* Set canary values to detect ifconfig options in push-update messages.
155 * These placeholder strings will be overwritten to NULL by the option
156 * parser if -ifconfig or -ifconfig-ipv6 options are present in the
157 * push-update.
158 */
159 const char *canary = "canary";
160 o.ifconfig_local = canary;
161 o.ifconfig_ipv6_local = canary;
162
163 while (msgs[++i].data && *(msgs[i].data))
164 {
165 if (!send_control_channel_string(c, BSTR(&msgs[i]), D_PUSH))
166 {
167 return false;
168 }
169
170 /* After sending the control message, we parse it, miming the behavior
171 * of `process_incoming_push_msg()` and we fill an empty `options` struct
172 * with the new options. If an `ifconfig_local` or `ifconfig_ipv6_local`
173 * options is found we update the vhash accordingly, so that the pushed
174 * ifconfig/ifconfig-ipv6 options can actually work.
175 * If we don't do that, packets arriving from the client with the
176 * new address will be rejected and packets for the new address
177 * will not be routed towards the client.
178 * Using `buf_string_compare_advance()` we mimic the behavior
179 * inside `process_incoming_push_msg()`. However, we don't need
180 * to check the return value here because we just want to `advance`,
181 * meaning we skip the `push_update_cmd' we added earlier.
182 * Also we need to make a temporary copy so we can buf_advance()
183 * without modifying original buffer.
184 */
185 struct buffer tmp_msg = msgs[i];
187 unsigned int permission_mask = pull_permission_mask(c);
188 if (process_push_update(c, &o, permission_mask, &option_types_found, &tmp_msg, true) == PUSH_MSG_ERROR)
189 {
190 msg(M_WARN, "Failed to process push update message sent to client ID: %u", c->c2.tls_multi->peer_id);
191 }
192 }
193
194 if (option_types_found & OPT_P_UP)
195 {
196 /* -ifconfig */
197 if (!o.ifconfig_local && mi->context.c2.push_ifconfig_defined)
198 {
199 unlearn_ifconfig(m, mi);
200 }
201 /* -ifconfig-ipv6 */
202 if (!o.ifconfig_ipv6_local && mi->context.c2.push_ifconfig_ipv6_defined)
203 {
205 }
206
207 if (o.ifconfig_local && !strcmp(o.ifconfig_local, canary))
208 {
209 o.ifconfig_local = NULL;
210 }
211 if (o.ifconfig_ipv6_local && !strcmp(o.ifconfig_ipv6_local, canary))
212 {
213 o.ifconfig_ipv6_local = NULL;
214 }
215
216 /* new ifconfig or new ifconfig-ipv6 */
217 update_vhash(m, mi, o.ifconfig_local, o.ifconfig_ipv6_local);
218 }
219
220 return true;
221}
222
223/* Return true if the client supports push-update */
224static bool
226{
228 const unsigned int iv_proto_peer = extract_iv_proto(mi->context.c2.tls_multi->peer_info);
230 {
231 return false;
232 }
233
234 return true;
235}
236
251static int
252send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const size_t push_bundle_size)
253{
254 if (dco_enabled(&m->top.options))
255 {
256 msg(M_WARN, "WARN: PUSH_UPDATE messages cannot currently be sent while DCO is enabled."
257 " To send a PUSH_UPDATE message, be sure to use the --disable-dco option.");
258 return 0;
259 }
260
261 if (!msg || !*msg || !m || (!target && type != UPT_BROADCAST))
262 {
263 return -EINVAL;
264 }
265
266 struct gc_arena gc = gc_new();
267 /* extra space for possible trailing ifconfig and push-continuation */
268 const size_t extra = 84 + sizeof(push_update_cmd);
269 /* push_bundle_size is the maximum size of a message, so if the message
270 * we want to send exceeds that size we have to split it into smaller messages */
271 ASSERT(push_bundle_size > extra);
272 const size_t safe_cap = push_bundle_size - extra;
273 size_t msgs_num = (strlen(msg) / safe_cap) + ((strlen(msg) % safe_cap) != 0);
274 struct buffer *msgs = gc_malloc((msgs_num + 1) * sizeof(struct buffer), true, &gc);
275
276 msgs[msgs_num].data = NULL;
278 {
279 gc_free(&gc);
280 return -EINVAL;
281 }
282
283 if (type == UPT_BY_CID)
284 {
285 struct multi_instance *mi = lookup_by_cid(m, *((unsigned long *)target));
286
287 if (!mi)
288 {
289 gc_free(&gc);
290 return -ENOENT;
291 }
292
293 if (!support_push_update(mi))
294 {
295 msg(M_CLIENT, "PUSH_UPDATE: not sending message to unsupported peer with ID: %u", mi->context.c2.tls_multi->peer_id);
296 gc_free(&gc);
297 return 0;
298 }
299
300 if (!mi->halt
301 && send_single_push_update(m, mi, msgs))
302 {
303 gc_free(&gc);
304 return 1;
305 }
306 else
307 {
308 gc_free(&gc);
309 return 0;
310 }
311 }
312
313 int count = 0;
314 struct hash_iterator hi;
315 const struct hash_element *he;
316
317 hash_iterator_init(m->iter, &hi);
318 while ((he = hash_iterator_next(&hi)))
319 {
320 struct multi_instance *curr_mi = he->value;
321
322 if (curr_mi->halt || !support_push_update(curr_mi))
323 {
324 continue;
325 }
326
327 /* Type is UPT_BROADCAST so we update every client */
328 if (!send_single_push_update(m, curr_mi, msgs))
329 {
330 msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", curr_mi->context.c2.tls_multi->peer_id);
331 continue;
332 }
333 count++;
334 }
335
337 gc_free(&gc);
338 return count;
339}
340
341#define RETURN_UPDATE_STATUS(n_sent) \
342 do \
343 { \
344 if ((n_sent) > 0) \
345 { \
346 msg(M_CLIENT, "SUCCESS: %d client(s) updated", (n_sent)); \
347 return true; \
348 } \
349 else \
350 { \
351 msg(M_CLIENT, "ERROR: no client updated"); \
352 return false; \
353 } \
354 } while (0)
355
356bool
358{
359 int n_sent = send_push_update(arg, NULL, options, UPT_BROADCAST, PUSH_BUNDLE_SIZE);
360
361 RETURN_UPDATE_STATUS(n_sent);
362}
363
364bool
365management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
366{
367 int n_sent = send_push_update(arg, &cid, options, UPT_BY_CID, PUSH_BUNDLE_SIZE);
368
369 RETURN_UPDATE_STATUS(n_sent);
370}
371#endif /* ifdef ENABLE_MANAGEMENT */
bool buf_string_compare_advance(struct buffer *src, const char *match)
Definition buffer.c:789
bool buf_printf(struct buffer *buf, const char *format,...)
Definition buffer.c:241
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Definition buffer.c:336
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Definition buffer.c:89
#define BSTR(buf)
Definition buffer.h:128
static int buf_read_u8(struct buffer *buf)
Definition buffer.h:786
static void gc_free(struct gc_arena *a)
Definition buffer.h:1025
static struct gc_arena gc_new(void)
Definition buffer.h:1017
#define PUSH_BUNDLE_SIZE
Definition common.h:89
#define D_PUSH
Definition errlevel.h:82
bool send_control_channel_string(struct context *c, const char *str, msglvl_t msglevel)
Definition forward.c:404
unsigned int pull_permission_mask(const struct context *c)
Definition init.c:2544
void hash_iterator_free(struct hash_iterator *hi)
Definition list.c:270
struct hash_element * hash_iterator_next(struct hash_iterator *hi)
Definition list.c:276
void hash_iterator_init(struct hash *hash, struct hash_iterator *hi)
Definition list.c:234
struct multi_instance * lookup_by_cid(struct multi_context *m, const unsigned long cid)
Definition multi.c:3989
void unlearn_ifconfig_ipv6(struct multi_context *m, struct multi_instance *mi)
Definition multi.c:4333
void unlearn_ifconfig(struct multi_context *m, struct multi_instance *mi)
Definition multi.c:4322
void update_vhash(struct multi_context *m, struct multi_instance *mi, const char *new_ip, const char *new_ipv6)
Update the vhash with new IP/IPv6 addresses in the multi_context when a push-update message containin...
Definition multi.c:4354
Header file for server-mode related structures and functions.
#define CLEAR(x)
Definition basic.h:32
#define M_CLIENT
Definition error.h:108
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define OPT_P_UP
Definition options.h:736
static bool dco_enabled(const struct options *o)
Returns whether the current configuration has dco enabled.
Definition options.h:1008
bool apply_push_options(struct context *c, struct options *options, struct buffer *buf, unsigned int permission_mask, unsigned int *option_types_found, struct env_set *es, bool is_update)
push_update_type
Definition push.h:47
@ UPT_BY_CID
Definition push.h:49
@ UPT_BROADCAST
Definition push.h:48
#define PUSH_MSG_ERROR
Definition push.h:28
#define PUSH_MSG_UPDATE
Definition push.h:35
#define push_update_cmd
Definition push.h:38
#define PUSH_MSG_CONTINUATION
Definition push.h:33
static struct buffer forge_msg(const char *src, const char *continuation, struct gc_arena *gc)
Definition push_util.c:70
static bool support_push_update(struct multi_instance *mi)
Definition push_util.c:225
int process_push_update(struct context *c, struct options *o, unsigned int permission_mask, unsigned int *option_types_found, struct buffer *buf, bool msg_sender)
Handles the receiving of a push-update message and applies updates to the specified options.
Definition push_util.c:14
static bool message_splitter(const char *s, struct buffer *msgs, struct gc_arena *gc, const size_t safe_cap)
Definition push_util.c:94
#define RETURN_UPDATE_STATUS(n_sent)
Definition push_util.c:341
static int send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const size_t push_bundle_size)
A function to send a PUSH_UPDATE control message from server to client(s).
Definition push_util.c:252
bool management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
Definition push_util.c:365
static bool send_single_push_update(struct multi_context *m, struct multi_instance *mi, struct buffer *msgs)
Definition push_util.c:141
static size_t find_first_comma_of_next_bundle(const char *str, size_t ix)
Return index of last , or 0 if it didn't find any.
Definition push_util.c:55
bool management_callback_send_push_update_broadcast(void *arg, const char *options)
Definition push_util.c:357
static char * gc_strdup(const char *src, struct gc_arena *gc)
Definition push_util.c:82
void throw_signal_soft(const int signum, const char *signal_text)
Throw a soft global signal.
Definition sig.c:204
#define IV_PROTO_PUSH_UPDATE
Supports push-update.
Definition ssl.h:117
unsigned int extract_iv_proto(const char *peer_info)
Extracts the IV_PROTO variable and returns its value or 0 if it cannot be extracted.
Definition ssl_util.c:60
SSL utility functions.
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:67
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
bool push_ifconfig_ipv6_defined
Definition openvpn.h:434
bool push_ifconfig_defined
Definition openvpn.h:428
struct env_set * es
Definition openvpn.h:420
struct tls_multi * tls_multi
TLS state structure for this VPN tunnel.
Definition openvpn.h:323
Contains all state information for one tunnel.
Definition openvpn.h:474
struct context_2 c2
Level 2 context.
Definition openvpn.h:517
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
void * value
Definition list.h:41
Main OpenVPN server state structure.
Definition multi.h:163
struct hash * iter
VPN tunnel instances indexed by real address of the remote peer, optimized for iteration.
Definition multi.h:171
Server-mode state structure for one single VPN tunnel.
Definition multi.h:102
struct context context
The context structure storing state for this VPN tunnel.
Definition multi.h:143
const char * ifconfig_ipv6_local
Definition options.h:328
const char * ifconfig_local
Definition options.h:326
int push_continuation
Definition options.h:563
char * peer_info
A multi-line string of general-purpose info received from peer over control channel.
Definition ssl_common.h:673
uint32_t peer_id
Definition ssl_common.h:700
struct gc_arena gc
Definition test_ssl.c:131