Bug fix for multi-stanza key use and replay attack detection
[fwknop.git] / server / incoming_spa.c
1 /*
2  *****************************************************************************
3  *
4  * File:    incoming_spa.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Process an incoming SPA data packet for fwknopd.
9  *
10  * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU 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 "netinet_common.h"
33
34 #if HAVE_SYS_WAIT_H
35   #include <sys/wait.h>
36 #endif
37
38 #include "incoming_spa.h"
39 #include "access.h"
40 #include "extcmd.h"
41 #include "log_msg.h"
42 #include "utils.h"
43 #include "fw_util.h"
44 #include "fwknopd_errors.h"
45 #include "replay_cache.h"
46
47 /* Validate and in some cases preprocess/reformat the SPA data.  Return an
48  * error code value if there is any indication the data is not valid spa data.
49 */
50 static int
51 preprocess_spa_data(fko_srv_options_t *opts, const char *src_ip)
52 {
53     spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
54
55     char    *ndx = (char *)&(spa_pkt->packet_data);
56     int      pkt_data_len = spa_pkt->packet_data_len;
57     int      i;
58
59     /* At this point, we can reset the packet data length to 0.  This is our
60      * indicator to the rest of the program that we do not have a current
61      * spa packet to process (after this one that is).
62     */
63     spa_pkt->packet_data_len = 0;
64
65     /* Detect and parse out SPA data from an HTTP request. If the SPA data
66      * starts with "GET /" and the user agent starts with "Fwknop", then
67      * assume it is a SPA over HTTP request.
68     */
69     if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "N", 1) == 0
70       && strncasecmp(ndx, "GET /", 5) == 0
71       && strstr(ndx, "User-Agent: Fwknop") != NULL)
72     {
73         /* This looks like an HTTP request, so let's see if we are
74          * configured to accept such request and if so, find the SPA
75          * data.
76         */
77
78         /* Now extract, adjust (convert characters translated by the fwknop
79          * client), and reset the SPA message itself.
80         */
81         strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len);
82
83         for(i=0; i<pkt_data_len; i++)
84         {
85             if(isspace(*ndx)) /* The first space marks the end of the req */
86             {
87                 *ndx = '\0';
88                 break;
89             }
90             else if(*ndx == '-') /* Convert '-' to '+' */
91                 *ndx = '+';
92             else if(*ndx == '_') /* Convert '_' to '/' */
93                 *ndx = '/';
94
95             ndx++;
96         }
97     }
98
99     /* Require base64-encoded data
100     */
101     if(! is_base64(spa_pkt->packet_data, pkt_data_len))
102         return(SPA_MSG_NOT_SPA_DATA);
103
104
105     /* --DSS:  Are there other checks we can do here ??? */
106
107     /* If we made it here, we have no reason to assume this is not SPA data
108      * (at least until we come up with more checks).
109     */
110     return(FKO_SUCCESS);
111 }
112
113 /* Popluate a spa_data struct from an initialized (and populated) FKO context.
114 */
115 static int
116 get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
117 {
118     int res = FKO_SUCCESS;
119
120     res = fko_get_username(ctx, &(spdat->username));
121     if(res != FKO_SUCCESS)
122         return(res);
123
124     res = fko_get_timestamp(ctx, &(spdat->timestamp));
125     if(res != FKO_SUCCESS)
126         return(res);
127
128     res = fko_get_version(ctx, &(spdat->version));
129     if(res != FKO_SUCCESS)
130         return(res);
131
132     res = fko_get_spa_message_type(ctx, &(spdat->message_type));
133     if(res != FKO_SUCCESS)
134         return(res);
135
136     res = fko_get_spa_message(ctx, &(spdat->spa_message));
137     if(res != FKO_SUCCESS)
138         return(res);
139
140     res = fko_get_spa_nat_access(ctx, &(spdat->nat_access));
141     if(res != FKO_SUCCESS)
142         return(res);
143
144     res = fko_get_spa_server_auth(ctx, &(spdat->server_auth));
145     if(res != FKO_SUCCESS)
146         return(res);
147
148     res = fko_get_spa_client_timeout(ctx, (int *)&(spdat->client_timeout));
149     if(res != FKO_SUCCESS)
150         return(res);
151
152     return(res);
153 }
154
155 /* Check for access.conf stanza SOURCE match based on SPA packet
156  * source IP
157 */
158 static int
159 is_src_match(acc_stanza_t *acc, const uint32_t ip)
160 {
161     while (acc)
162     {
163         if(compare_addr_list(acc->source_list, ip))
164             return 1;
165
166         acc = acc->next;
167     }
168     return 0;
169 }
170
171 /* Process the SPA packet data
172 */
173 void
174 incoming_spa(fko_srv_options_t *opts)
175 {
176     /* Always a good idea to initialize ctx to null if it will be used
177      * repeatedly (especially when using fko_new_with_data()).
178     */
179     fko_ctx_t       ctx = NULL;
180
181     char            *spa_ip_demark, *gpg_id;
182     time_t          now_ts;
183     int             res, status, ts_diff, enc_type, found_acc_sip=0, stanza_num=0;
184
185     spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
186
187     /* This will hold our pertinent SPA data.
188     */
189     spa_data_t spadat;
190
191     /* Loop through all access stanzas looking for a match
192     */
193     acc_stanza_t    *acc = opts->acc_stanzas;
194
195     inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
196         spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));
197
198     /* At this point, we want to validate and (if needed) preprocess the
199      * SPA data and/or to be reasonably sure we have a SPA packet (i.e
200      * try to eliminate obvious non-spa packets).
201     */
202     res = preprocess_spa_data(opts, spadat.pkt_source_ip);
203     if(res != FKO_SUCCESS)
204     {
205         if(opts->verbose > 1)
206             log_msg(LOG_INFO, "preprocess_spa_data() returned error %i: '%s' for incoming packet.",
207                 res, get_errstr(res));
208         return;
209     }
210
211     if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
212     {
213         if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
214             /* Check for a replay attack
215             */
216             if (is_replay(opts, spa_pkt->packet_data) != SPA_MSG_SUCCESS)
217                 return;
218     }
219     else
220     {
221         log_msg(LOG_WARNING,
222             "No access data found for source IP: %s", spadat.pkt_source_ip
223         );
224         return;
225     }
226
227     /* Now that we know there is a matching access.conf stanza and the
228      * incoming SPA packet is not a replay, see if we should grant any
229      * access
230     */
231     while(acc)
232     {
233         stanza_num++;
234
235         /* Check for a match for the SPA source IP and the access stanza
236         */
237         if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)))
238         {
239             acc = acc->next;
240             continue;
241         }
242
243         found_acc_sip = 1;
244
245         log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
246             stanza_num, spadat.pkt_source_ip);
247
248         if(opts->verbose > 1)
249             log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data);
250
251         /* Make sure this access stanza has not expired
252         */
253         if(acc->access_expire_time > 0)
254         {
255             if(acc->expired)
256             {
257                 acc = acc->next;
258                 continue;
259             }
260             else
261             {
262                 if(time(NULL) > acc->access_expire_time)
263                 {
264                     log_msg(LOG_INFO, "(stanza #%d) Access stanza has expired",
265                         stanza_num);
266                     acc->expired = 1;
267                     acc = acc->next;
268                     continue;
269                 }
270             }
271         }
272
273         /* Get encryption type and try its decoding routine first (if the key
274          * for that type is set)
275         */
276         enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
277
278         if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
279         {
280             if(acc->key != NULL)
281                 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
282             else
283             {
284                 log_msg(LOG_ERR,
285                     "(stanza #%d) No KEY for RIJNDAEL encrypted messages",
286                     stanza_num
287                 );
288                 acc = acc->next;
289                 continue;
290             }
291         }
292         else if(enc_type == FKO_ENCRYPTION_GPG)
293         {
294             /* For GPG we create the new context without decrypting on the fly
295              * so we can set some  GPG parameters first.
296             */
297             if(acc->gpg_decrypt_pw != NULL)
298             {
299                 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
300                 if(res != FKO_SUCCESS)
301                 {
302                     log_msg(LOG_WARNING,
303                         "(stanza #%d) Error creating fko context (before decryption): %s",
304                         stanza_num, fko_errstr(res)
305                     );
306                     acc = acc->next;
307                     continue;
308                 }
309
310                 /* Set whatever GPG parameters we have.
311                 */
312                 if(acc->gpg_home_dir != NULL)
313                     res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
314                     if(res != FKO_SUCCESS)
315                     {
316                         log_msg(LOG_WARNING,
317                             "(stanza #%d) Error setting GPG keyring path to %s: %s",
318                             stanza_num, acc->gpg_home_dir, fko_errstr(res)
319                         );
320                         acc = acc->next;
321                         continue;
322                     }
323
324                 if(acc->gpg_decrypt_id != NULL)
325                     fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);
326
327                 /* If GPG_REQUIRE_SIG is set for this acc stanza, then set
328                  * the FKO context accordingly and check the other GPG Sig-
329                  * related parameters. This also applies when REMOTE_ID is
330                  * set.
331                 */
332                 if(acc->gpg_require_sig)
333                 {
334                     fko_set_gpg_signature_verify(ctx, 1);
335
336                     /* Set whether or not to ignore signature verification errors.
337                     */
338                     fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error);
339                 }
340                 else
341                 {
342                     fko_set_gpg_signature_verify(ctx, 0);
343                     fko_set_gpg_ignore_verify_error(ctx, 1);
344                 }
345
346                 /* Now decrypt the data.
347                 */
348                 res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw);
349
350             }
351             else
352             {
353                 log_msg(LOG_ERR,
354                     "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages",
355                     stanza_num
356                 );
357                 acc = acc->next;
358                 continue;
359             }
360         }
361         else
362         {
363             log_msg(LOG_ERR, "(stanza #%d) Unable to determing encryption type. Got type=%i.",
364                 stanza_num, enc_type);
365             acc = acc->next;
366             continue;
367         }
368
369         /* Do we have a valid FKO context?
370         */
371         if(res != FKO_SUCCESS)
372         {
373             log_msg(LOG_WARNING, "(stanza #%d) Error creating fko context: %s",
374                 stanza_num, fko_errstr(res));
375
376             if(IS_GPG_ERROR(res))
377                 log_msg(LOG_WARNING, "(stanza #%d) - GPG ERROR: %s",
378                     stanza_num, fko_gpg_errstr(ctx));
379
380             if(ctx != NULL)
381                 fko_destroy(ctx);
382             acc = acc->next;
383             continue;
384         }
385
386         /* At this point, we assume the SPA data is valid.  Now we need to see
387          * if it meets our access criteria.
388         */
389         if(opts->verbose > 1)
390             log_msg(LOG_INFO, "(stanza #%d) SPA Decode (res=%i):\n%s",
391                 stanza_num, res, dump_ctx(ctx));
392
393         /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
394          * then we need to make sure this incoming message is signer ID matches
395          * an entry in the list.
396         */
397         if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig)
398         {
399             res = fko_get_gpg_signature_id(ctx, &gpg_id);
400             if(res != FKO_SUCCESS)
401             {
402                 log_msg(LOG_WARNING, "(stanza #%d) Error pulling the GPG signature ID from the context: %s",
403                     stanza_num, fko_gpg_errstr(ctx));
404                 if(ctx != NULL)
405                     fko_destroy(ctx);
406                 acc = acc->next;
407                 continue;
408             }
409
410             if(opts->verbose)
411                 log_msg(LOG_INFO, "(stanza #%d) Incoming SPA data signed by '%s'.",
412                     stanza_num, gpg_id);
413
414             if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id))
415             {
416                 log_msg(LOG_WARNING,
417                     "(stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
418                     stanza_num, gpg_id);
419                 if(ctx != NULL)
420                     fko_destroy(ctx);
421                 acc = acc->next;
422                 continue;
423             }
424         }
425
426         /* Populate our spa data struct for future reference.
427         */
428         res = get_spa_data_fields(ctx, &spadat);
429
430         /* Figure out what our timeout will be. If it is specified in the SPA
431          * data, then use that.  If not, try the FW_ACCESS_TIMEOUT from the
432          * access.conf file (if there is one).  Otherwise use the default.
433         */
434         if(spadat.client_timeout > 0)
435             spadat.fw_access_timeout = spadat.client_timeout;
436         else if(acc->fw_access_timeout > 0)
437             spadat.fw_access_timeout = acc->fw_access_timeout;
438         else
439             spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
440
441         if(res != FKO_SUCCESS)
442         {
443             log_msg(LOG_ERR, "(stanza #%d) Unexpected error pulling SPA data from the context: %s",
444                 stanza_num, fko_errstr(res));
445
446             if(ctx != NULL)
447                 fko_destroy(ctx);
448             acc = acc->next;
449             continue;
450         }
451
452         /* Check packet age if so configured.
453         */
454         if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
455         {
456             time(&now_ts);
457
458             ts_diff = abs(now_ts - spadat.timestamp);
459
460             if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
461             {
462                 log_msg(LOG_WARNING, "(stanza #%d) SPA data time difference is too great (%i seconds).",
463                     stanza_num, ts_diff);
464
465                 if(ctx != NULL)
466                     fko_destroy(ctx);
467                 acc = acc->next;
468                 continue;
469             }
470         }
471
472         /* At this point, we have enough to check the embedded (or packet source)
473          * IP address against the defined access rights.  We start by splitting
474          * the spa msg source IP from the remainder of the message.
475         */
476         spa_ip_demark = strchr(spadat.spa_message, ',');
477         if(spa_ip_demark == NULL)
478         {
479             log_msg(LOG_WARNING, "(stanza #%d) Error parsing SPA message string: %s",
480                 stanza_num, fko_errstr(res));
481
482             if(ctx != NULL)
483                 fko_destroy(ctx);
484             acc = acc->next;
485             continue;
486         }
487
488         strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);
489         strlcpy(spadat.spa_message_remain, spa_ip_demark+1, 1024);
490
491         /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
492          * is allowed.
493         */
494         if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0)
495         {
496             if(acc->require_source_address)
497             {
498                 log_msg(LOG_WARNING,
499                     "(stanza #%d) Got 0.0.0.0 when valid source IP was required.",
500                     stanza_num
501                 );
502
503                 if(ctx != NULL)
504                     fko_destroy(ctx);
505                 acc = acc->next;
506                 continue;
507             }
508
509             spadat.use_src_ip = spadat.pkt_source_ip;
510         }
511         else
512             spadat.use_src_ip = spadat.spa_message_src_ip;
513
514         /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
515          * matches.
516         */
517         if(acc->require_username != NULL)
518         {
519             if(strcmp(spadat.username, acc->require_username) != 0)
520             {
521                 log_msg(LOG_WARNING,
522                     "(stanza #%d) Username in SPA data (%s) does not match required username: %s",
523                     stanza_num, spadat.username, acc->require_username
524                 );
525
526                 if(ctx != NULL)
527                     fko_destroy(ctx);
528                 acc = acc->next;
529                 continue;
530             }
531         }
532
533         /* Take action based on SPA message type.
534         */
535         if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG
536               || spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
537               || spadat.message_type == FKO_NAT_ACCESS_MSG
538               || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG)
539         {
540 #if FIREWALL_IPTABLES
541             if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0)
542             {
543                 log_msg(LOG_WARNING,
544                     "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled",
545                     stanza_num, spadat.pkt_source_ip
546                 );
547
548                 if(ctx != NULL)
549                     fko_destroy(ctx);
550                 acc = acc->next;
551                 continue;
552             }
553 #else
554             log_msg(LOG_WARNING,
555                 "(stanza #%d) SPA packet from %s requested unsupported NAT access",
556                 stanza_num, spadat.pkt_source_ip
557             );
558
559             if(ctx != NULL)
560                 fko_destroy(ctx);
561             acc = acc->next;
562             continue;
563 #endif
564         }
565
566         /* Command messages.
567         */
568         if(spadat.message_type == FKO_COMMAND_MSG)
569         {
570             if(!acc->enable_cmd_exec)
571             {
572                 log_msg(LOG_WARNING,
573                     "(stanza #%d) SPA Command message are not allowed in the current configuration.",
574                     stanza_num
575                 );
576
577                 if(ctx != NULL)
578                     fko_destroy(ctx);
579                 acc = acc->next;
580                 continue;
581             }
582             else
583             {
584                 log_msg(LOG_INFO,
585                     "(stanza #%d) Processing SPA Command message: command='%s'.",
586                     stanza_num, spadat.spa_message_remain
587                 );
588
589                 /* Do we need to become another user? If so, we call
590                  * run_extcmd_as and pass the cmd_exec_uid.
591                 */
592                 if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
593                 {
594                     if(opts->verbose)
595                         log_msg(LOG_INFO, "(stanza #%d) Setting effective user to %s (UID=%i) before running command.",
596                             stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid);
597
598
599                     res = run_extcmd_as(acc->cmd_exec_uid,
600                                         spadat.spa_message_remain, NULL, 0, 0);
601                 }
602                 else /* Just run it as we are (root that is). */
603                     res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5);
604
605                 /* --DSS XXX: I have found that the status (and res for that
606                  *            matter) have been unreliable indicators of the
607                  *            actual exit status of some commands.  Not sure
608                  *            why yet.  For now, we will take what we get.
609                 */
610                 status = WEXITSTATUS(res);
611
612                 if(opts->verbose > 1)
613                     log_msg(LOG_WARNING,
614                         "(stanza #%d) CMD_EXEC: command returned %i",
615                         stanza_num, status);
616
617                 if(status != 0)
618                     res = SPA_MSG_COMMAND_ERROR;
619
620                 if(ctx != NULL)
621                     fko_destroy(ctx);
622
623                 /* we processed the command on a matching access stanza, so we
624                  * don't look for anything else to do with this SPA packet
625                 */
626                 break;
627             }
628         }
629
630         /* From this point forward, we have some kind of access message. So
631          * we first see if access is allowed by checking access against
632          * restrict_ports and open_ports.
633          *
634          *  --DSS TODO: We should add BLACKLIST support here as well.
635         */
636         if(! acc_check_port_access(acc, spadat.spa_message_remain))
637         {
638             log_msg(LOG_WARNING,
639                 "(stanza #%d) One or more requested protocol/ports was denied per access.conf.",
640                 stanza_num
641             );
642
643             if(ctx != NULL)
644                 fko_destroy(ctx);
645             acc = acc->next;
646             continue;
647         }
648
649         /* At this point, we process the SPA request and break out of the
650          * access stanza loop (first valid access stanza stops us looking
651          * for others).
652         */
653         process_spa_request(opts, acc, &spadat);
654         if(ctx != NULL)
655             fko_destroy(ctx);
656         break;
657     }
658
659     if(! found_acc_sip)
660     {
661         log_msg(LOG_WARNING,
662             "No access data found for source IP: %s", spadat.pkt_source_ip
663         );
664     }
665
666     return;
667 }
668
669 /***EOF***/