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
12int
13process_incoming_push_update(struct context *c, unsigned int permission_mask,
14 unsigned int *option_types_found, struct buffer *buf,
15 bool msg_sender)
16{
17 int ret = PUSH_MSG_ERROR;
18 const uint8_t ch = buf_read_u8(buf);
19 if (ch == ',')
20 {
21 if (apply_push_options(c, &c->options, buf, permission_mask, option_types_found, c->c2.es,
22 true))
23 {
24 switch (c->options.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 int
55find_first_comma_of_next_bundle(const char *str, int 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 int src_len = strlen(src);
73 int 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 necessay) 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 int safe_cap)
95{
96 if (!s || !*s)
97 {
98 return false;
99 }
100
101 char *str = gc_strdup(s, gc);
102 int 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
141send_single_push_update(struct context *c, struct buffer *msgs, unsigned int *option_types_found)
142{
143 if (!msgs[0].data || !*(msgs[0].data))
144 {
145 return false;
146 }
147 int i = -1;
148
149 while (msgs[++i].data && *(msgs[i].data))
150 {
152 {
153 return false;
154 }
155
156 /* After sending the control message, we update the options
157 * server-side in the client's context so pushed options like
158 * ifconfig/ifconfig-ipv6 can actually work.
159 * If we don't do that, packets arriving from the client with the
160 * new address will be rejected and packets for the new address
161 * will not be routed towards the client.
162 * For the same reason we later update the vhash too in
163 * `send_push_update()` function.
164 * Using `buf_string_compare_advance()` we mimic the behavior
165 * inside `process_incoming_push_msg()`. However, we don't need
166 * to check the return value here because we just want to `advance`,
167 * meaning we skip the `push_update_cmd' we added earlier.
168 */
170 if (process_incoming_push_update(c, pull_permission_mask(c), option_types_found, &msgs[i], true) == PUSH_MSG_ERROR)
171 {
172 msg(M_WARN, "Failed to process push update message sent to client ID: %u",
173 c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
174 continue;
175 }
176 c->options.push_option_types_found |= *option_types_found;
177 if (!options_postprocess_pull(&c->options, c->c2.es))
178 {
179 msg(M_WARN, "Failed to post-process push update message sent to client ID: %u",
180 c->c2.tls_multi ? c->c2.tls_multi->peer_id : UINT32_MAX);
181 }
182 }
183 return true;
184}
185
186int
187send_push_update(struct multi_context *m, const void *target, const char *msg, const push_update_type type, const int push_bundle_size)
188{
189 if (!msg || !*msg || !m
190 || (!target && type != UPT_BROADCAST))
191 {
192 return -EINVAL;
193 }
194
195 struct gc_arena gc = gc_new();
196 /* extra space for possible trailing ifconfig and push-continuation */
197 const int extra = 84 + sizeof(push_update_cmd);
198 /* push_bundle_size is the maximum size of a message, so if the message
199 * we want to send exceeds that size we have to split it into smaller messages */
200 const int safe_cap = push_bundle_size - extra;
201 int msgs_num = (strlen(msg) / safe_cap) + ((strlen(msg) % safe_cap) != 0);
202 struct buffer *msgs = gc_malloc((msgs_num + 1) * sizeof(struct buffer), true, &gc);
203
204 unsigned int option_types_found = 0;
205
206 msgs[msgs_num].data = NULL;
208 {
209 gc_free(&gc);
210 return -EINVAL;
211 }
212
213 if (type == UPT_BY_CID)
214 {
215 struct multi_instance *mi = lookup_by_cid(m, *((unsigned long *)target));
216
217 if (!mi)
218 {
219 return -ENOENT;
220 }
221
222 const char *old_ip = mi->context.options.ifconfig_local;
223 const char *old_ipv6 = mi->context.options.ifconfig_ipv6_local;
224 if (!mi->halt
225 && send_single_push_update(&mi->context, msgs, &option_types_found))
226 {
227 if (option_types_found & OPT_P_UP)
228 {
229 update_vhash(m, mi, old_ip, old_ipv6);
230 }
231 gc_free(&gc);
232 return 1;
233 }
234 else
235 {
236 gc_free(&gc);
237 return 0;
238 }
239 }
240
241 int count = 0;
242 struct hash_iterator hi;
243 const struct hash_element *he;
244
245 hash_iterator_init(m->iter, &hi);
246 while ((he = hash_iterator_next(&hi)))
247 {
248 struct multi_instance *curr_mi = he->value;
249
250 if (curr_mi->halt)
251 {
252 continue;
253 }
254
255 /* Type is UPT_BROADCAST so we update every client */
256 option_types_found = 0;
257 const char *old_ip = curr_mi->context.options.ifconfig_local;
258 const char *old_ipv6 = curr_mi->context.options.ifconfig_ipv6_local;
259 if (!send_single_push_update(&curr_mi->context, msgs, &option_types_found))
260 {
261 msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated",
262 curr_mi->context.c2.tls_multi ? curr_mi->context.c2.tls_multi->peer_id : UINT32_MAX);
263 continue;
264 }
265 if (option_types_found & OPT_P_UP)
266 {
267 update_vhash(m, curr_mi, old_ip, old_ipv6);
268 }
269 count++;
270 }
271
273 gc_free(&gc);
274 return count;
275}
276
277#define RETURN_UPDATE_STATUS(n_sent) \
278 do \
279 { \
280 if ((n_sent) > 0) \
281 { \
282 msg(M_CLIENT, "SUCCESS: %d client(s) updated", (n_sent)); \
283 return true; \
284 } \
285 else \
286 { \
287 msg(M_CLIENT, "ERROR: no client updated"); \
288 return false; \
289 } \
290 } while (0)
291
292
293bool
295{
296 int n_sent = send_push_update(arg, NULL, options, UPT_BROADCAST, PUSH_BUNDLE_SIZE);
297
298 RETURN_UPDATE_STATUS(n_sent);
299}
300
301bool
302management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
303{
304 int n_sent = send_push_update(arg, &cid, options, UPT_BY_CID, PUSH_BUNDLE_SIZE);
305
306 RETURN_UPDATE_STATUS(n_sent);
307}
308#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: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, int msglevel)
Definition forward.c:398
unsigned int pull_permission_mask(const struct context *c)
Definition init.c:2539
void hash_iterator_free(struct hash_iterator *hi)
Definition list.c:272
struct hash_element * hash_iterator_next(struct hash_iterator *hi)
Definition list.c:278
void hash_iterator_init(struct hash *hash, struct hash_iterator *hi)
Definition list.c:236
struct multi_instance * lookup_by_cid(struct multi_context *m, const unsigned long cid)
Definition multi.c:4002
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:4277
Header file for server-mode related structures and functions.
#define M_CLIENT
Definition error.h:106
#define msg(flags,...)
Definition error.h:150
#define M_WARN
Definition error.h:90
bool options_postprocess_pull(struct options *o, struct env_set *es)
Definition options.c:4285
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:5453
#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:94
static struct buffer forge_msg(const char *src, const char *continuation, struct gc_arena *gc)
Definition push_util.c:70
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:55
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:187
#define RETURN_UPDATE_STATUS(n_sent)
Definition push_util.c:277
bool management_callback_send_push_update_by_cid(void *arg, unsigned long cid, const char *options)
Definition push_util.c:302
bool management_callback_send_push_update_broadcast(void *arg, const char *options)
Definition push_util.c:294
static char * gc_strdup(const char *src, struct gc_arena *gc)
Definition push_util.c:82
static bool send_single_push_update(struct context *c, struct buffer *msgs, unsigned int *option_types_found)
Definition push_util.c:141
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:13
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:44
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:154