static void ctx_update(fko_ctx_t *ctx, int new_ctx_flag, int destroy_ctx_flag, int print_flag) { if (destroy_ctx_flag == CTX_DESTROY) { if (print_flag == DO_PRINT) printf("fko_destroy(): %s\n", fko_errstr(fko_destroy(*ctx))); else fko_destroy(*ctx); spa_calls++; *ctx = NULL; } if (new_ctx_flag == NEW_CTX) { /* always destroy before re-creating */ if (print_flag == DO_PRINT) printf("fko_destroy(): %s\n", fko_errstr(fko_destroy(*ctx))); else fko_destroy(*ctx); *ctx = NULL; if (print_flag == DO_PRINT) printf("fko_new(): %s\n", fko_errstr(fko_new(ctx))); else fko_new(ctx); spa_calls += 2; } return; }
/* For replay attack detection */ static int get_raw_digest(char **digest, char *pkt_data) { fko_ctx_t ctx = NULL; char *tmp_digest = NULL; int res = FKO_SUCCESS; /* initialize an FKO context with no decryption key just so * we can get the outer message digest */ res = fko_new_with_data(&ctx, (char *)pkt_data, NULL); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); return(SPA_MSG_FKO_CTX_ERROR); } res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); return(SPA_MSG_DIGEST_ERROR); } res = fko_set_raw_spa_digest(ctx); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); return(SPA_MSG_DIGEST_ERROR); } res = fko_get_raw_spa_digest(ctx, &tmp_digest); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error getting digest from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); return(SPA_MSG_DIGEST_ERROR); } *digest = strdup(tmp_digest); if (digest == NULL) return SPA_MSG_ERROR; fko_destroy(ctx); return res; }
static void spa_func_int(fko_ctx_t *ctx, char *name, int (*spa_set)(fko_ctx_t ctx, const int modifier), int min, int max, int final_val, int new_ctx_flag, int destroy_ctx_flag) { fko_ctx_t default_ctx = NULL; int i; spa_default_ctx(&default_ctx); printf("[+] calling libfko function: %s\n", name); for (i=min; i <= max; i++) { printf("%s(%d): %s\n", name, i, fko_errstr((spa_set)(*ctx, i))); printf("%s(%d): %s (DUPE)\n", name, i, fko_errstr((spa_set)(*ctx, i))); ctx_update(ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); spa_calls += 2; /* also set on a fully populated context */ (spa_set)(default_ctx, i); } printf("%s(%d): %s (FINAL)\n", name, final_val, fko_errstr((spa_set)(*ctx, final_val))); display_ctx(*ctx); fko_spa_data_final(default_ctx, ENC_KEY, 16, HMAC_KEY, 16); fko_destroy(default_ctx); default_ctx = NULL; return; }
static void spa_func_getset_int(fko_ctx_t *ctx, char *set_name, int (*spa_set)(fko_ctx_t ctx, const int modifier), char *get_name, int (*spa_get)(fko_ctx_t ctx, int *val), int min, int max, int final_val, int new_ctx_flag, int destroy_ctx_flag) { fko_ctx_t default_ctx = NULL; int get_val; int i, res; spa_default_ctx(&default_ctx); printf("[+] calling libfko get/set: %s/%s\n", get_name, set_name); for (i=min; i <= max; i++) { get_val = 1234; /* meaningless default */ printf("%s(%d): %s\n", set_name, i, fko_errstr((spa_set)(*ctx, i))); printf("%s(%d): %s (DUPE)\n", set_name, i, fko_errstr((spa_set)(*ctx, i))); res = (spa_get)(*ctx, &get_val); printf("%s(%d): %s\n", get_name, get_val, fko_errstr(res)); ctx_update(ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); spa_calls += 3; /* also set on a fully populated context */ (spa_set)(default_ctx, i); } printf("%s(%d): %s (FINAL)\n", set_name, final_val, fko_errstr((spa_set)(*ctx, final_val))); display_ctx(*ctx); fko_spa_data_final(default_ctx, ENC_KEY, 16, HMAC_KEY, 16); fko_destroy(default_ctx); default_ctx = NULL; return; }
static void afl_enc_pkt_from_file(fko_srv_options_t *opts) { FILE *fp = NULL; fko_ctx_t decrypt_ctx = NULL; unsigned char enc_spa_pkt[AFL_MAX_PKT_SIZE] = {0}, rc; int res = 0, es = EXIT_SUCCESS, enc_msg_len; char dump_buf[AFL_DUMP_CTX_SIZE]; fp = fopen(opts->config[CONF_AFL_PKT_FILE], "rb"); if(fp != NULL) { enc_msg_len = 0; while(fread(&rc, 1, 1, fp)) { enc_spa_pkt[enc_msg_len] = rc; enc_msg_len++; if(enc_msg_len == AFL_MAX_PKT_SIZE-1) break; } fclose(fp); fko_new(&decrypt_ctx); res = fko_afl_set_spa_data(decrypt_ctx, (const char *)enc_spa_pkt, enc_msg_len); if(res == FKO_SUCCESS) res = fko_decrypt_spa_data(decrypt_ctx, "fwknoptest", strlen("fwknoptest")); if(res == FKO_SUCCESS) res = dump_ctx_to_buffer(decrypt_ctx, dump_buf, sizeof(dump_buf)); if(res == FKO_SUCCESS) log_msg(LOG_INFO, "%s", dump_buf); else log_msg(LOG_ERR, "Error (%d): %s", res, fko_errstr(res)); fko_destroy(decrypt_ctx); if(res == FKO_SUCCESS) { log_msg(LOG_INFO, "SPA packet decode: %s", fko_errstr(res)); es = EXIT_SUCCESS; } else { log_msg(LOG_ERR, "Could not decode SPA packet: %s", fko_errstr(res)); es = EXIT_FAILURE; } } else log_msg(LOG_ERR, "Could not acquire SPA packet from file: %s.", opts->config[CONF_AFL_PKT_FILE]); clean_exit(opts, NO_FW_CLEANUP, es); }
/* destroy_ctx */ static PyObject * destroy_ctx(PyObject *self, PyObject *args) { fko_ctx_t ctx; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; fko_destroy(ctx); return Py_BuildValue("", NULL); }
int main(void) { fko_ctx_t ctx = NULL; int res = 0, i, es = EXIT_SUCCESS; int exec=0, success=0, fail=0; fiu_init(0); for (i=0; i < sizeof(fiu_rvs)/sizeof(int); i++) { exec++; printf("[+] libfiu injection tag: %s\n", fiu_tags[i]); fiu_enable(fiu_tags[i], fiu_rvs[i], NULL, 0); res = fko_new(&ctx); if(strncmp(fiu_tags[i], "fko_set_rand_value_lenval", strlen("fko_set_rand_value_lenval")) == 0) res = fko_set_rand_value(ctx, "asdf1234"); if(strncmp(fiu_tags[i], "fko_set_rand_value_strdup", strlen("fko_set_rand_value_strdup")) == 0) res = fko_set_rand_value(ctx, "asdf1234"); if(strncmp(fiu_tags[i], "fko_set_username_valuser", strlen("fko_set_username_valuser")) == 0) res = fko_set_username(ctx, "BADCHAR="); if(strncmp(fiu_tags[i], "fko_set_username_strdup", strlen("fko_set_username_strdup")) == 0) res = fko_set_username(ctx, "normaluser"); if(res == FKO_SUCCESS) { printf("[-] fko_new(): %s\n", fko_errstr(res)); fail++; es = EXIT_FAILURE; } else { printf("[+] fko_new(): %s\n", fko_errstr(res)); success++; } fko_destroy(ctx); ctx = NULL; fiu_disable(fiu_tags[i]); } printf("fiu_fault_injection() passed/failed/executed: %d/%d/%d\n", success, fail, exec); return es; }
static void afl_pkt_from_stdin(fko_srv_options_t *opts) { FILE *fp = NULL; fko_ctx_t decode_ctx = NULL; unsigned char spa_pkt[AFL_MAX_PKT_SIZE] = {0}; int res = 0, es = EXIT_SUCCESS; char dump_buf[AFL_DUMP_CTX_SIZE]; fp = fdopen(STDIN_FILENO, "r"); if(fp != NULL) { if(fgets((char *)spa_pkt, AFL_MAX_PKT_SIZE, fp) == NULL) { fclose(fp); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } fclose(fp); fko_new(&decode_ctx); res = fko_set_encoded_data(decode_ctx, (char *) spa_pkt, strlen((char *)spa_pkt), 0, FKO_DIGEST_SHA256); if(res == FKO_SUCCESS) res = fko_set_spa_data(decode_ctx, (const char *) spa_pkt); if(res == FKO_SUCCESS) res = fko_decode_spa_data(decode_ctx); if(res == FKO_SUCCESS) res = dump_ctx_to_buffer(decode_ctx, dump_buf, sizeof(dump_buf)); if(res == FKO_SUCCESS) log_msg(LOG_INFO, "%s", dump_buf); fko_destroy(decode_ctx); if(res == FKO_SUCCESS) { log_msg(LOG_INFO, "SPA packet decode: %s", fko_errstr(res)); es = EXIT_SUCCESS; } else { log_msg(LOG_ERR, "Could not decode SPA packet: %s", fko_errstr(res)); es = EXIT_FAILURE; } } else log_msg(LOG_ERR, "Could not acquire SPA packet from stdin."); clean_exit(opts, NO_FW_CLEANUP, es); }
/* Initialize an fko context with external (encrypted/encoded) data. * This is used to create a context with the purpose of decoding * and parsing the provided data into the context data. */ int fko_new_with_data(fko_ctx_t *r_ctx, char *enc_msg, char *dec_key) { fko_ctx_t ctx; int res = FKO_SUCCESS; /* Are we optimistic or what? */ ctx = calloc(1, sizeof *ctx); if(ctx == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* First, add the data to the context. */ ctx->encrypted_msg = strdup(enc_msg); if(ctx->encrypted_msg == NULL) { free(ctx); return(FKO_ERROR_MEMORY_ALLOCATION); } /* Consider it initialized here. */ ctx->initval = FKO_CTX_INITIALIZED; FKO_SET_CTX_INITIALIZED(ctx); /* If a decryption password is provided, go ahead and decrypt and * decode. */ if(dec_key != NULL) { res = fko_decrypt_spa_data(ctx, dec_key); if(res != FKO_SUCCESS) { fko_destroy(ctx); *r_ctx = NULL; /* Make sure the caller ctx is null just in case */ return(res); } } #if HAVE_LIBGPGME /* Set gpg signature verify on. */ ctx->verify_gpg_sigs = 1; #endif /* HAVE_LIBGPGME */ *r_ctx = ctx; return(res); }
int main(void) { fko_ctx_t ctx = NULL; int res = 0; res = fko_new(&ctx); if (res == FKO_SUCCESS) printf("[+] fko_new(): %s\n", fko_errstr(res)); else printf("[-] fko_new(): %s\n", fko_errstr(res)); fko_destroy(ctx); return 0; }
/* free up memory and exit */ static void clean_exit(fko_ctx_t ctx, fko_cli_options_t *opts, char *key, int *key_len, char *hmac_key, int *hmac_key_len, unsigned int exit_status) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx = NULL; free_configs(opts); zero_buf_wrapper(key, *key_len); zero_buf_wrapper(hmac_key, *hmac_key_len); *key_len = 0; *hmac_key_len = 0; exit(exit_status); }
/* free up memory and exit */ static void clean_exit(fko_ctx_t ctx, fko_cli_options_t *opts, char *key, int *key_len, char *hmac_key, int *hmac_key_len, unsigned int exit_status) { #if HAVE_LIBFIU if(opts->fault_injection_tag[0] != 0x0) fiu_disable(opts->fault_injection_tag); #endif if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx = NULL; free_configs(opts); zero_buf_wrapper(key, *key_len); zero_buf_wrapper(hmac_key, *hmac_key_len); *key_len = 0; *hmac_key_len = 0; exit(exit_status); }
/* Initialize an fko context. */ int fko_new(fko_ctx_t *r_ctx) { fko_ctx_t ctx = NULL; int res; char *ver; #if HAVE_LIBFIU fiu_return_on("fko_new_calloc", FKO_ERROR_MEMORY_ALLOCATION); #endif ctx = calloc(1, sizeof *ctx); if(ctx == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* Set default values and state. * * Note: We initialize the context early so that the fko_set_xxx * functions can operate properly. If there are any problems during * initialization, then fko_destroy() is called which will clean up * the context. */ ctx->initval = FKO_CTX_INITIALIZED; /* Set the version string. */ ver = strdup(FKO_PROTOCOL_VERSION); if(ver == NULL) { fko_destroy(ctx); ctx = NULL; return(FKO_ERROR_MEMORY_ALLOCATION); } ctx->version = ver; /* Rand value. */ res = fko_set_rand_value(ctx, NULL); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Username. */ res = fko_set_username(ctx, NULL); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Timestamp. */ res = fko_set_timestamp(ctx, 0); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Default Digest Type. */ res = fko_set_spa_digest_type(ctx, FKO_DEFAULT_DIGEST); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Default Message Type. */ res = fko_set_spa_message_type(ctx, FKO_DEFAULT_MSG_TYPE); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Default Encryption Type. */ res = fko_set_spa_encryption_type(ctx, FKO_DEFAULT_ENCRYPTION); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Default is Rijndael in CBC mode */ res = fko_set_spa_encryption_mode(ctx, FKO_DEFAULT_ENC_MODE); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } #if HAVE_LIBGPGME /* Set gpg signature verify on. */ ctx->verify_gpg_sigs = 1; #endif /* HAVE_LIBGPGME */ FKO_SET_CTX_INITIALIZED(ctx); *r_ctx = ctx; return(FKO_SUCCESS); }
int main(int argc, char **argv) { fko_ctx_t ctx = NULL; fko_ctx_t ctx2 = NULL; int res; char *spa_data=NULL, *version=NULL; char access_buf[MAX_LINE_LEN] = {0}; char key[MAX_KEY_LEN+1] = {0}; char hmac_key[MAX_KEY_LEN+1] = {0}; int key_len = 0, orig_key_len = 0, hmac_key_len = 0, enc_mode; int tmp_port = 0; char dump_buf[CTX_DUMP_BUFSIZE]; fko_cli_options_t options; memset(&options, 0x0, sizeof(fko_cli_options_t)); /* Initialize the log module */ log_new(); /* Handle command line */ config_init(&options, argc, argv); #if HAVE_LIBFIU /* Set any fault injection points early */ if(! enable_fault_injections(&options)) clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); #endif /* Handle previous execution arguments if required */ if(prev_exec(&options, argc, argv) != 1) clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); if(options.show_last_command) clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_SUCCESS); /* Intialize the context */ res = fko_new(&ctx); if(res != FKO_SUCCESS) { errmsg("fko_new", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Display version info and exit. */ if(options.version) { fko_get_version(ctx, &version); fprintf(stdout, "fwknop client %s, FKO protocol version %s\n", MY_VERSION, version); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_SUCCESS); } /* Set client timeout */ if(options.fw_timeout >= 0) { res = fko_set_spa_client_timeout(ctx, options.fw_timeout); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_client_timeout", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* Set the SPA packet message type based on command line options */ res = set_message_type(ctx, &options); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_message_type", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Adjust the SPA timestamp if necessary */ if(options.time_offset_plus > 0) { res = fko_set_timestamp(ctx, options.time_offset_plus); if(res != FKO_SUCCESS) { errmsg("fko_set_timestamp", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } if(options.time_offset_minus > 0) { res = fko_set_timestamp(ctx, -options.time_offset_minus); if(res != FKO_SUCCESS) { errmsg("fko_set_timestamp", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } if(options.server_command[0] != 0x0) { /* Set the access message to a command that the server will * execute */ snprintf(access_buf, MAX_LINE_LEN, "%s%s%s", options.allow_ip_str, ",", options.server_command); } else { /* Resolve the client's public facing IP address if requestesd. * if this fails, consider it fatal. */ if (options.resolve_ip_http_https) { if(options.resolve_http_only) { if(resolve_ip_http(&options) < 0) { clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } else { /* Default to HTTPS */ if(resolve_ip_https(&options) < 0) { clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } } /* Set a message string by combining the allow IP and the * port/protocol. The fwknopd server allows no port/protocol * to be specified as well, so in this case append the string * "none/0" to the allow IP. */ if(set_access_buf(ctx, &options, access_buf) != 1) clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } res = fko_set_spa_message(ctx, access_buf); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_message", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Set NAT access string */ if (options.nat_local || options.nat_access_str[0] != 0x0) { res = set_nat_access(ctx, &options, access_buf); if(res != FKO_SUCCESS) { errmsg("fko_set_nat_access_str", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* Set username */ if(options.spoof_user[0] != 0x0) { res = fko_set_username(ctx, options.spoof_user); if(res != FKO_SUCCESS) { errmsg("fko_set_username", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* Set up for using GPG if specified. */ if(options.use_gpg) { /* If use-gpg-agent was not specified, then remove the GPG_AGENT_INFO * ENV variable if it exists. */ #ifndef WIN32 if(!options.use_gpg_agent) unsetenv("GPG_AGENT_INFO"); #endif res = fko_set_spa_encryption_type(ctx, FKO_ENCRYPTION_GPG); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_encryption_type", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Set gpg path if necessary */ if(strlen(options.gpg_exe) > 0) { res = fko_set_gpg_exe(ctx, options.gpg_exe); if(res != FKO_SUCCESS) { errmsg("fko_set_gpg_exe", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* If a GPG home dir was specified, set it here. Note: Setting * this has to occur before calling any of the other GPG-related * functions. */ if(strlen(options.gpg_home_dir) > 0) { res = fko_set_gpg_home_dir(ctx, options.gpg_home_dir); if(res != FKO_SUCCESS) { errmsg("fko_set_gpg_home_dir", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } res = fko_set_gpg_recipient(ctx, options.gpg_recipient_key); if(res != FKO_SUCCESS) { errmsg("fko_set_gpg_recipient", res); if(IS_GPG_ERROR(res)) log_msg(LOG_VERBOSITY_ERROR, "GPG ERR: %s", fko_gpg_errstr(ctx)); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } if(strlen(options.gpg_signer_key) > 0) { res = fko_set_gpg_signer(ctx, options.gpg_signer_key); if(res != FKO_SUCCESS) { errmsg("fko_set_gpg_signer", res); if(IS_GPG_ERROR(res)) log_msg(LOG_VERBOSITY_ERROR, "GPG ERR: %s", fko_gpg_errstr(ctx)); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } res = fko_set_spa_encryption_mode(ctx, FKO_ENC_MODE_ASYMMETRIC); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_encryption_mode", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } if(options.encryption_mode && !options.use_gpg) { res = fko_set_spa_encryption_mode(ctx, options.encryption_mode); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_encryption_mode", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* Set Digest type. */ if(options.digest_type) { res = fko_set_spa_digest_type(ctx, options.digest_type); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_digest_type", res); clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } /* Acquire the necessary encryption/hmac keys */ if(get_keys(ctx, &options, key, &key_len, hmac_key, &hmac_key_len) != 1) clean_exit(ctx, &options, key, &key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); orig_key_len = key_len; if(options.encryption_mode == FKO_ENC_MODE_CBC_LEGACY_IV && key_len > 16) { log_msg(LOG_VERBOSITY_ERROR, "WARNING: Encryption key in '-M legacy' mode must be <= 16 bytes"); log_msg(LOG_VERBOSITY_ERROR, "long - truncating before sending SPA packet. Upgrading remote"); log_msg(LOG_VERBOSITY_ERROR, "fwknopd is recommended."); key_len = 16; } /* Finalize the context data (encrypt and encode the SPA data) */ res = fko_spa_data_final(ctx, key, key_len, hmac_key, hmac_key_len); if(res != FKO_SUCCESS) { errmsg("fko_spa_data_final", res); if(IS_GPG_ERROR(res)) log_msg(LOG_VERBOSITY_ERROR, "GPG ERR: %s", fko_gpg_errstr(ctx)); clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Display the context data. */ if (options.verbose || options.test) { res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_VERBOSITY_NORMAL, "%s", dump_buf); else log_msg(LOG_VERBOSITY_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); } /* Save packet data payload if requested. */ if (options.save_packet_file[0] != 0x0) write_spa_packet_data(ctx, &options); /* SPA packet random destination port handling */ if (options.rand_port) { tmp_port = get_rand_port(ctx); if(tmp_port < 0) clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); options.spa_dst_port = tmp_port; } /* If we are using one the "raw" modes (normally because * we're going to spoof the SPA packet source IP), then select * a random source port unless the source port is already set */ if ((options.spa_proto == FKO_PROTO_TCP_RAW || options.spa_proto == FKO_PROTO_UDP_RAW || options.spa_proto == FKO_PROTO_ICMP) && !options.spa_src_port) { tmp_port = get_rand_port(ctx); if(tmp_port < 0) clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); options.spa_src_port = tmp_port; } res = send_spa_packet(ctx, &options); if(res < 0) { log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet: packet not sent."); clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } else { log_msg(LOG_VERBOSITY_INFO, "send_spa_packet: bytes sent: %i", res); } /* Run through a decode cycle in test mode (--DSS XXX: This test/decode * portion should be moved elsewhere). */ if (options.test) { /************** Decoding now *****************/ /* Now we create a new context based on data from the first one. */ res = fko_get_spa_data(ctx, &spa_data); if(res != FKO_SUCCESS) { errmsg("fko_get_spa_data", res); clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* Pull the encryption mode. */ res = fko_get_spa_encryption_mode(ctx, &enc_mode); if(res != FKO_SUCCESS) { errmsg("fko_get_spa_encryption_mode", res); if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx = NULL; clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* If gpg-home-dir is specified, we have to defer decrypting if we * use the fko_new_with_data() function because we need to set the * gpg home dir after the context is created, but before we attempt * to decrypt the data. Therefore we either pass NULL for the * decryption key to fko_new_with_data() or use fko_new() to create * an empty context, populate it with the encrypted data, set our * options, then decode it. * * This also verifies the HMAC and truncates it if there are no * problems. */ res = fko_new_with_data(&ctx2, spa_data, NULL, 0, enc_mode, hmac_key, hmac_key_len, options.hmac_type); if(res != FKO_SUCCESS) { errmsg("fko_new_with_data", res); if(fko_destroy(ctx2) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx2 = NULL; clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } res = fko_set_spa_encryption_mode(ctx2, enc_mode); if(res != FKO_SUCCESS) { errmsg("fko_set_spa_encryption_mode", res); if(fko_destroy(ctx2) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx2 = NULL; clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } /* See if we are using gpg and if we need to set the GPG home dir. */ if(options.use_gpg) { if(strlen(options.gpg_home_dir) > 0) { res = fko_set_gpg_home_dir(ctx2, options.gpg_home_dir); if(res != FKO_SUCCESS) { errmsg("fko_set_gpg_home_dir", res); if(fko_destroy(ctx2) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx2 = NULL; clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } } } /* Decrypt */ res = fko_decrypt_spa_data(ctx2, key, key_len); if(res != FKO_SUCCESS) { errmsg("fko_decrypt_spa_data", res); if(IS_GPG_ERROR(res)) { /* we most likely could not decrypt the gpg-encrypted data * because we don't have access to the private key associated * with the public key we used for encryption. Since this is * expected, return 0 instead of an error condition (so calling * programs like the fwknop test suite don't interpret this as * an unrecoverable error), but print the error string for * debugging purposes. The test suite does run a series of * tests that use a single key pair for encryption and * authentication, so decryption become possible for these * tests. */ log_msg(LOG_VERBOSITY_ERROR, "GPG ERR: %s\n%s", fko_gpg_errstr(ctx2), "No access to recipient private key?"); } if(fko_destroy(ctx2) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx2 = NULL; clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_FAILURE); } res = dump_ctx_to_buffer(ctx2, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_VERBOSITY_NORMAL, "\nDump of the Decoded Data\n%s", dump_buf); else log_msg(LOG_VERBOSITY_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); if(fko_destroy(ctx2) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_VERBOSITY_ERROR, "[*] Could not zero out sensitive data buffer."); ctx2 = NULL; } clean_exit(ctx, &options, key, &orig_key_len, hmac_key, &hmac_key_len, EXIT_SUCCESS); return EXIT_SUCCESS; /* quiet down a gcc warning */ }
/* 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, *gpg_id, *raw_digest = NULL; time_t now_ts; int res, status, ts_diff, enc_type, stanza_num=0; int added_replay_digest = 0, pkt_data_len=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; acc_string_list_t *gpg_id_ndx; unsigned char is_gpg_match = 0; inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_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). */ pkt_data_len = spa_pkt->packet_data_len; res = preprocess_spa_data(opts, spadat.pkt_source_ip); if(res != FKO_SUCCESS) { log_msg(LOG_DEBUG, "[%s] preprocess_spa_data() returned error %i: '%s' for incoming packet.", spadat.pkt_source_ip, res, get_errstr(res)); return; } if(opts->foreground == 1 && opts->verbose > 2) { printf("[+] candidate SPA packet payload:\n"); hex_dump(spa_pkt->packet_data, pkt_data_len); } 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; } } if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip))) { if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { /* Check for a replay attack */ res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data); if(res != FKO_SUCCESS) { if (raw_digest != NULL) free(raw_digest); return; } if (raw_digest == NULL) return; if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS) { free(raw_digest); return; } } } else { log_msg(LOG_WARNING, "No access data found for source IP: %s", 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, fko_errstr(res) ); ctx = NULL; } /* Check for a match for the SPA source IP and the access stanza */ if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip))) { 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(acc->access_expire_time > 0) { if(acc->expired) { acc = acc->next; continue; } else { if(time(NULL) > acc->access_expire_time) { log_msg(LOG_INFO, "[%s] (stanza #%d) Access stanza has expired", spadat.pkt_source_ip, stanza_num); acc->expired = 1; 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(acc->use_rijndael) { if (acc->key == NULL) { log_msg(LOG_ERR, "[%s] (stanza #%d) No KEY for RIJNDAEL encrypted messages", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } /* Command mode messages may be quite long */ if(acc->enable_cmd_exec || enc_type == FKO_ENCRYPTION_RIJNDAEL) { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key, acc->key_len, acc->encryption_mode, acc->hmac_key, acc->hmac_key_len, acc->hmac_type); attempted_decrypt = 1; if(res == FKO_SUCCESS) cmd_exec_success = 1; } } if(acc->use_gpg && enc_type == FKO_ENCRYPTION_GPG && cmd_exec_success == 0) { /* For GPG we create the new context without decrypting on the fly * so we can set some GPG parameters first. */ if(acc->gpg_decrypt_pw != NULL || acc->gpg_allow_no_pw) { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL, 0, FKO_ENC_MODE_ASYMMETRIC, acc->hmac_key, acc->hmac_key_len, acc->hmac_type); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error creating fko context (before decryption): %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); acc = acc->next; continue; } /* Set whatever GPG parameters we have. */ if(acc->gpg_exe != NULL) { res = fko_set_gpg_exe(ctx, acc->gpg_exe); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error setting GPG path %s: %s", spadat.pkt_source_ip, stanza_num, acc->gpg_exe, fko_errstr(res) ); acc = acc->next; continue; } } if(acc->gpg_home_dir != NULL) { res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error setting GPG keyring path to %s: %s", spadat.pkt_source_ip, stanza_num, acc->gpg_home_dir, fko_errstr(res) ); acc = acc->next; continue; } } if(acc->gpg_decrypt_id != NULL) fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id); /* If GPG_REQUIRE_SIG is set for this acc stanza, then set * the FKO context accordingly and check the other GPG Sig- * related parameters. This also applies when REMOTE_ID is * set. */ if(acc->gpg_require_sig) { fko_set_gpg_signature_verify(ctx, 1); /* Set whether or not to ignore signature verification errors. */ fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error); } else { fko_set_gpg_signature_verify(ctx, 0); fko_set_gpg_ignore_verify_error(ctx, 1); } /* Now decrypt the data. */ res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw, 0); attempted_decrypt = 1; } } if(attempted_decrypt == 0) { log_msg(LOG_ERR, "(stanza #%d) No stanza encryption mode match for encryption type: %i.", stanza_num, enc_type); acc = acc->next; continue; } /* Do we have a valid FKO context? Did the SPA decrypt properly? */ if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error creating fko context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); if(IS_GPG_ERROR(res)) log_msg(LOG_WARNING, "[%s] (stanza #%d) - GPG ERROR: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ if (added_replay_digest == 0 && strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { res = add_replay(opts, raw_digest); if (res != SPA_MSG_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Could not add digest to replay cache", spadat.pkt_source_ip, stanza_num); acc = acc->next; continue; } added_replay_digest = 1; } /* At this point, we assume the SPA data is valid. Now we need to see * if it meets our access criteria. */ 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(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig) { res = fko_get_gpg_signature_id(ctx, &gpg_id); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error pulling the GPG signature ID from the context: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } log_msg(LOG_INFO, "[%s] (stanza #%d) Incoming SPA data signed by '%s'.", spadat.pkt_source_ip, stanza_num, gpg_id); if(acc->gpg_remote_id != NULL) { is_gpg_match = 0; for(gpg_id_ndx = acc->gpg_remote_id_list; gpg_id_ndx != NULL; gpg_id_ndx=gpg_id_ndx->next) { res = fko_gpg_signature_id_match(ctx, gpg_id_ndx->str, &is_gpg_match); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error in GPG siganture comparision: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } if(is_gpg_match) break; } if(! is_gpg_match) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.", spadat.pkt_source_ip, stanza_num, gpg_id); acc = acc->next; continue; } } } /* Populate our spa data struct for future reference. */ res = get_spa_data_fields(ctx, &spadat); /* 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. */ if(spadat.client_timeout > 0) spadat.fw_access_timeout = spadat.client_timeout; else if(acc->fw_access_timeout > 0) spadat.fw_access_timeout = acc->fw_access_timeout; else spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT; 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; } /* Check packet age if so configured. */ if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { time(&now_ts); ts_diff = abs(now_ts - spadat.timestamp); if(ts_diff > conf_pkt_age) { log_msg(LOG_WARNING, "[%s] (stanza #%d) SPA data time difference is too great (%i seconds).", spadat.pkt_source_ip, stanza_num, ts_diff); 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, fko_errstr(res)); 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, fko_errstr(res) ); ctx = NULL; } 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)); 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, fko_errstr(res) ); ctx = NULL; } 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(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0) { if(acc->require_source_address) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Got 0.0.0.0 when valid source IP was required.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } spadat.use_src_ip = spadat.pkt_source_ip; } else spadat.use_src_ip = spadat.spa_message_src_ip; /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ if(acc->require_username != NULL) { if(strcmp(spadat.username, acc->require_username) != 0) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Username in SPA data (%s) does not match required username: %s", spadat.pkt_source_ip, stanza_num, spadat.username, acc->require_username ); acc = acc->next; continue; } } /* Take action based on SPA message type. */ if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG || spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG || spadat.message_type == FKO_NAT_ACCESS_MSG || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG) { #if FIREWALL_IPTABLES if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0) { log_msg(LOG_WARNING, "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled", stanza_num, spadat.pkt_source_ip ); acc = acc->next; continue; } #else log_msg(LOG_WARNING, "(stanza #%d) SPA packet from %s requested unsupported NAT access", stanza_num, spadat.pkt_source_ip ); acc = acc->next; continue; #endif } /* Command messages. */ if(spadat.message_type == FKO_COMMAND_MSG) { if(!acc->enable_cmd_exec) { log_msg(LOG_WARNING, "[%s] (stanza #%d) SPA Command message are not allowed in the current configuration.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { log_msg(LOG_INFO, "[%s] (stanza #%d) Processing SPA Command message: command='%s'.", spadat.pkt_source_ip, stanza_num, spadat.spa_message_remain ); /* Do we need to become another user? If so, we call * run_extcmd_as and pass the cmd_exec_uid. */ if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0) { log_msg(LOG_INFO, "[%s] (stanza #%d) Setting effective user to %s (UID=%i) before running command.", spadat.pkt_source_ip, stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid); res = run_extcmd_as(acc->cmd_exec_uid, spadat.spa_message_remain, NULL, 0, 0); } else /* Just run it as we are (root that is). */ res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5); /* --DSS XXX: I have found that the status (and res for that * matter) have been unreliable indicators of the * actual exit status of some commands. Not sure * why yet. For now, we will take what we get. */ status = WEXITSTATUS(res); if(opts->verbose > 1) log_msg(LOG_WARNING, "[%s] (stanza #%d) CMD_EXEC: command returned %i", spadat.pkt_source_ip, stanza_num, status); if(status != 0) res = SPA_MSG_COMMAND_ERROR; 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, fko_errstr(res) ); ctx = NULL; } /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } } /* 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(! acc_check_port_access(acc, spadat.spa_message_remain)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) One or more requested protocol/ports was denied per access.conf.", spadat.pkt_source_ip, 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). */ process_spa_request(opts, acc, &spadat); 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; } 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] fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip ); ctx = NULL; } return; }
/* JNI interface: constructs arguments and calls main function */ jstring Java_biz_incomsystems_fwknop2_SendSPA_sendSPAPacket(JNIEnv* env, jobject thiz) { fko_ctx_t ctx; fwknop_options_t opts; int res, hmac_str_len = 0; short message_type; short digest_type = FKO_DIGEST_SHA256; short hmac_type = FKO_HMAC_SHA256; int key_len, hmac_key_len; char res_msg[MSG_BUFSIZE+1] = {0}; char spa_msg[MSG_BUFSIZE+1] = {0}; char nat_msg[MSG_BUFSIZE+1] = {0}; jstring ourSpa; char *key_tmp[MAX_KEY_LEN+1] = {0}, *hmac_key_tmp[MAX_KEY_LEN+1] = {0}; LOGV("**** Init fwknop ****"); memset(&opts, 0, sizeof(fwknop_options_t)); /* Read the member values from the Java Object that called sendSPAPacket() method */ jclass c = (*env)->GetObjectClass(env,thiz); jfieldID fid = (*env)->GetFieldID(env, c, "access_str", "Ljava/lang/String;"); jstring jaccess = (*env)->GetObjectField(env, thiz, fid); const char *access_str = (*env)->GetStringUTFChars(env, jaccess, 0); fid = (*env)->GetFieldID(env, c, "allowip_str", "Ljava/lang/String;"); jstring jallowip = (*env)->GetObjectField(env, thiz, fid); const char *allowip_str = (*env)->GetStringUTFChars(env, jallowip, 0); fid = (*env)->GetFieldID(env, c, "passwd_str", "Ljava/lang/String;"); jstring jpasswd = (*env)->GetObjectField(env, thiz, fid); char *passwd_str = (*env)->GetStringUTFChars(env, jpasswd, 0); fid = (*env)->GetFieldID(env, c, "passwd_b64", "Ljava/lang/String;"); jstring jpasswd_b64 = (*env)->GetObjectField(env, thiz, fid); const char *passwd_b64 = (*env)->GetStringUTFChars(env, jpasswd_b64, 0); fid = (*env)->GetFieldID(env, c, "digest_type", "Ljava/lang/String;"); jstring jdigest_type = (*env)->GetObjectField(env, thiz, fid); char *set_digest_type = (*env)->GetStringUTFChars(env, jdigest_type, 0); fid = (*env)->GetFieldID(env, c, "hmac_str", "Ljava/lang/String;"); jstring jhmac = (*env)->GetObjectField(env, thiz, fid); char *hmac_str = (*env)->GetStringUTFChars(env, jhmac, 0); fid = (*env)->GetFieldID(env, c, "hmac_b64", "Ljava/lang/String;"); jstring jhmac_b64 = (*env)->GetObjectField(env, thiz, fid); const char *hmac_b64 = (*env)->GetStringUTFChars(env, jhmac_b64, 0); fid = (*env)->GetFieldID(env, c, "hmac_type", "Ljava/lang/String;"); jstring jhmac_type = (*env)->GetObjectField(env, thiz, fid); char *set_hmac_type = (*env)->GetStringUTFChars(env, jhmac_type, 0); fid = (*env)->GetFieldID(env, c, "fw_timeout_str", "Ljava/lang/String;"); jstring jfwtimeout = (*env)->GetObjectField(env, thiz, fid); const char *fw_timeout_str = (*env)->GetStringUTFChars(env, jfwtimeout, 0); fid = (*env)->GetFieldID(env, c, "nat_access_str", "Ljava/lang/String;"); jstring jnat_access_str = (*env)->GetObjectField(env, thiz, fid); const char *nat_access_str = (*env)->GetStringUTFChars(env, jnat_access_str, 0); fid = (*env)->GetFieldID(env, c, "nat_local", "Ljava/lang/String;"); jstring jnat_local = (*env)->GetObjectField(env, thiz, fid); const char *nat_local = (*env)->GetStringUTFChars(env, jnat_local, 0); fid = (*env)->GetFieldID(env, c, "server_cmd_str", "Ljava/lang/String;"); jstring jserver_cmd = (*env)->GetObjectField(env, thiz, fid); const char *server_cmd_str = (*env)->GetStringUTFChars(env, jserver_cmd, 0); fid = (*env)->GetFieldID(env, c, "legacy", "Ljava/lang/String;"); jstring jlegacy = (*env)->GetObjectField(env, thiz, fid); const char *legacy = (*env)->GetStringUTFChars(env, jlegacy, 0); /* Sanity checks */ if(access_str == NULL) { sprintf(res_msg, "Error: Invalid or missing access string"); goto cleanup2; } if(allowip_str == NULL) { sprintf(res_msg, "Error: Invalid or missing allow IP"); goto cleanup2; } if(passwd_str == NULL) { sprintf(res_msg, "Error: Invalid or missing password"); goto cleanup2; } if(fw_timeout_str == NULL) { sprintf(res_msg, "Error: Invalid or missing firewall timeout value"); goto cleanup2; } if(hmac_str != NULL) { hmac_str_len = (int)strlen(hmac_str); } key_len = (int)strlen(passwd_str); if(legacy == NULL) { sprintf(legacy, "false"); } if(strcmp(hmac_b64, "true") == 0) { hmac_str_len = fko_base64_decode( hmac_str, (unsigned char *)hmac_key_tmp); if(hmac_str_len > MAX_KEY_LEN || hmac_str_len < 0) { LOGV("[*] Invalid key length: '%d', must be in [1,%d]", hmac_str_len, MAX_KEY_LEN); goto cleanup2; } else { memcpy(hmac_str, hmac_key_tmp, hmac_str_len); } } if(strcmp(passwd_b64, "true") == 0) { LOGV("Detected key b64"); key_len = fko_base64_decode(passwd_str, (unsigned char *)key_tmp); if(key_len > MAX_KEY_LEN || key_len < 0) { LOGV( "[*] Invalid key length: '%d', must be in [1,%d]", key_len, MAX_KEY_LEN); goto cleanup2; } else { memcpy(passwd_str, key_tmp, key_len); } } /* Using an HMAC is optional in the pre-rfc mode. */ if (server_cmd_str[0] != 0x0) { message_type = FKO_COMMAND_MSG; } else { message_type = FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG; } /* Intialize the context */ res = fko_new(&ctx); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Unable to create FKO context", res)); goto cleanup2; } /* Set server command */ if (server_cmd_str[0] != 0x0) { message_type = FKO_COMMAND_MSG; fko_set_spa_message_type(ctx, message_type); res = fko_set_spa_message(ctx, server_cmd_str); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA request message", res)); goto cleanup; } } else { /* Set client timeout */ res = fko_set_spa_client_timeout(ctx, atoi(fw_timeout_str)); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting FW timeout", res)); goto cleanup; } /* Set the spa message string */ snprintf(spa_msg, MSG_BUFSIZE, "%s,%s", allowip_str, access_str); res = fko_set_spa_message(ctx, spa_msg); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA request message", res)); goto cleanup; } } /* Set the HMAC mode if necessary */ if (strcmp(legacy, "true") == 0) { res = fko_set_spa_encryption_mode(ctx, FKO_ENC_MODE_CBC_LEGACY_IV); if (key_len > 16) { key_len = 16; } } if (hmac_str_len > 0) { if (strcmp(set_hmac_type, "MD5") == 0) { hmac_type = FKO_HMAC_MD5; } else if (strcmp(set_hmac_type, "SHA1") == 0) { hmac_type = FKO_HMAC_SHA1; } else if (strcmp(set_hmac_type, "SHA256") == 0) { hmac_type = FKO_HMAC_SHA256; } else if (strcmp(set_hmac_type, "SHA384") == 0) { hmac_type = FKO_HMAC_SHA384; } else if (strcmp(set_hmac_type, "SHA512") == 0) { hmac_type = FKO_HMAC_SHA512; } res = fko_set_spa_hmac_type(ctx, hmac_type); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA HMAC type", res)); goto cleanup; } } /* Set Nat */ if (nat_access_str[0] != 0x0){ // if nat_access_str is not blank, push it into fko context if (strncmp(nat_local, "true", 4) == 0) { message_type = FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG; fko_set_spa_message_type(ctx, message_type); LOGV("Finished setting local-nat."); } res = fko_set_spa_nat_access(ctx, nat_access_str); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting NAT string", res)); goto cleanup; } } LOGV("Setting digest type to %s.", set_digest_type); if (strcmp(set_digest_type, "MD5") == 0) { digest_type = FKO_HMAC_MD5; } else if (strcmp(set_digest_type, "SHA1") == 0) { digest_type = FKO_HMAC_SHA1; } else if (strcmp(set_digest_type, "SHA256") == 0) { digest_type = FKO_HMAC_SHA256; } else if (strcmp(set_digest_type, "SHA384") == 0) { digest_type = FKO_HMAC_SHA384; } else if (strcmp(set_digest_type, "SHA512") == 0) { digest_type = FKO_HMAC_SHA512; } res = fko_set_spa_digest_type(ctx, digest_type); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA digest type", res)); goto cleanup; } LOGV("Finished setting digest type."); /* Finalize the context data (Encrypt and encode). */ res = fko_spa_data_final(ctx, (char*)passwd_str, key_len, (char *)hmac_str, hmac_str_len); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error generating SPA data", res)); goto cleanup; } LOGV("Finished finalize."); res = fko_get_spa_data(ctx, &opts.spa_data); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error getting SPA data", res)); goto cleanup; } /* Generate the spa data packet */ ourSpa = (*env)->NewStringUTF(env, opts.spa_data); cleanup: /* Release the resources used by the fko context. */ fko_destroy(ctx); cleanup2: /* Release mem */ (*env)->ReleaseStringUTFChars(env, jaccess, access_str); (*env)->ReleaseStringUTFChars(env, jallowip, allowip_str); (*env)->ReleaseStringUTFChars(env, jpasswd, passwd_str); (*env)->ReleaseStringUTFChars(env, jpasswd_b64, passwd_b64); (*env)->ReleaseStringUTFChars(env, jdigest_type, set_digest_type); (*env)->ReleaseStringUTFChars(env, jhmac, hmac_str); (*env)->ReleaseStringUTFChars(env, jhmac_b64, hmac_b64); (*env)->ReleaseStringUTFChars(env, jhmac_type, set_hmac_type); (*env)->ReleaseStringUTFChars(env, jfwtimeout, fw_timeout_str); (*env)->ReleaseStringUTFChars(env, jnat_access_str, nat_access_str); (*env)->ReleaseStringUTFChars(env, jnat_local, nat_local); return ourSpa; }
/* JNI interface: constructs arguments and calls main function */ int fwknop_sendSPAPacket( const char *allowip_str, const char *access_str, const char *destip_str, const char *passwd_str, const char *fw_timeout_str ) { fko_ctx_t ctx; fwknop_options_t opts; int res; char res_msg[MSG_BUFSIZE+1] = {0}; char spa_msg[MSG_BUFSIZE+1] = {0}; printf("**** Init fwknop ****\n"); memset(&opts, 0, sizeof(fwknop_options_t)); /* Sanity checks */ if(access_str == NULL) { sprintf(res_msg, "Error: Invalid or missing access string"); goto cleanup2; } if(allowip_str == NULL) { sprintf(res_msg, "Error: Invalid or missing allow IP"); goto cleanup2; } if(destip_str == NULL) { sprintf(res_msg, "Error: Invalid or missing destination IP"); goto cleanup2; } if(passwd_str == NULL) { sprintf(res_msg, "Error: Invalid or missing password"); goto cleanup2; } if(fw_timeout_str == NULL) { sprintf(res_msg, "Error: Invalid or missing firewall timeout value"); goto cleanup2; } /* Set our spa server info */ opts.spa_server_str = (char*)destip_str; opts.spa_dst_port = FKO_DEFAULT_PORT; /* Until we make this settable. */ /* Intialize the context */ res = fko_new(&ctx); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Unable to create FKO context", res)); goto cleanup2; } /* Set client timeout */ res = fko_set_spa_client_timeout(ctx, atoi(fw_timeout_str)); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting FW timeout", res)); goto cleanup; } /* Set the spa message string */ snprintf(spa_msg, MSG_BUFSIZE, "%s,%s", allowip_str, access_str); res = fko_set_spa_message(ctx, spa_msg); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA request message", res)); goto cleanup; } /* Finalize the context data (Encrypt and encode). */ res = fko_spa_data_final(ctx, (char*)passwd_str); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error generating SPA data", res)); goto cleanup; } res = fko_get_spa_data(ctx, &opts.spa_data); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error getting SPA data", res)); goto cleanup; } /* --DSS NOTE: At this point, we could just return the SPA data * to the caller and use the Java network libs to send * the packet and eliminate the spa_comm code altogether. */ /* Send the spa data packet */ res = send_spa_packet(&opts); if (res < 0) { sprintf(res_msg, "Error: send_spa_packet: packet not sent."); } else if (res == 0) { sprintf(res_msg, "Error: send_spa_packet: Empty packet sent."); } else { sprintf(res_msg, "SPA Packet sent successfully."); } cleanup: /* Release the resources used by the fko context. */ fko_destroy(ctx); cleanup2: /* Log and return a string of success or error message. * This can be enhanced semantically with codes. */ printf("%s\n", res_msg); printf("**** Closing fwknop ****\n"); return res; // (*env)->NewStringUTF(env, res_msg); }
static void spa_encoded_msg_fuzzing(void) { fko_ctx_t decode_ctx = NULL; int res = 0, pkt_id, require_success, require_digest, digest_type, msg_len; int line_ctr = 0, spa_payload_ctr = 0; FILE *fz = NULL; char line[MAX_LINE_LEN] = {0}; char b64_encoded_msg[MAX_LINE_LEN] = {0}; unsigned char b64_decoded_msg[MAX_LINE_LEN] = {0}; /* fuzzing file contents (or from stdin) are formatted like this: * * <pkt_ID> <status: success|fail> <digest: yes|no> <digest type> <base64_SPA_payload> */ if ((fz = fopen("fuzz_spa_payloads", "r")) == NULL) return; while ((fgets(line, MAX_LINE_LEN, fz)) != NULL) { line_ctr++; line[MAX_LINE_LEN-1] = '\0'; if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; if(IS_EMPTY_LINE(line[0])) continue; if(sscanf(line, "%d %d %d %d %s", &pkt_id, &require_success, &require_digest, &digest_type, b64_encoded_msg) != 5) { printf("[+] fuzzing parsing error at line: %d\n", line_ctr); continue; } msg_len = fko_base64_decode(b64_encoded_msg, b64_decoded_msg); spa_payload_ctr++; fko_new(&decode_ctx); if ((res = fko_set_encoded_data(decode_ctx, (char *) b64_decoded_msg, msg_len, require_digest, digest_type)) != FKO_SUCCESS) { printf("[-] pkt_id: %d, fko_set_encoded_data(): %s\n", pkt_id, fko_errstr(res)); } res = fko_decode_spa_data(decode_ctx); if (require_success) { if (res != FKO_SUCCESS) { printf("[-] pkt_id: %d, expected decode success but: fko_decode_spa_data(): %s\n", pkt_id, fko_errstr(res)); } } else { if (res == FKO_SUCCESS) { printf("[-] pkt_id: %d, expected decode failure but: fko_decode_spa_data(): %s\n", pkt_id, fko_errstr(res)); } } fko_destroy(decode_ctx); memset(line, 0x0, MAX_LINE_LEN); memset(b64_encoded_msg, 0x0, MAX_LINE_LEN); } fclose(fz); printf("[+] Sent %d SPA payloads through libfko encode/decode cycle...\n", spa_payload_ctr); return; }
static void test_loop_compounded(void) { fko_ctx_t ctx = NULL, decrypt_ctx = NULL; char *spa_data = NULL; int i, j, k, l, res; for (i=0; i<FCN_CALLS; i++) { fko_new(&ctx); res = fko_set_spa_client_timeout(ctx, i); if (res != FKO_SUCCESS) printf("fko_set_spa_client_timeout(): %s\n", fko_errstr(res)); for (j=-1; j<FKO_LAST_MSG_TYPE+1; j++) { res = fko_set_spa_message_type(ctx, j); if (res != FKO_SUCCESS) printf("fko_set_spa_message_type(): %s\n", fko_errstr(res)); res = fko_set_timestamp(ctx, 100); if (res != FKO_SUCCESS) printf("fko_set_timestamp(): %s\n", fko_errstr(res)); fko_set_spa_message(ctx, "1.1.1.1,tcp/22"); res = fko_set_spa_message(ctx, "123.123.123.123,tcp/22"); if (res != FKO_SUCCESS) printf("fko_set_spa_message(): %s\n", fko_errstr(res)); res = fko_set_spa_nat_access(ctx, "1.2.3.4,1234"); if (res != FKO_SUCCESS) printf("fko_set_spa_nat_access(): %s\n", fko_errstr(res)); res = fko_set_username(ctx, "someuser"); if (res != FKO_SUCCESS) printf("fko_set_username(): %s\n", fko_errstr(res)); res = fko_set_spa_server_auth(ctx, "passwd"); if (res != FKO_SUCCESS) printf("fko_set_spa_server_auth(): %s\n", fko_errstr(res)); res = fko_set_spa_hmac_type(ctx, FKO_HMAC_SHA256); if (res != FKO_SUCCESS) printf("fko_set_spa_hmac_type(): %s\n", fko_errstr(res)); for (k=-4; k<=16; k+=4) { for (l=-4; l<=16; l+=4) { res = fko_spa_data_final(ctx, ENC_KEY, k, HMAC_KEY, l); if (res == FKO_SUCCESS) { res = fko_get_spa_data(ctx, &spa_data); if (res == FKO_SUCCESS) { res = fko_new_with_data(&decrypt_ctx, spa_data, NULL, 0, FKO_ENC_MODE_CBC, HMAC_KEY, l, FKO_HMAC_SHA256); if (res == FKO_SUCCESS) { res = fko_decrypt_spa_data(decrypt_ctx, ENC_KEY, k); if (res != FKO_SUCCESS) printf("fko_decrypt_spa_data(): %s\n", fko_errstr(res)); fko_destroy(decrypt_ctx); decrypt_ctx = NULL; spa_calls += 13; spa_compounded_calls += 13; } else { printf("fko_new_with_data(): %s\n", fko_errstr(res)); } } else { printf("fko_get_spa_data(): %s\n", fko_errstr(res)); } } else { printf("fko_spa_data_final(): %s\n", fko_errstr(res)); } } } } fko_destroy(ctx); ctx = NULL; spa_calls += 3; spa_compounded_calls += 3; } }
/* Initialize an fko context. */ int fko_new(fko_ctx_t *r_ctx) { fko_ctx_t ctx; int res; char *ver; ctx = calloc(1, sizeof *ctx); if(ctx == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* Set default values and state. * * Note: We have to explicitly set the ctx->state to initialized * just before making an fko_xxx function call, then set it * back to zero just afer. During initialization, we need * to make these functions think they are operating on an * initialized context, or else they would fail. */ /* Set the version string. */ ctx->initval = FKO_CTX_INITIALIZED; ver = strdup(FKO_PROTOCOL_VERSION); ctx->initval = 0; if(ver == NULL) { free(ctx); return(FKO_ERROR_MEMORY_ALLOCATION); } ctx->version = ver; /* Rand value. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_rand_value(ctx, NULL); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } /* Username. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_username(ctx, NULL); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } /* Timestamp. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_timestamp(ctx, 0); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } /* Default Digest Type. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_spa_digest_type(ctx, FKO_DEFAULT_DIGEST); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } /* Default Message Type. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_spa_message_type(ctx, FKO_DEFAULT_MSG_TYPE); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } /* Default Encryption Type. */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_spa_encryption_type(ctx, FKO_DEFAULT_ENCRYPTION); ctx->initval = 0; if(res != FKO_SUCCESS) { fko_destroy(ctx); return res; } #if HAVE_LIBGPGME /* Set gpg signature verify on. */ ctx->verify_gpg_sigs = 1; #endif /* HAVE_LIBGPGME */ /* Now we mean it. */ ctx->initval = FKO_CTX_INITIALIZED; FKO_SET_CTX_INITIALIZED(ctx); *r_ctx = ctx; return(FKO_SUCCESS); }
/* Initialize an fko context with external (encrypted/encoded) data. * This is used to create a context with the purpose of decoding * and parsing the provided data into the context data. */ int fko_new_with_data(fko_ctx_t *r_ctx, const char * const enc_msg, const char * const dec_key, const int dec_key_len, int encryption_mode, const char * const hmac_key, const int hmac_key_len, const int hmac_type) { fko_ctx_t ctx = NULL; int res = FKO_SUCCESS; /* Are we optimistic or what? */ int enc_msg_len; #if HAVE_LIBFIU fiu_return_on("fko_new_with_data_msg", FKO_ERROR_INVALID_DATA_FUNCS_NEW_ENCMSG_MISSING); #endif if(enc_msg == NULL) return(FKO_ERROR_INVALID_DATA_FUNCS_NEW_ENCMSG_MISSING); #if HAVE_LIBFIU fiu_return_on("fko_new_with_data_keylen", FKO_ERROR_INVALID_KEY_LEN); #endif if(dec_key_len < 0 || hmac_key_len < 0) return(FKO_ERROR_INVALID_KEY_LEN); ctx = calloc(1, sizeof *ctx); if(ctx == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); enc_msg_len = strnlen(enc_msg, MAX_SPA_ENCODED_MSG_SIZE); if(! is_valid_encoded_msg_len(enc_msg_len)) { free(ctx); return(FKO_ERROR_INVALID_DATA_FUNCS_NEW_MSGLEN_VALIDFAIL); } /* First, add the data to the context. */ ctx->encrypted_msg = strdup(enc_msg); ctx->encrypted_msg_len = enc_msg_len; if(ctx->encrypted_msg == NULL) { free(ctx); return(FKO_ERROR_MEMORY_ALLOCATION); } /* Default Encryption Mode (Rijndael in CBC mode) */ ctx->initval = FKO_CTX_INITIALIZED; res = fko_set_spa_encryption_mode(ctx, encryption_mode); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* HMAC digest type */ res = fko_set_spa_hmac_type(ctx, hmac_type); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Check HMAC if the access stanza had an HMAC key */ if(hmac_key_len > 0 && hmac_key != NULL) res = fko_verify_hmac(ctx, hmac_key, hmac_key_len); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; return res; } /* Consider it initialized here. */ FKO_SET_CTX_INITIALIZED(ctx); /* If a decryption key is provided, go ahead and decrypt and decode. */ if(dec_key != NULL) { res = fko_decrypt_spa_data(ctx, dec_key, dec_key_len); if(res != FKO_SUCCESS) { fko_destroy(ctx); ctx = NULL; *r_ctx = NULL; /* Make sure the caller ctx is null just in case */ return(res); } } #if HAVE_LIBGPGME /* Set gpg signature verify on. */ ctx->verify_gpg_sigs = 1; #endif /* HAVE_LIBGPGME */ *r_ctx = ctx; return(res); }
/* For replay attack detection */ static int get_raw_digest(char **digest, char *pkt_data) { fko_ctx_t ctx = NULL; char *tmp_digest = NULL; int res = FKO_SUCCESS; short raw_digest_type = -1; /* initialize an FKO context with no decryption key just so * we can get the outer message digest */ res = fko_new_with_data(&ctx, (char *)pkt_data, NULL, 0, FKO_DEFAULT_ENC_MODE, NULL, 0, 0); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_FKO_CTX_ERROR); } res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } res = fko_get_raw_spa_digest_type(ctx, &raw_digest_type); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error getting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } /* Make sure the digest type is what we expect */ if(raw_digest_type != FKO_DEFAULT_DIGEST) { log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } res = fko_set_raw_spa_digest(ctx); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } res = fko_get_raw_spa_digest(ctx, &tmp_digest); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error getting digest from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } *digest = strdup(tmp_digest); if (*digest == NULL) res = SPA_MSG_ERROR; /* really a strdup() memory allocation problem */ fko_destroy(ctx); ctx = NULL; return res; }
/* Process the SPA packet data */ int 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, *gpg_id; time_t now_ts; int res, status, ts_diff, enc_type; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Get the access.conf data for the stanza that matches this incoming * source IP address. */ acc_stanza_t *acc = acc_check_source(opts, spa_pkt->packet_src_ip); inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_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). */ res = preprocess_spa_data(opts, spadat.pkt_source_ip); if(res != FKO_SUCCESS) return(SPA_MSG_NOT_SPA_DATA); log_msg(LOG_INFO, "SPA Packet from IP: %s received.", spadat.pkt_source_ip); if(acc == NULL) { log_msg(LOG_WARNING, "No access data found for source IP: %s", spadat.pkt_source_ip ); return(SPA_MSG_ACCESS_DENIED); } if(opts->verbose > 1) log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data); /* 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(enc_type == FKO_ENCRYPTION_RIJNDAEL) { if(acc->key != NULL) res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key); else { log_msg(LOG_ERR, "No KEY for RIJNDAEL encrypted messages"); return(SPA_MSG_FKO_CTX_ERROR); } } else if(enc_type == FKO_ENCRYPTION_GPG) { /* For GPG we create the new context without decrypting on the fly * so we can set some GPG parameters first. */ if(acc->gpg_decrypt_pw != NULL) { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error creating fko context (before decryption): %s", fko_errstr(res) ); return(SPA_MSG_FKO_CTX_ERROR); } /* Set whatever GPG parameters we have. */ if(acc->gpg_home_dir != NULL) res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting GPG keyring path to %s: %s", acc->gpg_home_dir, fko_errstr(res) ); return(SPA_MSG_FKO_CTX_ERROR); } if(acc->gpg_decrypt_id != NULL) fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id); /* If GPG_REQUIRE_SIG is set for this acc stanza, then set * the FKO context accordingly and check the other GPG Sig- * related parameters. This also applies when REMOTE_ID is * set. */ if(acc->gpg_require_sig) { fko_set_gpg_signature_verify(ctx, 1); /* Set whether or not to ignore signature verification errors. */ fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error); } else { fko_set_gpg_signature_verify(ctx, 0); fko_set_gpg_ignore_verify_error(ctx, 1); } /* Now decrypt the data. */ res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw); } else { log_msg(LOG_ERR, "No GPG_DECRYPT_PW for GPG encrypted messages"); return(SPA_MSG_FKO_CTX_ERROR); } } else { log_msg(LOG_ERR, "Unable to determing encryption type. Got type=%i.", enc_type); return(SPA_MSG_FKO_CTX_ERROR); } /* Do we have a valid FKO context? */ if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error creating fko context: %s", fko_errstr(res)); if(IS_GPG_ERROR(res)) log_msg(LOG_WARNING, " - GPG ERROR: %s", fko_gpg_errstr(ctx)); goto clean_and_bail; } /* At this point, we assume the SPA data is valid. Now we need to see * if it meets our access criteria. */ if(opts->verbose > 2) log_msg(LOG_INFO, "SPA Decode (res=%i):\n%s", res, dump_ctx(ctx)); /* 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(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig) { res = fko_get_gpg_signature_id(ctx, &gpg_id); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error pulling the GPG signature ID from the context: %s", fko_gpg_errstr(ctx)); goto clean_and_bail; } if(opts->verbose) log_msg(LOG_INFO, "Incoming SPA data signed by '%s'.", gpg_id); if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id)) { log_msg(LOG_WARNING, "Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.", gpg_id); goto clean_and_bail; } } /* Check for replays if so configured. */ if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { res = replay_check(opts, ctx); if(res != 0) /* non-zero means we have seen this packet before. */ goto clean_and_bail; } /* Populate our spa data struct for future reference. */ res = get_spa_data_fields(ctx, &spadat); /* 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. */ if(spadat.client_timeout > 0) spadat.fw_access_timeout = spadat.client_timeout; else if(acc->fw_access_timeout > 0) spadat.fw_access_timeout = acc->fw_access_timeout; else spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT; if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "Unexpected error pulling SPA data from the context: %s", fko_errstr(res)); res = SPA_MSG_ERROR; goto clean_and_bail; } /* Check packet age if so configured. */ if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { time(&now_ts); ts_diff = now_ts - spadat.timestamp; if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE])) { log_msg(LOG_WARNING, "SPA data is too old (%i seconds).", ts_diff); res = SPA_MSG_TOO_OLD; goto clean_and_bail; } } /* 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, "Error parsing SPA message string: %s", fko_errstr(res)); res = SPA_MSG_ERROR; goto clean_and_bail; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); strlcpy(spadat.spa_message_remain, spa_ip_demark+1, 1024); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0) { if(acc->require_source_address) { log_msg(LOG_WARNING, "Got 0.0.0.0 when valid source IP was required." ); res = SPA_MSG_ACCESS_DENIED; goto clean_and_bail; } spadat.use_src_ip = spadat.pkt_source_ip; } else spadat.use_src_ip = spadat.spa_message_src_ip; /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ if(acc->require_username != NULL) { if(strcmp(spadat.username, acc->require_username) != 0) { log_msg(LOG_WARNING, "Username in SPA data (%s) does not match required username: %s", spadat.username, acc->require_username ); res = SPA_MSG_ACCESS_DENIED; goto clean_and_bail; } } /* Take action based on SPA message type. */ /* Command messages. */ if(spadat.message_type == FKO_COMMAND_MSG) { if(!acc->enable_cmd_exec) { log_msg(LOG_WARNING, "SPA Command message are not allowed in the current configuration." ); res = SPA_MSG_ACCESS_DENIED; } else { log_msg(LOG_INFO, "Processing SPA Command message: command='%s'.", spadat.spa_message_remain ); /* Do we need to become another user? If so, we call * run_extcmd_as and pass the cmd_exec_uid. */ if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0) { if(opts->verbose) log_msg(LOG_INFO, "Setting effective user to %s (UID=%i) before running command.", acc->cmd_exec_user, acc->cmd_exec_uid); res = run_extcmd_as(acc->cmd_exec_uid, spadat.spa_message_remain, NULL, 0, 0); } else /* Just run it as we are (root that is). */ res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5); /* --DSS XXX: I have found that the status (and res for that * matter) have been unreliable indicators of the * actual exit status of some commands. Not sure * why yet. For now, we will take what we get. */ status = WEXITSTATUS(res); if(opts->verbose > 2) log_msg(LOG_WARNING, "CMD_EXEC: command returned %i", status); if(status != 0) res = SPA_MSG_COMMAND_ERROR; } goto clean_and_bail; } /* 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(! acc_check_port_access(acc, spadat.spa_message_remain)) { log_msg(LOG_WARNING, "One or more requested protocol/ports was denied per access.conf." ); res = SPA_MSG_ACCESS_DENIED; goto clean_and_bail; } /* At this point, we can process the SPA request. */ res = process_spa_request(opts, &spadat); clean_and_bail: if(ctx != NULL) fko_destroy(ctx); return(res); }
/* For replay attack detection */ static int get_raw_digest(char **digest, char *pkt_data) { fko_ctx_t ctx = NULL; char *tmp_digest = NULL; int res = FKO_SUCCESS; short raw_digest_type = -1; /* initialize an FKO context with no decryption key just so * we can get the outer message digest */ //pkt_data:SPAÔʼÊý¾Ý°ü¡ //ÔÚ²»´«Èë½âÃÜÃÜÔ¿µÄÇé¿öÏÂÎÒÃÇ¿ÉÒÔ»ñÈ¡½ÓÊÕµ½µÄmessageÕªÒª¡£ res = fko_new_with_data(&ctx, (char *)pkt_data, NULL, 0, FKO_DEFAULT_ENC_MODE, NULL, 0, 0); //½«pkt_data¸´ÖƵ½ctx->encrypted_msgÖС£ if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_FKO_CTX_ERROR); } //ÉèÖÃSPAÕªÒªÀàÐÍ(default:SHA256)¡£ res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } //»ñÈ¡SPAÕªÒªÀàÐÍ(SHA256)¡£ res = fko_get_raw_spa_digest_type(ctx, &raw_digest_type); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error getting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } /* Make sure the digest type is what we expect */ //È·±£ÊÇÎÒÃÇËùÆÚÍûµÄÕªÒªÀàÐÍ¡£ if(raw_digest_type != FKO_DEFAULT_DIGEST) { log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } //¼ÆËãctx->encrypted_msgµÄÕªÒª(default:SHA256)£¬´æÈëctx->raw_digest¡£ res = fko_set_raw_spa_digest(ctx); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error setting digest for SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } //»ñÈ¡Server¼ÆËã³öµÄÕªÒª¡£tmp_digest=ctx->raw_digest; res = fko_get_raw_spa_digest(ctx, &tmp_digest); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "Error getting digest from SPA data: %s", fko_errstr(res)); fko_destroy(ctx); ctx = NULL; return(SPA_MSG_DIGEST_ERROR); } *digest = strdup(tmp_digest); if (*digest == NULL) res = SPA_MSG_ERROR; /* really a strdup() memory allocation problem */ fko_destroy(ctx); //ÊÍ·Åctx½á¹¹¡£ ctx = NULL; return res; }
/* Function for accepting password input from a file */ void get_key_file(char *key, int *key_len, const char *key_file, fko_ctx_t ctx, const fko_cli_options_t *options) { FILE *pwfile_ptr; unsigned int numLines = 0, i = 0, found_dst; char conf_line_buf[MAX_LINE_LEN] = {0}; char tmp_char_buf[MAX_LINE_LEN] = {0}; char *lptr; memset(key, 0x00, MAX_KEY_LEN+1); if ((pwfile_ptr = fopen(key_file, "r")) == NULL) { log_msg(LOG_VERBOSITY_ERROR, "Could not open config file: %s", key_file); fko_destroy(ctx); ctx = NULL; exit(EXIT_FAILURE); } while ((fgets(conf_line_buf, MAX_LINE_LEN, pwfile_ptr)) != NULL) { numLines++; conf_line_buf[MAX_LINE_LEN-1] = '\0'; lptr = conf_line_buf; memset(tmp_char_buf, 0x0, MAX_LINE_LEN); while (*lptr == ' ' || *lptr == '\t' || *lptr == '=') lptr++; /* Get past comments and empty lines. */ if (*lptr == '#' || *lptr == '\n' || *lptr == '\r' || *lptr == '\0' || *lptr == ';') continue; /* Look for a line like "<SPA destination IP>: <password>" - this allows * multiple keys to be placed within the same file, and the client will * reference the matching one for the SPA server we are contacting */ found_dst = 1; for (i=0; i < strlen(options->spa_server_str); i++) if (*lptr++ != options->spa_server_str[i]) found_dst = 0; if (! found_dst) continue; if (*lptr == ':') lptr++; else continue; /* Skip whitespace until we get to the password */ while (*lptr == ' ' || *lptr == '\t' || *lptr == '=') lptr++; i = 0; while (*lptr != '\0' && *lptr != '\n') { key[i] = *lptr; lptr++; i++; } key[i] = '\0'; } fclose(pwfile_ptr); if (key[0] == '\0') { log_msg(LOG_VERBOSITY_ERROR, "Could not get key for IP: %s from: %s", options->spa_server_str, key_file); fko_destroy(ctx); ctx = NULL; exit(EXIT_FAILURE); } *key_len = strlen(key); 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; }
static void test_loop(int new_ctx_flag, int destroy_ctx_flag) { fko_ctx_t ctx = NULL, decrypt_ctx = NULL; int i, j; char *spa_data = NULL, encode_buf[100], decode_buf[100]; printf("[+] test_loop(): %s, %s\n", new_ctx_flag == NEW_CTX ? "NEW_CTX" : "NO_NEW_CTX", destroy_ctx_flag == CTX_DESTROY ? "DESTROY_CTX" : "NO_DESTROY_CTX"); printf("fko_new(): %s\n", fko_errstr(fko_new(&ctx))); fko_destroy(ctx); ctx = NULL; printf("fko_new(): %s\n", fko_errstr(fko_new(&ctx))); spa_func_getset_int(&ctx, "fko_set_spa_client_timeout", &fko_set_spa_client_timeout, "fko_get_spa_client_timeout", &fko_get_spa_client_timeout, -F_INT, F_INT, 10, new_ctx_flag, destroy_ctx_flag); spa_func_getset_short(&ctx, "fko_set_spa_message_type", &fko_set_spa_message_type, "fko_get_spa_message_type", &fko_get_spa_message_type, FKO_COMMAND_MSG-F_INT, FKO_LAST_MSG_TYPE+F_INT, FKO_ACCESS_MSG, NO_DIGEST, new_ctx_flag, destroy_ctx_flag); spa_func_int(&ctx, "fko_set_timestamp", &fko_set_timestamp, -F_INT, F_INT, 10, new_ctx_flag, destroy_ctx_flag); for (i=0; i<FCN_CALLS; i++) { printf("fko_set_spa_message(1.1.1.1,tcp/22): %s\n", fko_errstr(fko_set_spa_message(ctx, "1.1.1.1,tcp/22"))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { printf("fko_set_spa_nat_access(1.2.3.4,1234): %s\n", fko_errstr(fko_set_spa_nat_access(ctx, "1.2.3.4,1234"))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { printf("fko_set_username(someuser): %s\n", fko_errstr(fko_set_username(ctx, "someuser"))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } spa_func_getset_short(&ctx, "fko_set_spa_encryption_type", &fko_set_spa_encryption_type, "fko_get_spa_encryption_type", &fko_get_spa_encryption_type, FKO_ENCRYPTION_INVALID_DATA-F_INT, FKO_LAST_ENCRYPTION_TYPE+F_INT, FKO_ENCRYPTION_RIJNDAEL, NO_DIGEST, new_ctx_flag, destroy_ctx_flag); spa_func_getset_int(&ctx, "fko_set_spa_encryption_mode", &fko_set_spa_encryption_mode, "fko_get_spa_encryption_mode", &fko_get_spa_encryption_mode, FKO_ENC_MODE_UNKNOWN-F_INT, FKO_LAST_ENC_MODE+F_INT, FKO_ENC_MODE_CBC, new_ctx_flag, destroy_ctx_flag); if (ENABLE_GPG_TESTS) { for (i=0; i<FCN_CALLS; i++) { printf("fko_set_spa_encryption_type(FKO_ENCRYPTION_GPG): %s\n", fko_errstr(fko_set_spa_encryption_type(ctx, FKO_ENCRYPTION_GPG))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { printf("fko_set_gpg_home_dir(/home/mbr/.gnupg): %s\n", fko_errstr(fko_set_gpg_home_dir(ctx, "/home/mbr/.gnupg"))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { printf("fko_set_gpg_recipient(1234asdf): %s\n", fko_errstr(fko_set_gpg_recipient(ctx, "1234asdf"))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } } spa_func_getset_short(&ctx, "fko_set_spa_digest_type", &fko_set_spa_digest_type, "fko_get_spa_digest_type", &fko_get_spa_digest_type, FKO_DIGEST_INVALID_DATA-F_INT, FKO_LAST_DIGEST_TYPE+F_INT, FKO_DEFAULT_DIGEST, DO_DIGEST, new_ctx_flag, destroy_ctx_flag); spa_func_getset_short(&ctx, "fko_set_raw_spa_digest_type", &fko_set_spa_digest_type, "fko_get_raw_spa_digest_type", &fko_get_spa_digest_type, FKO_DIGEST_INVALID_DATA-F_INT, FKO_LAST_DIGEST_TYPE+F_INT, FKO_DEFAULT_DIGEST, RAW_DIGEST, new_ctx_flag, destroy_ctx_flag); spa_func_getset_short(&ctx, "fko_set_spa_hmac_type", &fko_set_spa_hmac_type, "fko_get_spa_hmac_type", &fko_get_spa_hmac_type, FKO_HMAC_INVALID_DATA-F_INT, FKO_LAST_HMAC_MODE+F_INT, FKO_HMAC_SHA256, NO_DIGEST, new_ctx_flag, destroy_ctx_flag); printf("Trying encrypt / authenticate step with bogus key lengths...\n"); for (i=-100; i < 200; i += 10) { for (j=-100; j < 200; j += 10) { fko_spa_data_final(ctx, ENC_KEY, i, HMAC_KEY, j); fko_spa_data_final(ctx, NULL, i, HMAC_KEY, j); fko_spa_data_final(ctx, ENC_KEY, i, NULL, j); fko_spa_data_final(ctx, NULL, i, NULL, j); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, NO_PRINT); spa_calls += 4; } } for (i=0; i<FCN_CALLS; i++) { printf("fko_spa_data_final(ENC_KEY, 16, HMAC_KEY, 16): %s\n", fko_errstr(fko_spa_data_final(ctx, ENC_KEY, 16, HMAC_KEY, 16))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { printf("fko_get_spa_data(): %s\n", fko_errstr(fko_get_spa_data(ctx, &spa_data))); printf(" SPA DATA: %s\n", spa_data == NULL ? "<NULL>" : spa_data); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } printf("fko_new_with_data(): %s (data: %s)\n", fko_errstr(fko_new_with_data(&decrypt_ctx, spa_data, NULL, 0, FKO_ENC_MODE_CBC, NULL, 0, FKO_HMAC_SHA256)), spa_data); /* verify hmac, decrypt, and display ctx all together*/ for (i=0; i<FCN_CALLS; i++) { display_ctx(decrypt_ctx); printf("fko_verify_hmac() (1): %s\n", fko_errstr(fko_verify_hmac(decrypt_ctx, HMAC_KEY, 16))); printf("fko_decrypt_spa_data() (1): %s\n", fko_errstr(fko_decrypt_spa_data(decrypt_ctx, ENC_KEY, 16))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } /* now, separately verify hmac, decrypt, and display ctx */ for (i=0; i<FCN_CALLS; i++) { printf("fko_verify_hmac() (2): %s\n", fko_errstr(fko_verify_hmac(decrypt_ctx, HMAC_KEY, 16))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } /* now decrypt */ for (i=0; i<FCN_CALLS; i++) { printf("fko_decrypt_spa_data() (2): %s\n", fko_errstr(fko_decrypt_spa_data(decrypt_ctx, ENC_KEY, 16))); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } for (i=0; i<FCN_CALLS; i++) { display_ctx(decrypt_ctx); ctx_update(&ctx, new_ctx_flag, destroy_ctx_flag, DO_PRINT); } /* NULL tests */ fko_set_rand_value(ctx, NULL); fko_set_rand_value(ctx, NULL); fko_set_username(ctx, NULL); fko_set_username(ctx, NULL); fko_set_spa_message(ctx, NULL); fko_set_spa_message(ctx, NULL); fko_set_spa_nat_access(ctx, NULL); fko_set_spa_nat_access(ctx, NULL); fko_set_spa_server_auth(ctx, NULL); fko_set_spa_server_auth(ctx, NULL); fko_set_spa_data(ctx, NULL); fko_set_spa_data(ctx, NULL); spa_calls += 12; for (i=0; i<FCN_CALLS; i++) { fko_destroy(ctx); ctx = NULL; } for (i=0; i<FCN_CALLS; i++) { fko_destroy(decrypt_ctx); decrypt_ctx = NULL; } /* exercise the base64 encode/decode wrapper */ fko_base64_encode((unsigned char *)ENC_KEY, encode_buf, 16); fko_base64_decode(encode_buf, (unsigned char *)decode_buf); /* call fko_errstr() across valid and invalid values */ for (i=-5; i < FKO_LAST_ERROR+5; i++) { printf("libfko error (%d): %s\n", i, fko_errstr(i)); spa_calls++; } 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 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; }
/* JNI interface: constructs arguments and calls main function */ jstring Java_com_max2idea_android_fwknop_Fwknop_sendSPAPacket(JNIEnv* env, jobject thiz) { fko_ctx_t ctx; fwknop_options_t opts; int res, hmac_str_len = 0; char res_msg[MSG_BUFSIZE+1] = {0}; char spa_msg[MSG_BUFSIZE+1] = {0}; LOGV("**** Init fwknop ****"); memset(&opts, 0, sizeof(fwknop_options_t)); /* Read the member values from the Java Object that called sendSPAPacket() method */ jclass c = (*env)->GetObjectClass(env, thiz); jfieldID fid = (*env)->GetFieldID(env, c, "access_str", "Ljava/lang/String;"); jstring jaccess = (*env)->GetObjectField(env, thiz, fid); const char *access_str = (*env)->GetStringUTFChars(env, jaccess, 0); fid = (*env)->GetFieldID(env, c, "allowip_str", "Ljava/lang/String;"); jstring jallowip = (*env)->GetObjectField(env, thiz, fid); const char *allowip_str = (*env)->GetStringUTFChars(env, jallowip, 0); fid = (*env)->GetFieldID(env, c, "destip_str", "Ljava/lang/String;"); jstring jdestip = (*env)->GetObjectField(env, thiz, fid); const char *destip_str = (*env)->GetStringUTFChars(env, jdestip, 0); fid = (*env)->GetFieldID(env, c, "passwd_str", "Ljava/lang/String;"); jstring jpasswd = (*env)->GetObjectField(env, thiz, fid); const char *passwd_str = (*env)->GetStringUTFChars(env, jpasswd, 0); fid = (*env)->GetFieldID(env, c, "hmac_str", "Ljava/lang/String;"); jstring jhmac = (*env)->GetObjectField(env, thiz, fid); const char *hmac_str = (*env)->GetStringUTFChars(env, jhmac, 0); fid = (*env)->GetFieldID(env, c, "fw_timeout_str", "Ljava/lang/String;"); jstring jfwtimeout = (*env)->GetObjectField(env, thiz, fid); const char *fw_timeout_str = (*env)->GetStringUTFChars(env, jfwtimeout, 0); /* Sanity checks */ if(access_str == NULL) { sprintf(res_msg, "Error: Invalid or missing access string"); goto cleanup2; } if(allowip_str == NULL) { sprintf(res_msg, "Error: Invalid or missing allow IP"); goto cleanup2; } if(destip_str == NULL) { sprintf(res_msg, "Error: Invalid or missing destination IP"); goto cleanup2; } if(passwd_str == NULL) { sprintf(res_msg, "Error: Invalid or missing password"); goto cleanup2; } if(fw_timeout_str == NULL) { sprintf(res_msg, "Error: Invalid or missing firewall timeout value"); goto cleanup2; } /* Using an HMAC is optional (currently) */ if(hmac_str != NULL) { hmac_str_len = (int)strlen(hmac_str); } /* Set our spa server info */ opts.spa_server_str = (char*)destip_str; opts.spa_dst_port = FKO_DEFAULT_PORT; /* Until we make this settable. */ /* Intialize the context */ res = fko_new(&ctx); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Unable to create FKO context", res)); goto cleanup2; } /* Set client timeout */ res = fko_set_spa_client_timeout(ctx, atoi(fw_timeout_str)); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting FW timeout", res)); goto cleanup; } /* Set the spa message string */ snprintf(spa_msg, MSG_BUFSIZE, "%s,%s", allowip_str, access_str); res = fko_set_spa_message(ctx, spa_msg); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA request message", res)); goto cleanup; } /* Set the HMAC mode if necessary */ if (hmac_str_len > 0) { res = fko_set_spa_hmac_type(ctx, FKO_DEFAULT_HMAC_MODE); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error setting SPA HMAC type", res)); goto cleanup; } } /* Finalize the context data (Encrypt and encode). */ res = fko_spa_data_final(ctx, (char*)passwd_str, (int)strlen(passwd_str), (char *)hmac_str, hmac_str_len); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error generating SPA data", res)); goto cleanup; } res = fko_get_spa_data(ctx, &opts.spa_data); if (res != FKO_SUCCESS) { strcpy(res_msg, fko_errmsg("Error getting SPA data", res)); goto cleanup; } /* --DSS NOTE: At this point, we could just return the SPA data * to the caller and use the Java network libs to send * the packet and eliminate the spa_comm code altogether. */ /* Send the spa data packet */ res = send_spa_packet(&opts); if (res < 0) { sprintf(res_msg, "Error: send_spa_packet: packet not sent."); } else if (res == 0) { sprintf(res_msg, "Error: send_spa_packet: Empty packet sent."); } else { sprintf(res_msg, "SPA Packet sent successfully."); } cleanup: /* Release the resources used by the fko context. */ fko_destroy(ctx); cleanup2: /* Release mem */ (*env)->ReleaseStringUTFChars(env, jaccess, access_str); (*env)->ReleaseStringUTFChars(env, jallowip, allowip_str); (*env)->ReleaseStringUTFChars(env, jdestip, destip_str); (*env)->ReleaseStringUTFChars(env, jpasswd, passwd_str); (*env)->ReleaseStringUTFChars(env, jhmac, hmac_str); (*env)->ReleaseStringUTFChars(env, jfwtimeout, fw_timeout_str); /* Log and return a string of success or error message. * This can be enhanced semantically with codes. */ LOGV("%s", res_msg); return (*env)->NewStringUTF(env, res_msg); }