2 *****************************************************************************
6 * Author: Damien S. Stuart
8 * Purpose: Process an incoming SPA data packet for fwknopd.
10 * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
12 * License (GNU Public License):
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.
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.
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
29 *****************************************************************************
31 #include "fwknopd_common.h"
32 #include "netinet_common.h"
38 #include "incoming_spa.h"
44 #include "fwknopd_errors.h"
45 #include "replay_cache.h"
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.
51 preprocess_spa_data(fko_srv_options_t *opts, const char *src_ip)
53 spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
55 char *ndx = (char *)&(spa_pkt->packet_data);
56 int pkt_data_len = spa_pkt->packet_data_len;
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).
63 spa_pkt->packet_data_len = 0;
65 /* Ignore any SPA packets that contain the Rijndael or GnuPG prefixes
66 * since an attacker might have tacked them on to a previously seen
67 * SPA packet in an attempt to get past the replay check. And, we're
68 * no worse off since a legitimate SPA packet that happens to include
69 * a prefix after the outer one is stripped off won't decrypt properly
70 * anyway because libfko would not add a new one.
72 if(strncmp(ndx, B64_RIJNDAEL_SALT, B64_RIJNDAEL_SALT_STR_LEN) == 0)
73 return(SPA_MSG_BAD_DATA);
75 if(pkt_data_len > MIN_GNUPG_MSG_SIZE
76 && strncmp(ndx, B64_GPG_PREFIX, B64_GPG_PREFIX_STR_LEN) == 0)
77 return(SPA_MSG_BAD_DATA);
79 /* Detect and parse out SPA data from an HTTP request. If the SPA data
80 * starts with "GET /" and the user agent starts with "Fwknop", then
81 * assume it is a SPA over HTTP request.
83 if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "N", 1) == 0
84 && strncasecmp(ndx, "GET /", 5) == 0
85 && strstr(ndx, "User-Agent: Fwknop") != NULL)
87 /* This looks like an HTTP request, so let's see if we are
88 * configured to accept such request and if so, find the SPA
92 /* Now extract, adjust (convert characters translated by the fwknop
93 * client), and reset the SPA message itself.
95 strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len);
97 for(i=0; i<pkt_data_len; i++)
99 if(isspace(*ndx)) /* The first space marks the end of the req */
104 else if(*ndx == '-') /* Convert '-' to '+' */
106 else if(*ndx == '_') /* Convert '_' to '/' */
113 /* Require base64-encoded data
115 if(! is_base64(spa_pkt->packet_data, pkt_data_len))
116 return(SPA_MSG_NOT_SPA_DATA);
119 /* --DSS: Are there other checks we can do here ??? */
121 /* If we made it here, we have no reason to assume this is not SPA data
122 * (at least until we come up with more checks).
127 /* For replay attack detection
130 get_raw_digest(char **digest, char *pkt_data)
132 fko_ctx_t ctx = NULL;
133 char *tmp_digest = NULL;
134 int res = FKO_SUCCESS;
136 /* initialize an FKO context with no decryption key just so
137 * we can get the outer message digest
139 res = fko_new_with_data(&ctx, (char *)pkt_data, NULL);
140 if(res != FKO_SUCCESS)
142 log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s",
145 return(SPA_MSG_FKO_CTX_ERROR);
148 res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST);
149 if(res != FKO_SUCCESS)
151 log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s",
154 return(SPA_MSG_DIGEST_ERROR);
157 res = fko_set_raw_spa_digest(ctx);
158 if(res != FKO_SUCCESS)
160 log_msg(LOG_WARNING, "Error setting digest for SPA data: %s",
163 return(SPA_MSG_DIGEST_ERROR);
166 res = fko_get_raw_spa_digest(ctx, &tmp_digest);
167 if(res != FKO_SUCCESS)
169 log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
172 return(SPA_MSG_DIGEST_ERROR);
175 *digest = strdup(tmp_digest);
178 return SPA_MSG_ERROR;
186 /* Popluate a spa_data struct from an initialized (and populated) FKO context.
189 get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
191 int res = FKO_SUCCESS;
193 res = fko_get_username(ctx, &(spdat->username));
194 if(res != FKO_SUCCESS)
197 res = fko_get_timestamp(ctx, &(spdat->timestamp));
198 if(res != FKO_SUCCESS)
201 res = fko_get_version(ctx, &(spdat->version));
202 if(res != FKO_SUCCESS)
205 res = fko_get_spa_message_type(ctx, &(spdat->message_type));
206 if(res != FKO_SUCCESS)
209 res = fko_get_spa_message(ctx, &(spdat->spa_message));
210 if(res != FKO_SUCCESS)
213 res = fko_get_spa_nat_access(ctx, &(spdat->nat_access));
214 if(res != FKO_SUCCESS)
217 res = fko_get_spa_server_auth(ctx, &(spdat->server_auth));
218 if(res != FKO_SUCCESS)
221 res = fko_get_spa_client_timeout(ctx, (int *)&(spdat->client_timeout));
222 if(res != FKO_SUCCESS)
228 /* Check for access.conf stanza SOURCE match based on SPA packet
232 is_src_match(acc_stanza_t *acc, const uint32_t ip)
236 if(compare_addr_list(acc->source_list, ip))
244 /* Process the SPA packet data
247 incoming_spa(fko_srv_options_t *opts)
249 /* Always a good idea to initialize ctx to null if it will be used
250 * repeatedly (especially when using fko_new_with_data()).
252 fko_ctx_t ctx = NULL;
254 char *spa_ip_demark, *gpg_id, *raw_digest = NULL;
256 int res, status, ts_diff, enc_type, stanza_num=0;
257 int added_replay_digest = 0;
259 spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
261 /* This will hold our pertinent SPA data.
265 /* Loop through all access stanzas looking for a match
267 acc_stanza_t *acc = opts->acc_stanzas;
269 inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
270 spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));
272 /* At this point, we want to validate and (if needed) preprocess the
273 * SPA data and/or to be reasonably sure we have a SPA packet (i.e
274 * try to eliminate obvious non-spa packets).
276 res = preprocess_spa_data(opts, spadat.pkt_source_ip);
277 if(res != FKO_SUCCESS)
279 if(opts->verbose > 1)
280 log_msg(LOG_INFO, "preprocess_spa_data() returned error %i: '%s' for incoming packet.",
281 res, get_errstr(res));
285 if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
287 if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
288 /* Check for a replay attack
290 res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data);
291 if(res != FKO_SUCCESS)
293 if (raw_digest != NULL)
297 if (raw_digest == NULL)
300 if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS)
309 "No access data found for source IP: %s", spadat.pkt_source_ip
314 /* Now that we know there is a matching access.conf stanza and the
315 * incoming SPA packet is not a replay, see if we should grant any
322 /* Check for a match for the SPA source IP and the access stanza
324 if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)))
330 log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
331 stanza_num, spadat.pkt_source_ip);
333 if(opts->verbose > 1)
334 log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data);
336 /* Make sure this access stanza has not expired
338 if(acc->access_expire_time > 0)
347 if(time(NULL) > acc->access_expire_time)
349 log_msg(LOG_INFO, "(stanza #%d) Access stanza has expired",
358 /* Get encryption type and try its decoding routine first (if the key
359 * for that type is set)
361 enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
363 if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
366 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
370 "(stanza #%d) No KEY for RIJNDAEL encrypted messages",
377 else if(enc_type == FKO_ENCRYPTION_GPG)
379 /* For GPG we create the new context without decrypting on the fly
380 * so we can set some GPG parameters first.
382 if(acc->gpg_decrypt_pw != NULL)
384 res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
385 if(res != FKO_SUCCESS)
388 "(stanza #%d) Error creating fko context (before decryption): %s",
389 stanza_num, fko_errstr(res)
395 /* Set whatever GPG parameters we have.
397 if(acc->gpg_home_dir != NULL)
398 res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
399 if(res != FKO_SUCCESS)
402 "(stanza #%d) Error setting GPG keyring path to %s: %s",
403 stanza_num, acc->gpg_home_dir, fko_errstr(res)
409 if(acc->gpg_decrypt_id != NULL)
410 fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);
412 /* If GPG_REQUIRE_SIG is set for this acc stanza, then set
413 * the FKO context accordingly and check the other GPG Sig-
414 * related parameters. This also applies when REMOTE_ID is
417 if(acc->gpg_require_sig)
419 fko_set_gpg_signature_verify(ctx, 1);
421 /* Set whether or not to ignore signature verification errors.
423 fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error);
427 fko_set_gpg_signature_verify(ctx, 0);
428 fko_set_gpg_ignore_verify_error(ctx, 1);
431 /* Now decrypt the data.
433 res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw);
439 "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages",
448 log_msg(LOG_ERR, "(stanza #%d) Unable to determing encryption type. Got type=%i.",
449 stanza_num, enc_type);
454 /* Do we have a valid FKO context? Did the SPA decrypt properly?
456 if(res != FKO_SUCCESS)
458 log_msg(LOG_WARNING, "(stanza #%d) Error creating fko context: %s",
459 stanza_num, fko_errstr(res));
461 if(IS_GPG_ERROR(res))
462 log_msg(LOG_WARNING, "(stanza #%d) - GPG ERROR: %s",
463 stanza_num, fko_gpg_errstr(ctx));
471 /* Add this SPA packet into the replay detection cache
473 if (! added_replay_digest)
475 res = add_replay(opts, raw_digest);
476 if (res != SPA_MSG_SUCCESS)
478 log_msg(LOG_WARNING, "(stanza #%d) Could not add digest to replay cache",
485 added_replay_digest = 1;
488 /* At this point, we assume the SPA data is valid. Now we need to see
489 * if it meets our access criteria.
491 if(opts->verbose > 1)
492 log_msg(LOG_INFO, "(stanza #%d) SPA Decode (res=%i):\n%s",
493 stanza_num, res, dump_ctx(ctx));
495 /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
496 * then we need to make sure this incoming message is signer ID matches
497 * an entry in the list.
499 if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig)
501 res = fko_get_gpg_signature_id(ctx, &gpg_id);
502 if(res != FKO_SUCCESS)
504 log_msg(LOG_WARNING, "(stanza #%d) Error pulling the GPG signature ID from the context: %s",
505 stanza_num, fko_gpg_errstr(ctx));
513 log_msg(LOG_INFO, "(stanza #%d) Incoming SPA data signed by '%s'.",
516 if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id))
519 "(stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
528 /* Populate our spa data struct for future reference.
530 res = get_spa_data_fields(ctx, &spadat);
532 /* Figure out what our timeout will be. If it is specified in the SPA
533 * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the
534 * access.conf file (if there is one). Otherwise use the default.
536 if(spadat.client_timeout > 0)
537 spadat.fw_access_timeout = spadat.client_timeout;
538 else if(acc->fw_access_timeout > 0)
539 spadat.fw_access_timeout = acc->fw_access_timeout;
541 spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
543 if(res != FKO_SUCCESS)
545 log_msg(LOG_ERR, "(stanza #%d) Unexpected error pulling SPA data from the context: %s",
546 stanza_num, fko_errstr(res));
554 /* Check packet age if so configured.
556 if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
560 ts_diff = abs(now_ts - spadat.timestamp);
562 if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
564 log_msg(LOG_WARNING, "(stanza #%d) SPA data time difference is too great (%i seconds).",
565 stanza_num, ts_diff);
574 /* At this point, we have enough to check the embedded (or packet source)
575 * IP address against the defined access rights. We start by splitting
576 * the spa msg source IP from the remainder of the message.
578 spa_ip_demark = strchr(spadat.spa_message, ',');
579 if(spa_ip_demark == NULL)
581 log_msg(LOG_WARNING, "(stanza #%d) Error parsing SPA message string: %s",
582 stanza_num, fko_errstr(res));
590 strlcpy(spadat.spa_message_src_ip,
591 spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);
593 if(strnlen(spadat.spa_message_src_ip,
594 MIN_IPV4_STR_LEN) < MIN_IPV4_STR_LEN)
596 log_msg(LOG_WARNING, "(stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
597 stanza_num, fko_errstr(res));
605 strlcpy(spadat.spa_message_remain, spa_ip_demark+1, 1024);
607 /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
610 if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0)
612 if(acc->require_source_address)
615 "(stanza #%d) Got 0.0.0.0 when valid source IP was required.",
625 spadat.use_src_ip = spadat.pkt_source_ip;
628 spadat.use_src_ip = spadat.spa_message_src_ip;
630 /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
633 if(acc->require_username != NULL)
635 if(strcmp(spadat.username, acc->require_username) != 0)
638 "(stanza #%d) Username in SPA data (%s) does not match required username: %s",
639 stanza_num, spadat.username, acc->require_username
649 /* Take action based on SPA message type.
651 if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG
652 || spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
653 || spadat.message_type == FKO_NAT_ACCESS_MSG
654 || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG)
656 #if FIREWALL_IPTABLES
657 if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0)
660 "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled",
661 stanza_num, spadat.pkt_source_ip
671 "(stanza #%d) SPA packet from %s requested unsupported NAT access",
672 stanza_num, spadat.pkt_source_ip
684 if(spadat.message_type == FKO_COMMAND_MSG)
686 if(!acc->enable_cmd_exec)
689 "(stanza #%d) SPA Command message are not allowed in the current configuration.",
701 "(stanza #%d) Processing SPA Command message: command='%s'.",
702 stanza_num, spadat.spa_message_remain
705 /* Do we need to become another user? If so, we call
706 * run_extcmd_as and pass the cmd_exec_uid.
708 if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
711 log_msg(LOG_INFO, "(stanza #%d) Setting effective user to %s (UID=%i) before running command.",
712 stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid);
715 res = run_extcmd_as(acc->cmd_exec_uid,
716 spadat.spa_message_remain, NULL, 0, 0);
718 else /* Just run it as we are (root that is). */
719 res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5);
721 /* --DSS XXX: I have found that the status (and res for that
722 * matter) have been unreliable indicators of the
723 * actual exit status of some commands. Not sure
724 * why yet. For now, we will take what we get.
726 status = WEXITSTATUS(res);
728 if(opts->verbose > 1)
730 "(stanza #%d) CMD_EXEC: command returned %i",
734 res = SPA_MSG_COMMAND_ERROR;
739 /* we processed the command on a matching access stanza, so we
740 * don't look for anything else to do with this SPA packet
746 /* From this point forward, we have some kind of access message. So
747 * we first see if access is allowed by checking access against
748 * restrict_ports and open_ports.
750 * --DSS TODO: We should add BLACKLIST support here as well.
752 if(! acc_check_port_access(acc, spadat.spa_message_remain))
755 "(stanza #%d) One or more requested protocol/ports was denied per access.conf.",
765 /* At this point, we process the SPA request and break out of the
766 * access stanza loop (first valid access stanza stops us looking
769 process_spa_request(opts, acc, &spadat);
775 if (raw_digest != NULL)