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#endif
11
12#if defined(__GNUC__) || defined(__clang__)
13#pragma GCC diagnostic push
14#pragma GCC diagnostic ignored "-Wconversion"
15#endif
16
17int
18process_incoming_push_update(struct context *c, unsigned int permission_mask,
19 unsigned int *option_types_found, struct buffer *buf,
20 bool msg_sender)
21{
22 int ret = PUSH_MSG_ERROR;
23 const uint8_t ch = buf_read_u8(buf);
24 if (ch == ',')
25 {
26 if (apply_push_options(c, &c->options, buf, permission_mask, option_types_found, c->c2.es,
27 true))
28 {
29 switch (c->options.push_continuation)
30 {
31 case 0:
32 case 1:
33 ret = PUSH_MSG_UPDATE;
34 break;
35
36 case 2:
38 break;
39 }
40 }
41 else if (!msg_sender)
42 {
43 throw_signal_soft(SIGUSR1, "Offending option received from server");
44 }
45 }
46 else if (ch == '\0')
47 {
48 ret = PUSH_MSG_UPDATE;
49 }
50
51 return ret;
52}
53
54#ifdef ENABLE_MANAGEMENT
59static int
60find_first_comma_of_next_bundle(const char *str, int ix)
61{
62 while (ix > 0)
63 {
64 if (str[ix] == ',')
65 {
66 return ix;
67 }
68 ix--;
69 }
70 return 0;
71}
72
73/* Allocate memory and assemble the final message */
74static struct buffer
76{
77 int src_len = strlen(src);
78 int con_len = continuation ? strlen(continuation) : 0;
79 struct buffer buf = alloc_buf_gc(src_len + sizeof(push_update_cmd) + con_len + 2, gc);
80
81 buf_printf(&buf, "%s,%s%s", push_update_cmd, src, continuation ? continuation : "");
82
83 return buf;
84}
85
86static char *
87gc_strdup(const char *src, struct gc_arena *gc)
88{
89 char *ret = gc_malloc((strlen(src) + 1) * sizeof(char), true, gc);
90
91 strcpy(ret, src);
92 return ret;
93}
94
95/* It split the messagge (if necessay) and fill msgs with the message chunks.
96 * Return `false` on failure an `true` on success.
97 */
98static bool
99message_splitter(const char *s, struct buffer *msgs, struct gc_arena *gc, const int safe_cap)
100{
101 if (!s || !*s)
102 {
103 return false;
104 }
105
106 char *str = gc_strdup(s, gc);
107 int i = 0;
108 int im = 0;
109
110 while (*str)
111 {
112 /* + ',' - '/0' */
113 if (strlen(str) > safe_cap)
114 {
116 if (!ci)
117 {
118 /* if no commas were found go to fail, do not send any message */
119 return false;
120 }
121 str[ci] = '\0';
122 /* copy from i to (ci -1) */
123 msgs[im] = forge_msg(str, ",push-continuation 2", gc);
124 i = ci + 1;
125 }
126 else
127 {
128 if (im)
129 {
130 msgs[im] = forge_msg(str, ",push-continuation 1", gc);
131 }
132 else
133 {
134 msgs[im] = forge_msg(str, NULL, gc);
135 }
136 i = strlen(str);
137 }
138 str = &str[i];
139 im++;
140 }
141 return true;
142}
143
144/* send the message(s) prepared to one single client */
145static bool
146send_single_push_update(struct context *c, struct buffer *msgs, unsigned int *option_types_found)
147{
148 if (!msgs[0].data || !*(msgs[0].data))
149 {
150 return false;
151 }
152 int i = -1;
153
154 while (msgs[++i].data && *(msgs[i].data))
155 {
157 {
158 return false;
159 }
160
161 /* After sending the control message, we update the options
162 * server-side in the client's context so pushed options like
163 * ifconfig/ifconfig-ipv6 can actually work.
164 * If we don't do that, packets arriving from the client with the
165 * new address will be rejected and packets for the new address
166 * will not be routed towards the client.
167 * For the same reason we later update the vhash too in
168 * `send_push_update()` function.
169 * Using `buf_string_compare_advance()` we mimic the behavior
170 * inside `process_incoming_push_msg()`. However, we don't need
171 * to check the return value here because we just want to `advance`,
172 * meaning we skip the `push_update_cmd' we added earlier.
173 */
175 if (process_incoming_push_update(c, pull_permission_mask(c), option_types_found, &msgs[i], true) == PUSH_MSG_ERROR)
176 {
177 msg(M_WARN, "Failed to process push update message sent to client ID: %u",
178 c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
179 continue;
180 }
181 c->options.push_option_types_found |= *option_types_found;
182 if (!options_postprocess_pull(&c->options, c->c2.es))
183 {
184 msg(M_WARN, "Failed to post-process push update message sent to client ID: %u",
185 c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
186 }
187 }
188 return true;
189}
190
191int
192send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size)
193{
194 if (!msg || !*msg || !m
195 || (!target && type != UPT_BROADCAST))
196 {
197 return -EINVAL;
198 }
199
200 struct gc_arena gc = gc_new();
201 /* extra space for possible trailing ifconfig and push-continuation */
202 const int extra = 84 + sizeof(push_update_cmd);
203 /* push_bundle_size is the maximum size of a message, so if the message
204 * we want to send exceeds that size we have to split it into smaller messages */
205 const int safe_cap = push_bundle_size - extra;
206 int msgs_num = (strlen(msg) / safe_cap) + ((strlen(msg) % safe_cap) != 0);
207 struct buffer *msgs = gc_malloc((msgs_num + 1) * sizeof(struct buffer), true, &gc);
208
209 unsigned int option_types_found = 0;
210
211 msgs[msgs_num].data = NULL;
213 {
214 gc_free(&gc);
215 return -EINVAL;
216 }
217
218 if (type == UPT_BY_CID)
219 {
220 struct multi_instance *mi = lookup_by_cid(m, *((unsigned long *)target));
221
222 if (!mi)
223 {
224 return -ENOENT;
225 }
226
227 const char *old_ip = mi->context.options.ifconfig_local;
228 const char *old_ipv6 = mi->context.options.ifconfig_ipv6_local;
229 if (!mi->halt
230 && send_single_push_update(&mi->context, msgs, &option_types_found))
231 {
232 if (option_types_found & OPT_P_UP)
233 {
234 update_vhash(m, mi, old_ip, old_ipv6);
235 }
236 gc_free(&gc);
237 return 1;
238 }
239 else
240 {
241 gc_free(&gc);
242 return 0;
243 }
244 }
245
246 int count = 0;
247 struct hash_iterator hi;
248 const struct hash_element *he;
249
250 hash_iterator_init(m->iter, &hi);
251 while ((he = hash_iterator_next(&hi)))
252 {
253 struct multi_instance *curr_mi = he->value;
254
255 if (curr_mi->halt)
256 {
257 continue;
258 }
259
260 /* Type is UPT_BROADCAST so we update every client */
261 option_types_found = 0;
262 const char *old_ip = curr_mi->context.options.ifconfig_local;
263 const char *old_ipv6 = curr_mi->context.options.ifconfig_ipv6_local;
264 if (!send_single_push_update(&curr_mi->context, msgs, &option_types_found))
265 {
266 msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated",
267 curr_mi->context.c2.tls_multi ? curr_mi->context.c2.tls_multi->peer_id : UINT32_MAX);
268 continue;
269 }
270 if (option_types_found & OPT_P_UP)
271 {
272 update_vhash(m, curr_mi, old_ip, old_ipv6);
273 }
274 count++;
275 }
276
278 gc_free(&gc);
279 return count;
280}
281
282#define RETURN_UPDATE_STATUS(n_sent) \
283 do \
284 { \
285 if ((n_sent) > 0) \
286 { \
287 msg(M_CLIENT, "SUCCESS: %d client(s) updated", (n_sent)); \
288 return true; \
289 } \
290 else \
291 { \
292 msg(M_CLIENT, "ERROR: no client updated"); \
293 return false; \
294 } \
295 } while (0)
296
297
298bool
300{
301 int n_sent = send_push_update(arg, NULL, options, UPT_BROADCAST, PUSH_BUNDLE_SIZE);
302
303 RETURN_UPDATE_STATUS(n_sent);
304}
305
306bool
307management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
308{
309 int n_sent = send_push_update(arg, &cid, options, UPT_BY_CID, PUSH_BUNDLE_SIZE);
310
311 RETURN_UPDATE_STATUS(n_sent);
312}
313#endif /* ifdef ENABLE_MANAGEMENT */
314
315#if defined(__GNUC__) || defined(__clang__)
316#pragma GCC diagnostic pop
317#endif
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:774
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
#define PUSH_BUNDLE_SIZE
Definition common.h:87
#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:4005
void update_vhash(struct multi_context *m, struct multi_instance *mi, const char *old_ip, const char *old_ipv6)
Update the vhash with new IP/IPv6 addresses in the multi_context when a push-update message containin...
Definition multi.c:4280
Header file for server-mode related structures and functions.
#define M_CLIENT
Definition error.h:108
#define msg(flags,...)
Definition error.h:152
#define M_WARN
Definition error.h:92
bool options_postprocess_pull(struct options *o, struct env_set *es)
Definition options.c:4293
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)
Definition options.c:5463
#define OPT_P_UP
Definition options.h:733
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 bool message_splitter(const char *s, struct buffer *msgs, struct gc_arena *gc, const int safe_cap)
Definition push_util.c:99
static struct buffer forge_msg(const char *src, const char *continuation, struct gc_arena *gc)
Definition push_util.c:75
static int find_first_comma_of_next_bundle(const char *str, int ix)
Return index of last , or 0 if it didn't find any.
Definition push_util.c:60
int send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size)
A function to send a PUSH_UPDATE control message from server to client(s).
Definition push_util.c:192
#define RETURN_UPDATE_STATUS(n_sent)
Definition push_util.c:282
bool management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
Definition push_util.c:307
bool management_callback_send_push_update_broadcast(void *arg, const char *options)
Definition push_util.c:299
static char * gc_strdup(const char *src, struct gc_arena *gc)
Definition push_util.c:87
static bool send_single_push_update(struct context *c, struct buffer *msgs, unsigned int *option_types_found)
Definition push_util.c:146
int process_incoming_push_update(struct context *c, 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:18
void throw_signal_soft(const int signum, const char *signal_text)
Throw a soft global signal.
Definition sig.c:204
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
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
struct options options
Options loaded from command line or configuration file.
Definition openvpn.h:475
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:164
struct hash * iter
VPN tunnel instances indexed by real address of the remote peer, optimized for iteration.
Definition multi.h:172
Server-mode state structure for one single VPN tunnel.
Definition multi.h:103
struct context context
The context structure storing state for this VPN tunnel.
Definition multi.h:144
const char * ifconfig_ipv6_local
Definition options.h:325
const char * ifconfig_local
Definition options.h:323
int push_continuation
Definition options.h:560
uint32_t peer_id
Definition ssl_common.h:700
struct gc_arena gc
Definition test_ssl.c:131