/* Process the SPA packet data */ void incoming_spa(fko_srv_options_t *opts) { /* Always a good idea to initialize ctx to null if it will be used * repeatedly (especially when using fko_new_with_data()). */ fko_ctx_t ctx = NULL; char *spa_ip_demark, *raw_digest = NULL; int res, enc_type, stanza_num=0; int added_replay_digest = 0; int is_err, cmd_exec_success = 0, attempted_decrypt = 0; int conf_pkt_age = 0; char dump_buf[CTX_DUMP_BUFSIZE]; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Loop through all access stanzas looking for a match */ acc_stanza_t *acc = opts->acc_stanzas; inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); /* At this point, we want to validate and (if needed) preprocess the * SPA data and/or to be reasonably sure we have a SPA packet (i.e * try to eliminate obvious non-spa packets). */ if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest)) return; if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { conf_pkt_age = strtol_wrapper(opts->config[CONF_MAX_SPA_PACKET_AGE], 0, RCHK_MAX_SPA_PACKET_AGE, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] [%s] invalid MAX_SPA_PACKET_AGE", spadat.pkt_source_ip); return; } } /* Now that we know there is a matching access.conf stanza and the * incoming SPA packet is not a replay, see if we should grant any * access */ while(acc) { res = FKO_SUCCESS; cmd_exec_success = 0; attempted_decrypt = 0; stanza_num++; /* Start access loop with a clean FKO context */ if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } /* Check for a match for the SPA source and destination IP and the access stanza */ if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num)) { acc = acc->next; continue; } log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match", stanza_num, spadat.pkt_source_ip); log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data); /* Make sure this access stanza has not expired */ if(! check_stanza_expiration(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Get encryption type and try its decoding routine first (if the key * for that type is set) */ enc_type = fko_encryption_type((char *)spa_pkt->packet_data); if(! handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, &cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt, enc_type, stanza_num, res)) { acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ if(! add_replay_cache(opts, acc, &spadat, raw_digest, &added_replay_digest, stanza_num, &res)) { acc = acc->next; continue; } /* At this point the SPA data is authenticated via the HMAC (if used * for now). Next we need to see if it meets our access criteria which * the server imposes regardless of the content of the SPA packet. */ log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):", spadat.pkt_source_ip, stanza_num, res); res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_DEBUG, "%s", dump_buf); else log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty, * then we need to make sure this incoming message is signer ID matches * an entry in the list. */ if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res)) { acc = acc->next; continue; } /* Populate our spa data struct for future reference. */ res = get_spa_data_fields(ctx, &spadat); if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } /* Figure out what our timeout will be. If it is specified in the SPA * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the * access.conf file (if there is one). Otherwise use the default. */ set_timeout(acc, &spadat); /* Check packet age if so configured. */ if(! check_pkt_age(opts, &spadat, stanza_num, conf_pkt_age)) { acc = acc->next; continue; } /* At this point, we have enough to check the embedded (or packet source) * IP address against the defined access rights. We start by splitting * the spa msg source IP from the remainder of the message. */ spa_ip_demark = strchr(spadat.spa_message, ','); if(spa_ip_demark == NULL) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num); break; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); if(! is_valid_ipv4_addr(spadat.spa_message_src_ip)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); break; } strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(! check_src_access(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ if(! check_username(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Take action based on SPA message type. */ if(! check_nat_access_types(opts, acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Command messages. */ if(spadat.message_type == FKO_COMMAND_MSG) { if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res)) { /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } else { acc = acc->next; continue; } } /* From this point forward, we have some kind of access message. So * we first see if access is allowed by checking access against * restrict_ports and open_ports. * * --DSS TODO: We should add BLACKLIST support here as well. */ if(! check_port_proto(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we process the SPA request and break out of the * access stanza loop (first valid access stanza stops us looking * for others). */ if(opts->test) /* no firewall changes in --test mode */ { log_msg(LOG_WARNING, "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { process_spa_request(opts, acc, &spadat); } /* If we made it here, then the SPA packet was processed according * to a matching access.conf stanza, so we're done with this packet. */ break; } if (raw_digest != NULL) free(raw_digest); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } return; }
/* Process the SPA packet data */ void incoming_spa(fko_srv_options_t *opts) { /* Always a good idea to initialize ctx to null if it will be used * repeatedly (especially when using fko_new_with_data()). */ fko_ctx_t ctx = NULL; char *spa_ip_demark, *raw_digest = NULL; int res, enc_type, stanza_num=0; int added_replay_digest = 0; int cmd_exec_success = 0, attempted_decrypt = 0; char dump_buf[CTX_DUMP_BUFSIZE]; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Loop through all access stanzas looking for a match */ //²éÕÒËùÓеÄstanzas£¬Æ¥ÅäÏàÓ¦Ïî¡£ acc_stanza_t *acc = opts->acc_stanzas; inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); //»ñÈ¡Êý¾Ý±¨À´Ô´µØÖ·¡£ inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); //»ñÈ¡Êý¾Ý±¨Ä¿µÄµØÖ·¡£ /* At this point, we want to validate and (if needed) preprocess the * SPA data and/or to be reasonably sure we have a SPA packet (i.e * try to eliminate obvious non-spa packets). */ //ÔÚÕâÀÎÒÃÇÒªÑéÖ¤ºÍÔ¤´¦ÀíSPAÊý¾Ý£¬ÌÞ³ýÃ÷ÏÔ²»ÊÇSPAµÄÊý¾Ý°ü¡£ //ͨ¹ý±È¶ÔÕªÒª¼ì²éÊý¾ÝÍêÕûÐÔºÍÊÇ·ñÊܵ½ÖطŹ¥»÷¡£ //¼ÆËãctx->encrypted_msgµÄSHA256ÕªÒªÊý¾Ý£¬´æÔÚraw_digest¡£ if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest)) return; /* Now that we know there is a matching access.conf stanza and the * incoming SPA packet is not a replay, see if we should grant any * access */ while(acc) { res = FKO_SUCCESS; cmd_exec_success = 0; attempted_decrypt = 0; stanza_num++; /* Start access loop with a clean FKO context */ //¶Ôÿһ¸östanza½øÐÐÆ¥Å䣬ֱµ½Æ¥Åä³É¹¦ÎªÖ¹¡£ if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } /* Check for a match for the SPA source and destination IP and the access stanza */ //Æ¥ÅästanzaÖеÄÔ´µØÖ·ºÍÄ¿µÄµØÖ·¡£ if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num)) { acc = acc->next; continue; } log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match", stanza_num, spadat.pkt_source_ip); log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data); /* Make sure this access stanza has not expired */ //È·±£Õâ¸östanzaûÓйýÆÚ¡£ if(! check_stanza_expiration(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Get encryption type and try its decoding routine first (if the key * for that type is set) */ //¼ì²â´Ë¶ÎÃÜÎĵļÓÃÜÀàÐÍ¡£ enc_type = fko_encryption_type((char *)spa_pkt->packet_data); //»¹Ô¼ÓÑÎÃÜÎÄ£¬Í¨¹ýHMACժҪУÑéÊý¾Ý°üµÄÍêÕûÐÔ£¬½âÃÜencrypted_msg¡£ if(acc->use_rijndael) handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, &cmd_exec_success, enc_type, stanza_num, &res); if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt, enc_type, stanza_num, res)) { acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ //¼ì²âÖطŹ¥»÷¡£ if(! add_replay_cache(opts, acc, &spadat, raw_digest, &added_replay_digest, stanza_num, &res)) { acc = acc->next; continue; } /* At this point the SPA data is authenticated via the HMAC (if used * for now). Next we need to see if it meets our access criteria which * the server imposes regardless of the content of the SPA packet. */ log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):", spadat.pkt_source_ip, stanza_num, res); res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_DEBUG, "%s", dump_buf); else log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty, * then we need to make sure this incoming message is signer ID matches * an entry in the list. */ if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res)) { acc = acc->next; continue; } /* Populate our spa data struct for future reference. */ //ÏòspadatÖÐÌî³ä×ֶΡ£ res = get_spa_data_fields(ctx, &spadat); if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } /* Figure out what our timeout will be. If it is specified in the SPA * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the * access.conf file (if there is one). Otherwise use the default. */ //²é¿´ÔÚSPAÖÐÓÐûÓÐÉèÖó¬Ê±×ֶΣ¬·ñÔò¾ÍÓÃaccess.confÖÐÉèÖõÄ×ֶΡ£ set_timeout(acc, &spadat); /* Check packet age if so configured. */ //²é¿´Êý¾Ý°üÖеÄʱ¼ä´ÁÊÇ·ñ¹ýʱ¡£ if(! check_pkt_age(opts, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we have enough to check the embedded (or packet source) * IP address against the defined access rights. We start by splitting * the spa msg source IP from the remainder of the message. */ spa_ip_demark = strchr(spadat.spa_message, ','); if(spa_ip_demark == NULL) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num); break; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); if(! is_valid_ipv4_addr(spadat.spa_message_src_ip)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); break; } strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(! check_src_access(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ //Èç¹ûREQUIRE_USERNAME ×ֶα»ÉèÖã¬È·±£Óû§ÃûÔÚSPAÖпɱ»Æ¥Åä¡£ if(! check_username(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Take action based on SPA message type. */ if(! check_nat_access_types(opts, acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Command messages. */ if(acc->cmd_cycle_open != NULL) { if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res)) break; /* successfully processed a matching access stanza */ else { acc = acc->next; continue; } } else if(spadat.message_type == FKO_COMMAND_MSG) { if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res)) { /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } else { acc = acc->next; continue; } } /* From this point forward, we have some kind of access message. So * we first see if access is allowed by checking access against * restrict_ports and open_ports. * * --DSS TODO: We should add BLACKLIST support here as well. */ if(! check_port_proto(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we process the SPA request and break out of the * access stanza loop (first valid access stanza stops us looking * for others). */ if(opts->test) /* no firewall changes in --test mode */ { log_msg(LOG_WARNING, "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { if(acc->cmd_cycle_open != NULL) { //Ö´ÐÐiptablesµÄCMD¡£ if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res)) break; /* successfully processed a matching access stanza */ else { acc = acc->next; continue; } } else { process_spa_request(opts, acc, &spadat); } } /* If we made it here, then the SPA packet was processed according * to a matching access.conf stanza, so we're done with this packet. */ break; } if (raw_digest != NULL) free(raw_digest); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } return; }