Branch data Line data Source code
1 : : /**
2 : : * \file server/cmd_cycle.c
3 : : *
4 : : * \brief Fwknop routines for managing command cycles as defined via
5 : : * access.conf stanzas (CMD_CYCLE_OPEN and CMD_CYCLE_CLOSE).
6 : : */
7 : :
8 : : /* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2015 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : */
30 : :
31 : : #include "fwknopd_common.h"
32 : : #include "log_msg.h"
33 : : #include "extcmd.h"
34 : : #include "cmd_cycle.h"
35 : : #include "access.h"
36 : :
37 : : static char cmd_buf[CMD_CYCLE_BUFSIZE];
38 : : static char err_buf[CMD_CYCLE_BUFSIZE];
39 : :
40 : : static void
41 : 1321 : zero_cmd_buffers(void)
42 : : {
43 : : memset(cmd_buf, 0x0, CMD_CYCLE_BUFSIZE);
44 : : memset(err_buf, 0x0, CMD_CYCLE_BUFSIZE);
45 : 1321 : return;
46 : : }
47 : :
48 : : static int pid_status = 0;
49 : :
50 : : static int
51 : 2579 : is_var(const char * const var, const char * const cmd_str)
52 : : {
53 : : int i;
54 [ + + ]: 5268 : for(i=0; i < strlen(var); i++)
55 : : {
56 [ + + ]: 4369 : if(cmd_str[i] != var[i])
57 : : return 0;
58 : : }
59 : : return 1;
60 : : }
61 : :
62 : : static int
63 : 883 : build_cmd(spa_data_t *spadat, const char * const cmd_cycle_str, int timer)
64 : : {
65 : 883 : char port_str[MAX_PORT_STR_LEN+1] = {0};
66 : 883 : char proto_str[MAX_PROTO_STR_LEN+1] = {0};
67 : 883 : char timestamp_str[20] = {0};
68 : 883 : char client_timeout_str[10] = {0};
69 : 883 : acc_port_list_t *port_list = NULL;
70 : 883 : int i=0, buf_idx=0;
71 : :
72 : : #if HAVE_LIBFIU
73 [ + - ]: 883 : fiu_return_on("cmd_cycle_build_err", 0);
74 : : #endif
75 : :
76 [ - + ]: 883 : if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
77 : : {
78 : 0 : free_acc_port_list(port_list);
79 : 0 : return 0;
80 : : }
81 : :
82 : : /* We only look at the first port/proto combination for command
83 : : * open/close cycles even if the SPA message had multiple ports
84 : : * and protocols set.
85 : : */
86 : 883 : snprintf(port_str, MAX_PORT_STR_LEN+1, "%d", port_list->port);
87 : 883 : snprintf(proto_str, MAX_PROTO_STR_LEN+1, "%d", port_list->proto);
88 : :
89 : 883 : zero_cmd_buffers();
90 : :
91 : : /* Look for the following variables for substitution:
92 : : * IP, SRC, PKT_SRC, DST, PORT, and PROTO
93 : : */
94 [ + + ]: 25543 : for(i=0; i < strnlen(cmd_cycle_str, CMD_CYCLE_BUFSIZE); i++)
95 : : {
96 [ + + ]: 24660 : if(cmd_cycle_str[i] == '$')
97 : : {
98 : : /* Found the start of a variable, now validate it and
99 : : * swap in the IP/port/proto.
100 : : */
101 [ + + ]: 899 : if(is_var("IP", (cmd_cycle_str+i+1)))
102 : : {
103 : 70 : strlcat(cmd_buf, spadat->use_src_ip,
104 : : CMD_CYCLE_BUFSIZE);
105 : 70 : i += strlen("IP");
106 : 70 : buf_idx += strlen(spadat->use_src_ip);
107 : : }
108 : : /* SRC is a synonym for IP
109 : : */
110 [ + + ]: 829 : else if(is_var("SRC", (cmd_cycle_str+i+1)))
111 : : {
112 : 411 : strlcat(cmd_buf, spadat->use_src_ip,
113 : : CMD_CYCLE_BUFSIZE);
114 : 411 : i += strlen("SRC");
115 : 411 : buf_idx += strlen(spadat->use_src_ip);
116 : : }
117 : : /* Special case for the SPA packet source IP in
118 : : * the IP header (i.e. not from the decrypted SPA
119 : : * payload) if the user really wants this.
120 : : */
121 [ + + ]: 418 : else if(is_var("PKT_SRC", (cmd_cycle_str+i+1)))
122 : : {
123 : 5 : strlcat(cmd_buf, spadat->pkt_source_ip,
124 : : CMD_CYCLE_BUFSIZE);
125 : 5 : i += strlen("PKT_SRC");
126 : 5 : buf_idx += strlen(spadat->pkt_source_ip);
127 : : }
128 [ + + ]: 413 : else if(is_var("DST", (cmd_cycle_str+i+1)))
129 : : {
130 : 405 : strlcat(cmd_buf, spadat->pkt_destination_ip,
131 : : CMD_CYCLE_BUFSIZE);
132 : 405 : i += strlen("DST");
133 : 405 : buf_idx += strlen(spadat->pkt_destination_ip);
134 : : }
135 [ + + ]: 8 : else if (is_var("PORT", (cmd_cycle_str+i+1)))
136 : : {
137 : 2 : strlcat(cmd_buf, port_str, CMD_CYCLE_BUFSIZE);
138 : 2 : i += strlen("PORT");
139 : 2 : buf_idx += strlen(port_str);
140 : : }
141 [ + + ]: 6 : else if (is_var("PROTO", (cmd_cycle_str+i+1)))
142 : : {
143 : 2 : strlcat(cmd_buf, proto_str, CMD_CYCLE_BUFSIZE);
144 : 2 : i += strlen("PROTO");
145 : 2 : buf_idx += strlen(proto_str);
146 : : }
147 [ + + ]: 4 : else if (is_var("TIMEOUT", (cmd_cycle_str+i+1)))
148 : : {
149 [ - + ]: 2 : snprintf(timestamp_str, sizeof(timestamp_str), "%lli",
150 : 2 : (long long)spadat->timestamp +
151 : 2 : (spadat->client_timeout == 0 ? timer :
152 : : spadat->client_timeout));
153 : 2 : strlcat(cmd_buf, timestamp_str, CMD_CYCLE_BUFSIZE);
154 : 2 : i += strlen("TIMEOUT");
155 : 2 : buf_idx += strlen(timestamp_str);
156 : : }
157 [ + - ]: 2 : else if (is_var("CLIENT_TIMEOUT", (cmd_cycle_str+i+1)))
158 : : {
159 [ - + ]: 2 : snprintf(client_timeout_str, sizeof(client_timeout_str), "%u",
160 : 2 : spadat->client_timeout == 0 ? timer :
161 : : spadat->client_timeout);
162 : 2 : strlcat(cmd_buf, client_timeout_str, CMD_CYCLE_BUFSIZE);
163 : 2 : i += strlen("CLIENT_TIMEOUT");
164 : 2 : buf_idx += strlen(client_timeout_str);
165 : : }
166 : 899 : continue;
167 : : }
168 [ + - ]: 23761 : if(cmd_cycle_str[i] != '\0')
169 : 23761 : cmd_buf[buf_idx++] = cmd_cycle_str[i];
170 [ - + ]: 23761 : if(buf_idx == CMD_CYCLE_BUFSIZE)
171 : : {
172 : 0 : free_acc_port_list(port_list);
173 : 0 : return 0;
174 : : }
175 : : }
176 : :
177 : 883 : free_acc_port_list(port_list);
178 : 883 : return 1;
179 : : }
180 : :
181 : : static int
182 : 884 : cmd_open(fko_srv_options_t *opts, acc_stanza_t *acc,
183 : : spa_data_t *spadat, const int stanza_num)
184 : : {
185 : : /* CMD_CYCLE_OPEN: Build the open command by taking care of variable
186 : : * substitutions if necessary.
187 : : */
188 [ + - ]: 442 : if(build_cmd(spadat, acc->cmd_cycle_open, acc->cmd_cycle_timer))
189 : : {
190 : 442 : log_msg(LOG_INFO, "[%s] (stanza #%d) Running CMD_CYCLE_OPEN command: %s",
191 : 442 : spadat->pkt_source_ip, stanza_num, cmd_buf);
192 : :
193 : : /* Run the open command
194 : : */
195 : 442 : run_extcmd(cmd_buf, err_buf, CMD_CYCLE_BUFSIZE,
196 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
197 : : }
198 : : else
199 : : {
200 : 0 : log_msg(LOG_ERR,
201 : : "[%s] (stanza #%d) Could not build CMD_CYCLE_OPEN command.",
202 : 0 : spadat->pkt_source_ip, stanza_num
203 : : );
204 : : return 0;
205 : : }
206 : : return 1;
207 : : }
208 : :
209 : : static int
210 : 441 : add_cmd_close(fko_srv_options_t *opts, acc_stanza_t *acc,
211 : : spa_data_t *spadat, const int stanza_num)
212 : : {
213 : 441 : cmd_cycle_list_t *last_clist=NULL, *new_clist=NULL, *tmp_clist=NULL;
214 : : time_t now;
215 : 441 : int cmd_close_len = 0;
216 : :
217 : : /* CMD_CYCLE_CLOSE: Build the close command, but don't execute it until
218 : : * the expiration timer has passed.
219 : : */
220 [ + - ]: 441 : if(build_cmd(spadat, acc->cmd_cycle_close, acc->cmd_cycle_timer))
221 : : {
222 : : /* Now the corresponding close command is now in cmd_buf
223 : : * for later execution when the timer expires.
224 : : */
225 : 441 : cmd_close_len = strnlen(cmd_buf, CMD_CYCLE_BUFSIZE-1)+1;
226 [ + + ]: 441 : log_msg(LOG_INFO,
227 : : "[%s] (stanza #%d) Running CMD_CYCLE_CLOSE command in %d seconds: %s",
228 : 441 : spadat->pkt_source_ip, stanza_num,
229 : 877 : (spadat->client_timeout == 0 ? acc->cmd_cycle_timer :
230 : : spadat->client_timeout), cmd_buf);
231 : : }
232 : : else
233 : : {
234 : 0 : log_msg(LOG_ERR,
235 : : "[%s] (stanza #%d) Could not build CMD_CYCLE_CLOSE command.",
236 : 0 : spadat->pkt_source_ip, stanza_num
237 : : );
238 : : return 0;
239 : : }
240 : :
241 : : /* Add the corresponding close command - to be executed after the
242 : : * designated timer has expired.
243 : : */
244 [ - + ]: 441 : if((new_clist = calloc(1, sizeof(cmd_cycle_list_t))) == NULL)
245 : : {
246 : 0 : log_msg(LOG_ERR,
247 : : "[*] Fatal memory allocation error creating string list entry"
248 : : );
249 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
250 : : }
251 : :
252 [ + + ]: 441 : if(opts->cmd_cycle_list == NULL)
253 : : {
254 : 441 : opts->cmd_cycle_list = new_clist;
255 : : }
256 : : else
257 : : {
258 : : tmp_clist = opts->cmd_cycle_list;
259 : :
260 : : do {
261 : 8886 : last_clist = tmp_clist;
262 [ + + ]: 8886 : } while((tmp_clist = tmp_clist->next));
263 : :
264 : 403 : last_clist->next = new_clist;
265 : : }
266 : :
267 : : /* Set the source IP
268 : : */
269 : 441 : strlcpy(new_clist->src_ip, spadat->use_src_ip,
270 : : sizeof(new_clist->src_ip));
271 : :
272 : : /* Set the expiration timer
273 : : */
274 : 441 : time(&now);
275 [ + + ]: 441 : new_clist->expire = now + (spadat->client_timeout == 0 ?
276 : 436 : acc->cmd_cycle_timer : spadat->client_timeout);
277 : :
278 : : /* Set the close command
279 : : */
280 [ - + ]: 441 : if((new_clist->close_cmd = calloc(1, cmd_close_len)) == NULL)
281 : : {
282 : 0 : log_msg(LOG_ERR,
283 : : "[*] Fatal memory allocation error creating command close string"
284 : : );
285 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
286 : : }
287 : 441 : strlcpy(new_clist->close_cmd, cmd_buf, cmd_close_len);
288 : :
289 : : /* Set the access.conf stanza number
290 : : */
291 : 441 : new_clist->stanza_num = stanza_num;
292 : :
293 : : return 1;
294 : : }
295 : :
296 : : /* This is the main driver for open/close command cycles
297 : : */
298 : : int
299 : 442 : cmd_cycle_open(fko_srv_options_t *opts, acc_stanza_t *acc,
300 : : spa_data_t *spadat, const int stanza_num, int *res)
301 : : {
302 [ + - ]: 442 : if(! cmd_open(opts, acc, spadat, stanza_num))
303 : : return 0;
304 : :
305 [ + + ]: 442 : if(acc->cmd_cycle_do_close)
306 [ + - ]: 441 : if(! add_cmd_close(opts, acc, spadat, stanza_num))
307 : : return 0;
308 : :
309 : : return 1;
310 : : }
311 : :
312 : : static void
313 : 441 : free_cycle_list_node(cmd_cycle_list_t *list_node)
314 : : {
315 [ + - ]: 441 : if(list_node != NULL)
316 : : {
317 [ + - ]: 441 : if(list_node->close_cmd != NULL)
318 : 441 : free(list_node->close_cmd);
319 : 441 : free(list_node);
320 : : }
321 : 441 : return;
322 : : }
323 : :
324 : : /* Run all close commands based on the expiration timer
325 : : */
326 : : void
327 : 59120 : cmd_cycle_close(fko_srv_options_t *opts)
328 : : {
329 : 59120 : cmd_cycle_list_t *curr=NULL, *prev=NULL;
330 : 59120 : int do_delete=1;
331 : : time_t now;
332 : :
333 : 59120 : time(&now);
334 : :
335 [ + + ]: 59120 : if(opts->cmd_cycle_list == NULL)
336 : : {
337 : : return; /* No active command cycles */
338 : : }
339 : : else
340 : : {
341 [ + + ]: 2102 : while(do_delete)
342 : : {
343 : 1270 : do_delete = 0;
344 : :
345 : : /* Keep going through the command list for as long as
346 : : * there are commands to be executed (and expired).
347 : : */
348 [ + + ]: 16833 : for(curr = opts->cmd_cycle_list;
349 : : curr != NULL;
350 : 15125 : prev = curr, curr=curr->next)
351 : : {
352 [ + + ]: 15563 : if(curr->expire <= now)
353 : : {
354 : 438 : log_msg(LOG_INFO,
355 : : "[%s] (stanza #%d) Timer expired, running CMD_CYCLE_CLOSE command: %s",
356 : 438 : curr->src_ip, curr->stanza_num,
357 : : curr->close_cmd);
358 : :
359 : 438 : zero_cmd_buffers();
360 : :
361 : : /* Run the close command
362 : : */
363 : 438 : run_extcmd(curr->close_cmd, err_buf, CMD_CYCLE_BUFSIZE,
364 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
365 : :
366 [ + + ]: 438 : if(prev == NULL)
367 : 162 : opts->cmd_cycle_list = curr->next;
368 : : else
369 : 276 : prev->next = curr->next;
370 : :
371 : 438 : free_cycle_list_node(curr);
372 : 438 : do_delete = 1;
373 : 438 : break;
374 : : }
375 : : }
376 : : }
377 : : }
378 : :
379 : : return;
380 : : }
381 : :
382 : : void
383 : 7114 : free_cmd_cycle_list(fko_srv_options_t *opts)
384 : : {
385 : 7114 : cmd_cycle_list_t *tmp_clist=NULL, *clist=NULL;
386 : :
387 : 7114 : clist = opts->cmd_cycle_list;
388 : :
389 [ + + ]: 7117 : while(clist != NULL)
390 : : {
391 : 3 : tmp_clist = clist->next;
392 : 3 : free_cycle_list_node(clist);
393 : 3 : clist = tmp_clist;
394 : : }
395 : 7114 : return;
396 : : }
|