fwknop-2.6.10/0000775000175000017500000000000013332165577010123 500000000000000fwknop-2.6.10/python/0000775000175000017500000000000013332165576011443 500000000000000fwknop-2.6.10/python/fkomodule.c0000664000175000017500000013051113332165274013510 00000000000000/* ***************************************************************************** * * File: fkomodule.c * * Purpose: Python wrapper module for the fwknop library (libfko). * * Fwknop is developed primarily by the people listed in the file 'AUTHORS'. * Copyright (C) 2009-2015 fwknop developers and contributors. For a full * list of contributors, see the file 'CREDITS'. * * License (GNU General Public License): * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * ***************************************************************************** */ #include #include /* A lot to figure out yet... */ /* This will be our Error object. */ static PyObject *FKOError; /* FKO context functions. */ static PyObject * init_ctx(PyObject *self, PyObject *args); static PyObject * init_ctx_with_data(PyObject *self, PyObject *args); static PyObject * destroy_ctx(PyObject *self, PyObject *args); /* FKO SPA data functions. */ static PyObject * get_version(PyObject *self, PyObject *args); static PyObject * get_rand_value(PyObject *self, PyObject *args); static PyObject * set_rand_value(PyObject *self, PyObject *args); static PyObject * get_username(PyObject *self, PyObject *args); static PyObject * set_username(PyObject *self, PyObject *args); static PyObject * get_timestamp(PyObject *self, PyObject *args); static PyObject * set_timestamp(PyObject *self, PyObject *args); static PyObject * get_spa_digest_type(PyObject *self, PyObject *args); static PyObject * set_spa_digest_type(PyObject *self, PyObject *args); static PyObject * get_spa_encryption_type(PyObject *self, PyObject *args); static PyObject * set_spa_encryption_type(PyObject *self, PyObject *args); static PyObject * get_spa_message_type(PyObject *self, PyObject *args); static PyObject * set_spa_message_type(PyObject *self, PyObject *args); static PyObject * get_spa_message(PyObject *self, PyObject *args); static PyObject * set_spa_message(PyObject *self, PyObject *args); static PyObject * get_spa_nat_access(PyObject *self, PyObject *args); static PyObject * set_spa_nat_access(PyObject *self, PyObject *args); static PyObject * get_spa_server_auth(PyObject *self, PyObject *args); static PyObject * set_spa_server_auth(PyObject *self, PyObject *args); static PyObject * get_spa_client_timeout(PyObject *self, PyObject *args); static PyObject * set_spa_client_timeout(PyObject *self, PyObject *args); static PyObject * get_spa_digest(PyObject *self, PyObject *args); static PyObject * set_spa_digest(PyObject *self, PyObject *args); static PyObject * get_spa_data(PyObject *self, PyObject *args); static PyObject * set_spa_data(PyObject *self, PyObject *args); static PyObject * get_encoded_data(PyObject *self, PyObject *args); static PyObject * get_raw_spa_digest_type(PyObject *self, PyObject *args); static PyObject * set_raw_spa_digest_type(PyObject *self, PyObject *args); static PyObject * get_raw_spa_digest(PyObject *self, PyObject *args); static PyObject * set_raw_spa_digest(PyObject *self, PyObject *args); static PyObject * get_spa_encryption_mode(PyObject *self, PyObject *args); static PyObject * set_spa_encryption_mode(PyObject *self, PyObject *args); static PyObject * get_spa_hmac_type(PyObject *self, PyObject *args); static PyObject * set_spa_hmac_type(PyObject *self, PyObject *args); /* FKO other utility Functions. */ static PyObject * spa_data_final(PyObject *self, PyObject *args); static PyObject * decrypt_spa_data(PyObject *self, PyObject *args); static PyObject * encrypt_spa_data(PyObject *self, PyObject *args); static PyObject * decode_spa_data(PyObject *self, PyObject *args); static PyObject * encode_spa_data(PyObject *self, PyObject *args); static PyObject * encryption_type(PyObject *self, PyObject *args); static PyObject * key_gen(PyObject *self, PyObject *args); static PyObject * base64_encode(PyObject *self, PyObject *args); static PyObject * base64_decode(PyObject *self, PyObject *args); static PyObject * verify_hmac(PyObject *self, PyObject *args); static PyObject * set_spa_hmac(PyObject *self, PyObject *args); static PyObject * get_spa_hmac(PyObject *self, PyObject *args); /* FKO GPG-related Functions. */ static PyObject * get_gpg_recipient(PyObject *self, PyObject *args); static PyObject * set_gpg_recipient(PyObject *self, PyObject *args); static PyObject * get_gpg_signer(PyObject *self, PyObject *args); static PyObject * set_gpg_signer(PyObject *self, PyObject *args); static PyObject * get_gpg_home_dir(PyObject *self, PyObject *args); static PyObject * set_gpg_home_dir(PyObject *self, PyObject *args); static PyObject * get_gpg_signature_verify(PyObject *self, PyObject *args); static PyObject * set_gpg_signature_verify(PyObject *self, PyObject *args); static PyObject * get_gpg_ignore_verify_error(PyObject *self, PyObject *args); static PyObject * set_gpg_ignore_verify_error(PyObject *self, PyObject *args); static PyObject * get_gpg_exe(PyObject *self, PyObject *args); static PyObject * set_gpg_exe(PyObject *self, PyObject *args); static PyObject * get_gpg_signature_id(PyObject *self, PyObject *args); static PyObject * get_gpg_signature_fpr(PyObject *self, PyObject *args); static PyObject * get_gpg_signature_summary(PyObject *self, PyObject *args); static PyObject * get_gpg_signature_status(PyObject *self, PyObject *args); static PyObject * gpg_signature_id_match(PyObject *self, PyObject *args); static PyObject * gpg_signature_fpr_match(PyObject *self, PyObject *args); /* FKO error message functions. */ static PyObject * errstr(PyObject *self, PyObject *args); static PyObject * gpg_errstr(PyObject *self, PyObject *args); static PyMethodDef FKOMethods[] = { {"init_ctx", init_ctx, METH_VARARGS, "Initialize a new FKO context."}, {"init_ctx_with_data", init_ctx_with_data, METH_VARARGS, "Initialize a new FKO context with encoded SPA data and optional key."}, {"destroy_ctx", destroy_ctx, METH_VARARGS, "Destroy an FKO context and release resources it was using."}, {"get_version", get_version, METH_VARARGS, "Returns the SPA protocol version string"}, {"get_rand_value", get_rand_value, METH_VARARGS, "Returns the random value string for this context"}, {"set_rand_value", set_rand_value, METH_VARARGS, "Sets or generates the random value string for this context"}, {"get_username", get_username, METH_VARARGS, "Returns the username string for this context"}, {"set_username", set_username, METH_VARARGS, "Sets the username string for this context"}, {"get_timestamp", get_timestamp, METH_VARARGS, "Returns the timestamp value for this context"}, {"set_timestamp", set_timestamp, METH_VARARGS, "Sets the timestamp value with optional offset for this context"}, {"get_spa_digest_type", get_spa_digest_type, METH_VARARGS, "Returns the spa_digest_type value for this context"}, {"set_spa_digest_type", set_spa_digest_type, METH_VARARGS, "Sets the spa_digest_type value for this context"}, {"get_spa_encryption_type", get_spa_encryption_type, METH_VARARGS, "Returns the spa_encryption_type value for this context"}, {"set_spa_encryption_type", set_spa_encryption_type, METH_VARARGS, "Sets the spa_encryption_type value for this context"}, {"get_spa_message_type", get_spa_message_type, METH_VARARGS, "Returns the spa_message_type value for this context"}, {"set_spa_message_type", set_spa_message_type, METH_VARARGS, "Sets the spa_message_type data for this context"}, {"get_spa_message", get_spa_message, METH_VARARGS, "Returns the spa_message data for this context"}, {"set_spa_message", set_spa_message, METH_VARARGS, "Sets the spa_message data for this context"}, {"get_spa_nat_access", get_spa_nat_access, METH_VARARGS, "Returns the spa_nat_access string for this context"}, {"set_spa_nat_access", set_spa_nat_access, METH_VARARGS, "Sets the spa_nat_access string for this context"}, {"get_spa_server_auth", get_spa_server_auth, METH_VARARGS, "Returns the spa_server_auth string for this context"}, {"set_spa_server_auth", set_spa_server_auth, METH_VARARGS, "Sets the spa_server_auth string for this context"}, {"get_spa_client_timeout", get_spa_client_timeout, METH_VARARGS, "Returns the spa_client_timeout value for this context"}, {"set_spa_client_timeout", set_spa_client_timeout, METH_VARARGS, "Sets the spa_client_timeout value for this context"}, {"get_spa_digest", get_spa_digest, METH_VARARGS, "Returns the spa_digest data for this context"}, {"set_spa_digest", set_spa_digest, METH_VARARGS, "Sets the spa_digest data for this context"}, {"get_spa_data", get_spa_data, METH_VARARGS, "Returns the spa_data string for this context"}, {"set_spa_data", set_spa_data, METH_VARARGS, "Sets the spa_data string for this context"}, {"get_encoded_data", get_encoded_data, METH_VARARGS, "Returns the encoded_data string for this context"}, //--DSS {"get_raw_spa_digest_type", get_raw_spa_digest_type, METH_VARARGS, "Returns the raw spa_digest_type value for this context"}, {"set_raw_spa_digest_type", set_raw_spa_digest_type, METH_VARARGS, "Sets the raw_spa_digest_type string for this context"}, {"get_raw_spa_digest", get_raw_spa_digest, METH_VARARGS, "Returns the raw spa_digest value for this context"}, {"set_raw_spa_digest", set_raw_spa_digest, METH_VARARGS, "Sets the raw_spa_digest string for this context"}, {"get_spa_encryption_mode", get_spa_encryption_mode, METH_VARARGS, "Returns the raw spa_encryption_mode value for this context"}, {"set_spa_encryption_mode", set_spa_encryption_mode, METH_VARARGS, "Sets the set_spa_encryption_mode string for this context"}, {"get_spa_hmac_type", get_spa_hmac_type, METH_VARARGS, "Returns the raw spa_hmac_type value for this context"}, {"set_spa_hmac_type", set_spa_hmac_type, METH_VARARGS, "Sets the spa_hmac_type string for this context"}, {"spa_data_final", spa_data_final, METH_VARARGS, "Recalculate and recreate the SPA data for the current context"}, {"decrypt_spa_data", decrypt_spa_data, METH_VARARGS, "Decrypt and parse the current context SPA data"}, {"encrypt_spa_data", encrypt_spa_data, METH_VARARGS, "Encrypt the current context raw data into a SPA data message"}, {"decode_spa_data", decode_spa_data, METH_VARARGS, "Decode and parse the decrypted current context SPA data"}, {"encode_spa_data", encode_spa_data, METH_VARARGS, "Encode the current context raw data to prepare for encryption"}, //--DSS {"encryption_type", encryption_type, METH_VARARGS, "Return the assumed encryption type based on the encryptped data"}, {"key_gen", key_gen, METH_VARARGS, "Generate Rijndael and HMAC keys and base64 encode them"}, {"base64_encode", base64_encode, METH_VARARGS, "Base64 encode function"}, {"base64_decode", base64_decode, METH_VARARGS, "Base64 decode function"}, {"verify_hmac", verify_hmac, METH_VARARGS, "Generate HMAC for the data and verify it against the HMAC included with the data"}, {"set_spa_hmac", set_spa_hmac, METH_VARARGS, "Calculate the HMAC for the given data"}, {"get_spa_hmac", get_spa_hmac, METH_VARARGS, "Return the HMAC for the data in the current context"}, {"get_gpg_recipient", get_gpg_recipient, METH_VARARGS, "Returns the gpg_recipient string for this context"}, {"set_gpg_recipient", set_gpg_recipient, METH_VARARGS, "Sets the gpg_recipient string for this context"}, {"get_gpg_signer", get_gpg_signer, METH_VARARGS, "Returns the gpg_signer string for this context"}, {"set_gpg_signer", set_gpg_signer, METH_VARARGS, "Sets the gpg_signer string for this context"}, {"get_gpg_home_dir", get_gpg_home_dir, METH_VARARGS, "Returns the gpg_home_dir string for this context"}, {"set_gpg_home_dir", set_gpg_home_dir, METH_VARARGS, "Sets the gpg_home_dir string for this context"}, {"get_gpg_signature_verify", get_gpg_signature_verify, METH_VARARGS, "Returns the gpg_signature_verify flag value for this context"}, {"set_gpg_signature_verify", set_gpg_signature_verify, METH_VARARGS, "Sets the gpg_signature_verify flag value for this context"}, {"get_gpg_ignore_verify_error", get_gpg_ignore_verify_error, METH_VARARGS, "Returns the gpg_ignore_verify_error flag value for this context"}, {"set_gpg_ignore_verify_error", set_gpg_ignore_verify_error, METH_VARARGS, "Sets the gpg_ignore_verify_error flag value for this context"}, {"get_gpg_exe", get_gpg_exe, METH_VARARGS, "Returns the path to the gpg executable used by this context"}, {"set_gpg_exe", set_gpg_exe, METH_VARARGS, "Sets the path to the gpg executable to use for this context"}, {"get_gpg_signature_id", get_gpg_signature_id, METH_VARARGS, "Returns the gpg_signature_id string for recently decoded GPG-encoded message"}, {"get_gpg_signature_fpr", get_gpg_signature_fpr, METH_VARARGS, "Returns the gpg_signature_fpr (fingerprint) string for recently decoded GPG-encoded message"}, {"get_gpg_signature_summary", get_gpg_signature_summary, METH_VARARGS, "Returns the gpg_signature_summary value for recently decoded GPG-encoded message"}, {"get_gpg_signature_status", get_gpg_signature_status, METH_VARARGS, "Returns the gpg_signature_status value for recently decoded GPG-encoded message"}, {"gpg_signature_id_match", gpg_signature_id_match, METH_VARARGS, "Returns a true value if the GPG signature of GPG-encoded message matches the given id string"}, {"gpg_signature_fpr_match", gpg_signature_fpr_match, METH_VARARGS, "Returns a true value if the GPG fingerprint of GPG-encoded message matches the given fingerprint string"}, {"errstr", errstr, METH_VARARGS, "Returns the error message for the given error code"}, {"gpg_errstr", gpg_errstr, METH_VARARGS, "Returns the GPG error message for the given error code"}, {NULL, NULL, 0, NULL} }; /***************************************************************************** * Module init */ PyMODINIT_FUNC init_fko(void) { PyObject *m; m = Py_InitModule("_fko", FKOMethods); if (m == NULL) return; FKOError = PyErr_NewException("fko.error", NULL, NULL); Py_INCREF(FKOError); PyModule_AddObject(m, "error", FKOError); } /***************************************************************************** * FKO context functions. */ /* init_ctx */ static PyObject * init_ctx(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res; res = fko_new(&ctx); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("k", ctx); } /* init_ctx_with_data */ static PyObject * init_ctx_with_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_data; char *dec_key; int dec_key_len; int enc_mode; char *hmac_key; int hmac_key_len; int hmac_type; int res; if(!PyArg_ParseTuple(args, "ss#is#", &spa_data, &dec_key, &dec_key_len, &enc_mode, &hmac_key, &hmac_key_len, &hmac_type)) return NULL; res = fko_new_with_data(&ctx, spa_data, dec_key, dec_key_len, enc_mode, hmac_key, hmac_key_len, hmac_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("k", ctx); } /* 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); } /***************************************************************************** * FKO SPA data functions. */ /* get_version */ static PyObject * get_version(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *ver_str; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_version(ctx, &ver_str); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", ver_str); } /* get_rand_value */ static PyObject * get_rand_value(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *rand_value; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_rand_value(ctx, &rand_value); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", rand_value); } /* set_rand_value */ static PyObject * set_rand_value(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *rand_value; int res; if(!PyArg_ParseTuple(args, "kz", &ctx, &rand_value)) return NULL; res = fko_set_rand_value(ctx, rand_value); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_username */ static PyObject * get_username(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *username; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_username(ctx, &username); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", username); } /* set_username */ static PyObject * set_username(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *username; int res; if(!PyArg_ParseTuple(args, "kz", &ctx, &username)) return NULL; res = fko_set_username(ctx, username); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_timestamp */ static PyObject * get_timestamp(PyObject *self, PyObject *args) { fko_ctx_t ctx; time_t timestamp; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_timestamp(ctx, ×tamp); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("k", timestamp); } /* set_timestamp */ static PyObject * set_timestamp(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res, offset; if(!PyArg_ParseTuple(args, "kk", &ctx, &offset)) return NULL; res = fko_set_timestamp(ctx, offset); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_digest_type */ static PyObject * get_spa_digest_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short digest_type; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_digest_type(ctx, &digest_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", digest_type); } /* set_spa_digest_type */ static PyObject * set_spa_digest_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short digest_type; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &digest_type)) return NULL; res = fko_set_spa_digest_type(ctx, digest_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_encryption_type */ static PyObject * get_spa_encryption_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short encryption_type; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_encryption_type(ctx, &encryption_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", encryption_type); } /* set_spa_encryption_type */ static PyObject * set_spa_encryption_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short encryption_type; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &encryption_type)) return NULL; res = fko_set_spa_encryption_type(ctx, encryption_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_message_type */ static PyObject * get_spa_message_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short message_type; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_message_type(ctx, &message_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", message_type); } /* set_spa_message_type */ static PyObject * set_spa_message_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short message_type; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &message_type)) return NULL; res = fko_set_spa_message_type(ctx, message_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_message */ static PyObject * get_spa_message(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_message; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_message(ctx, &spa_message); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", spa_message); } /* set_spa_message */ static PyObject * set_spa_message(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_message; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &spa_message)) return NULL; res = fko_set_spa_message(ctx, spa_message); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_nat_access */ static PyObject * get_spa_nat_access(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_nat_access; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_nat_access(ctx, &spa_nat_access); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", spa_nat_access); } /* set_spa_nat_access */ static PyObject * set_spa_nat_access(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_nat_access; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &spa_nat_access)) return NULL; res = fko_set_spa_nat_access(ctx, spa_nat_access); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_server_auth */ static PyObject * get_spa_server_auth(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_server_auth; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_server_auth(ctx, &spa_server_auth); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", spa_server_auth); } /* set_spa_server_auth */ static PyObject * set_spa_server_auth(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_server_auth; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &spa_server_auth)) return NULL; res = fko_set_spa_server_auth(ctx, spa_server_auth); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_client_timeout */ static PyObject * get_spa_client_timeout(PyObject *self, PyObject *args) { fko_ctx_t ctx; int client_timeout; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_client_timeout(ctx, &client_timeout); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("i", client_timeout); } /* set_spa_client_timeout */ static PyObject * set_spa_client_timeout(PyObject *self, PyObject *args) { fko_ctx_t ctx; int client_timeout; int res; if(!PyArg_ParseTuple(args, "ki", &ctx, &client_timeout)) return NULL; res = fko_set_spa_client_timeout(ctx, client_timeout); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_digest */ static PyObject * get_spa_digest(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_digest; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_digest(ctx, &spa_digest); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", spa_digest); } /* set_spa_digest */ static PyObject * set_spa_digest(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_set_spa_digest(ctx); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_spa_data */ static PyObject * get_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_data; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_data(ctx, &spa_data); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", spa_data); } /* set_spa_data */ static PyObject * set_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *spa_data; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &spa_data)) return NULL; res = fko_set_spa_data(ctx, spa_data); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_encoded_data */ static PyObject * get_encoded_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *encoded_data; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_encoded_data(ctx, &encoded_data); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", encoded_data); } static PyObject * get_raw_spa_digest_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short raw_digest_type; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_raw_spa_digest_type(ctx, &raw_digest_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", raw_digest_type); } static PyObject * set_raw_spa_digest_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short raw_digest_type; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &raw_digest_type)) return NULL; res = fko_set_raw_spa_digest_type(ctx, raw_digest_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * get_raw_spa_digest(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *raw_spa_digest; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_raw_spa_digest(ctx, &raw_spa_digest); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", raw_spa_digest); } static PyObject * set_raw_spa_digest(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_set_raw_spa_digest(ctx); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * get_spa_encryption_mode(PyObject *self, PyObject *args) { fko_ctx_t ctx; int encryption_mode; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_encryption_mode(ctx, &encryption_mode); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", encryption_mode); } static PyObject * set_spa_encryption_mode(PyObject *self, PyObject *args) { fko_ctx_t ctx; int encryption_mode; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &encryption_mode)) return NULL; res = fko_set_spa_encryption_mode(ctx, encryption_mode); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * get_spa_hmac_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short hmac_type; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_hmac_type(ctx, &hmac_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("h", hmac_type); } static PyObject * set_spa_hmac_type(PyObject *self, PyObject *args) { fko_ctx_t ctx; short hmac_type; int res; if(!PyArg_ParseTuple(args, "kh", &ctx, &hmac_type)) return NULL; res = fko_set_spa_hmac_type(ctx, hmac_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /***************************************************************************** * FKO other utility functions. */ /* spa_data_final */ static PyObject * spa_data_final(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *enc_key; int enc_key_len; char *hmac_key; int hmac_key_len; int res; if(!PyArg_ParseTuple(args, "ks#s#", &ctx, &enc_key, &enc_key_len, &hmac_key, &hmac_key_len)) return NULL; res = fko_spa_data_final(ctx, enc_key, enc_key_len, hmac_key, hmac_key_len); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* decrypt_spa_data */ static PyObject * decrypt_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *key; int key_len; int res; if(!PyArg_ParseTuple(args, "ks#", &ctx, &key, &key_len)) return NULL; res = fko_decrypt_spa_data(ctx, key, key_len); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* encrypt_spa_data */ static PyObject * encrypt_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *key; int key_len; int res; if(!PyArg_ParseTuple(args, "ks#", &ctx, &key, &key_len)) return NULL; res = fko_encrypt_spa_data(ctx, key, key_len); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* decode_spa_data */ static PyObject * decode_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_decode_spa_data(ctx); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* encode_spa_data */ static PyObject * encode_spa_data(PyObject *self, PyObject *args) { fko_ctx_t ctx; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_encode_spa_data(ctx); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * encryption_type(PyObject *self, PyObject *args) { char *spa_data; int enc_type; if(!PyArg_ParseTuple(args, "s", &spa_data)) return NULL; enc_type = fko_encryption_type(spa_data); return Py_BuildValue("i", enc_type); } static PyObject * key_gen(PyObject *self, PyObject *args) { char *key_b64; int key_b64_len; char *hmac_key_b64; int hmac_key_b64_len; int hmac_type; int res; if(!PyArg_ParseTuple(args, "s#s#ih", &key_b64, &key_b64_len, &hmac_key_b64, &hmac_key_b64_len, &hmac_type)) return NULL; res = fko_key_gen(key_b64, key_b64_len, hmac_key_b64, hmac_key_b64_len, hmac_type); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * base64_encode(PyObject *self, PyObject *args) { unsigned char *in; int in_len; char *out; /* --DSS Note the order of args is different than the libfko call. We need to do this for the following parse call. */ if(!PyArg_ParseTuple(args, "s#s", &in, &in_len, &out)) return NULL; fko_base64_encode(in, out, in_len); return Py_BuildValue("s", out); } static PyObject * base64_decode(PyObject *self, PyObject *args) { char *in; unsigned char *out; int res; if(!PyArg_ParseTuple(args, "ss", &in, &out)) return NULL; res = fko_base64_decode(in, out); return Py_BuildValue("s#", out, res); } static PyObject * verify_hmac(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *hmac_key; int hmac_key_len; int res; if(!PyArg_ParseTuple(args, "ks#", &ctx, &hmac_key, &hmac_key_len)) return NULL; res = fko_verify_hmac(ctx, hmac_key, hmac_key_len); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * set_spa_hmac(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *hmac_key; int hmac_key_len; int res; if(!PyArg_ParseTuple(args, "ks#", &ctx, &hmac_key, &hmac_key_len)) return NULL; res = fko_set_spa_hmac(ctx, hmac_key, hmac_key_len); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } static PyObject * get_spa_hmac(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *hmac_data; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_spa_hmac(ctx, &hmac_data); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", hmac_data); } /***************************************************************************** * FKO GPG-related functions. */ /* get_gpg_recipient */ static PyObject * get_gpg_recipient(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_recipient; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_recipient(ctx, &gpg_recipient); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_recipient); } /* set_gpg_recipient */ static PyObject * set_gpg_recipient(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_recipient; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_recipient)) return NULL; res = fko_set_gpg_recipient(ctx, gpg_recipient); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_signer */ static PyObject * get_gpg_signer(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_signer; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signer(ctx, &gpg_signer); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_signer); } /* set_gpg_signer */ static PyObject * set_gpg_signer(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_signer; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_signer)) return NULL; res = fko_set_gpg_signer(ctx, gpg_signer); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_home_dir */ static PyObject * get_gpg_home_dir(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_home_dir; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_home_dir(ctx, &gpg_home_dir); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_home_dir); } /* set_gpg_home_dir */ static PyObject * set_gpg_home_dir(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_home_dir; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_home_dir)) return NULL; res = fko_set_gpg_home_dir(ctx, gpg_home_dir); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_signature_verify */ static PyObject * get_gpg_signature_verify(PyObject *self, PyObject *args) { fko_ctx_t ctx; unsigned char gpg_signature_verify; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signature_verify(ctx, &gpg_signature_verify); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("b", gpg_signature_verify); } /* set_gpg_signature_verify */ static PyObject * set_gpg_signature_verify(PyObject *self, PyObject *args) { fko_ctx_t ctx; unsigned char gpg_signature_verify; int res; if(!PyArg_ParseTuple(args, "kb", &ctx, &gpg_signature_verify)) return NULL; res = fko_set_gpg_signature_verify(ctx, gpg_signature_verify); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_ignore_verify_error */ static PyObject * get_gpg_ignore_verify_error(PyObject *self, PyObject *args) { fko_ctx_t ctx; unsigned char gpg_ignore_verify_error; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_ignore_verify_error(ctx, &gpg_ignore_verify_error); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("b", gpg_ignore_verify_error); } /* set_gpg_ignore_verify_error */ static PyObject * set_gpg_ignore_verify_error(PyObject *self, PyObject *args) { fko_ctx_t ctx; unsigned char gpg_ignore_verify_error; int res; if(!PyArg_ParseTuple(args, "kb", &ctx, &gpg_ignore_verify_error)) return NULL; res = fko_set_gpg_ignore_verify_error(ctx, gpg_ignore_verify_error); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_exe */ static PyObject * get_gpg_exe(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_exe; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_exe(ctx, &gpg_exe); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_exe); } /* set_gpg_exe */ static PyObject * set_gpg_exe(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_exe; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_exe)) return NULL; res = fko_set_gpg_exe(ctx, gpg_exe); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("", NULL); } /* get_gpg_signature_id */ static PyObject * get_gpg_signature_id(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_signature_id; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signature_id(ctx, &gpg_signature_id); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_signature_id); } /* get_gpg_signature_fpr */ static PyObject * get_gpg_signature_fpr(PyObject *self, PyObject *args) { fko_ctx_t ctx; char *gpg_signature_fpr; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signature_fpr(ctx, &gpg_signature_fpr); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("s", gpg_signature_fpr); } /* get_gpg_signature_summary */ static PyObject * get_gpg_signature_summary(PyObject *self, PyObject *args) { fko_ctx_t ctx; int gpg_signature_summary; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signature_summary(ctx, &gpg_signature_summary); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("i", gpg_signature_summary); } /* get_gpg_signature_status */ static PyObject * get_gpg_signature_status(PyObject *self, PyObject *args) { fko_ctx_t ctx; int gpg_signature_status; int res; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; res = fko_get_gpg_signature_status(ctx, &gpg_signature_status); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("i", gpg_signature_status); } /* gpg_signature_id_match */ static PyObject * gpg_signature_id_match(PyObject *self, PyObject *args) { fko_ctx_t ctx; const char *gpg_signature_id; unsigned char gpg_signature_id_match; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_signature_id)) return NULL; res = fko_gpg_signature_id_match(ctx, gpg_signature_id, &gpg_signature_id_match); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("b", gpg_signature_id_match); } /* gpg_signature_fpr_match */ static PyObject * gpg_signature_fpr_match(PyObject *self, PyObject *args) { fko_ctx_t ctx; const char *gpg_signature_fpr; unsigned char gpg_signature_fpr_match; int res; if(!PyArg_ParseTuple(args, "ks", &ctx, &gpg_signature_fpr)) return NULL; res = fko_gpg_signature_id_match(ctx, gpg_signature_fpr, &gpg_signature_fpr_match); if(res != FKO_SUCCESS) { PyErr_SetString(FKOError, fko_errstr(res)); return NULL; } return Py_BuildValue("b", gpg_signature_fpr_match); } /***************************************************************************** * FKO error message function. */ /* errstr */ static PyObject * errstr(PyObject *self, PyObject *args) { const char *errmsg; int res; if(!PyArg_ParseTuple(args, "i", &res)) return NULL; errmsg = fko_errstr(res); return Py_BuildValue("s", errmsg); } /* gpg_errstr */ static PyObject * gpg_errstr(PyObject *self, PyObject *args) { fko_ctx_t ctx; const char *errmsg; if(!PyArg_ParseTuple(args, "k", &ctx)) return NULL; errmsg = fko_gpg_errstr(ctx); return Py_BuildValue("s", errmsg); } /***EOF***/ fwknop-2.6.10/python/setup.py0000664000175000017500000000371513332165274013076 00000000000000#!/usr/bin/python ############################################################################## # # File: setup.py # # Purpose: Driver script for the fko module. # # Fwknop is developed primarily by the people listed in the file 'AUTHORS'. # Copyright (C) 2009-2014 fwknop developers and contributors. For a full # list of contributors, see the file 'CREDITS'. # # License (GNU General Public License): # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ############################################################################## # from distutils.core import setup, Extension # The fko extension module. # fko_ext = Extension( '_fko', define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '5')], include_dirs = ['../lib/'], library_dirs = ['../lib/.libs'], libraries = ['fko'], sources = ['fkomodule.c'] ) setup ( name = 'fko', version = '1.5', description = 'Wrapper for Fwknop library (libfko)', author = 'Damien S. Stuart', author_email = 'dstuart@dstuart.org', license = 'GPL2+', long_description = ''' Python module that wraps the fwknop library to provide the ability to generate, decode, parse, and process SPA formatted messages. ''', ext_modules = [fko_ext], py_modules = ['fko'] ) ###EOF### fwknop-2.6.10/python/README0000664000175000017500000000344513332165274012244 00000000000000fko python module version 1.0 ============================== This module is essentially a Python wrapper for the Firewall Knock Operator library, "libfko". See the "libfko" documentation for additional information on the functionality provided by "libfko" and usage overview. Before attempting to build this module, libfko needs to be installed on the system. To build and install the module: * Build with "python setup.py build" * Install with "python setup.py install" Simple usage example: #!/usr/bin/python # # Import the Fko class and all constants. # from fko import * # Create an Fko instance with an empty context. # fko = Fko() # Set the HMAC digest algorithm # fko.hmac_type(FKO_HMAC_SHA256) # Set the SPA message (Note: Access request is default if not specified). # fko.spa_message("0.0.0.0,tcp/22") # Create the final SPA data message string. # fko.spa_data_final("mypassword", "myhmackey") # print the spa message. # print fko.spa_data() (prints something like this): /m4fc/3OGd1IOchWM8o/yUvoa8TdbsRgU0zrp4UWuese9DIcHl09pZ3ukrNy/2OZAH3gnRH186YVMQEB1qfx23xVMq3IXx/fBwxgLBIqNdii2yWGqUVlxw83tfiP/p3Fmr7AeM1mev62xpE8R5vdACNxIDMM51lmYKh6JtEMoHrXXFAIRqbwCXJvlTEQij4vlZ4KfhMpnxxiiUciDWB11mbVDGSsQqKU9MQ # To decode SPA data: # fko = Fko("/m4fc/3OGd1IOchWM8o/yUvoa8TdbsRgU0zrp4UWuese9DIcHl09pZ3ukrNy/2OZAH3gnRH186YVMQEB1qfx23xVMq3IXx/fBwxgLBIqNdii2yWGqUVlxw83tfiP/p3Fmr7AeM1mev62xpE8R5vdACNxIDMM51lmYKh6JtEMoHrXXFAIRqbwCXJvlTEQij4vlZ4KfhMpnxxiiUciDWB11mbVDGSsQqKU9MQ", "mypassword", "myhmackey") # Print some of the data: # print "Version:", fko.version() print "Timestamp:", fko.timestamp() print "Username:", fko.username() print "Digest Type (value):", fko.digest_type() print "Digest Type (string):", fko.digest_type_str() print "Digest:", fko.spa_digest() print "SPA Message:", fko.spa_message() fwknop-2.6.10/python/fko.py0000664000175000017500000012124513332165274012514 00000000000000############################################################################## # # fko.py # # License (GNU General Public License): # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ############################################################################## """Wrapper functions for libfko. The fko module provides a class that implements the functions for managing fwknop Single Packet Authorization (SPA) via the fwknop library (libfko). You can find more detailed information in the libfko documention (try "info libfko" if you have the standard GNU texinfo tools). Example simple minimal fknop client: import socket from fko import * fko_port = 62201 fko_host = "192.168.7.67" # Create the Fko object which will initialize the FKO # context and populate some of its fields with default # data. # f = Fko() # Set the SPA message (access request) # f.spa_message('192.168.7.5,tcp/22') # Alternate way to set SPA message using the FkoAccess class. # # ar = FkoAccess("192.168.7.5", "tcp", 22) # f.spa_message(ar.str()) # Generate the final SPA data string. # f.spa_data_final('put_pw_here', 'put_hmac_pw_here') # Display the final SPA data string. # print "SPA Data:", f.spa_data() # Send the SPA request. # s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto(f.spa_data(), (fko_host, fko_port)) s.close() """ import _fko from string import join # FKO Constants definitions """Message type constants """ FKO_COMMAND_MSG = 0 FKO_ACCESS_MSG = 1 FKO_NAT_ACCESS_MSG = 2 FKO_CLIENT_TIMEOUT_ACCESS_MSG = 3 FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG = 4 FKO_LOCAL_NAT_ACCESS_MSG = 5 FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG = 6 """Digest type constants """ FKO_DIGEST_INVALID_DATA = -1 FKO_DIGEST_UNKNOWN = 0 FKO_DIGEST_MD5 = 1 FKO_DIGEST_SHA1 = 2 FKO_DIGEST_SHA256 = 3 FKO_DIGEST_SHA384 = 4 FKO_DIGEST_SHA512 = 5 FKO_DIGEST_SHA3_256 = 6 FKO_DIGEST_SHA3_512 = 7 """Hmac type constants """ FKO_HMAC_INVALID_DATA = -1 FKO_HMAC_UNKNOWN = 0 FKO_HMAC_MD5 = 1 FKO_HMAC_SHA1 = 2 FKO_HMAC_SHA256 = 3 FKO_HMAC_SHA384 = 4 FKO_HMAC_SHA512 = 5 FKO_HMAC_SHA3_256 = 6 FKO_HMAC_SHA3_512 = 7 """Encryption type constants """ FKO_ENCRYPTION_INVALID_DATA = -1 FKO_ENCRYPTION_UNKNOWN = 0 FKO_ENCRYPTION_RIJNDAEL = 1 FKO_ENCRYPTION_GPG = 2 """Symmetric encryption modes to correspond to rijndael.h """ FKO_ENC_MODE_UNKNOWN = 0 FKO_ENC_MODE_ECB = 1 FKO_ENC_MODE_CBC = 2 FKO_ENC_MODE_CFB = 3 FKO_ENC_MODE_PCBC = 4 FKO_ENC_MODE_OFB = 5 FKO_ENC_MODE_CTR = 6 FKO_ENC_MODE_ASYMMETRIC = 7 FKO_ENC_MODE_CBC_LEGACY_IV = 8 """FKO error codes """ FKO_SUCCESS = 0 FKO_ERROR_CTX_NOT_INITIALIZED = 1 FKO_ERROR_MEMORY_ALLOCATION = 2 FKO_ERROR_FILESYSTEM_OPERATION = 3 FKO_ERROR_INVALID_DATA = 4 FKO_ERROR_INVALID_DATA_CLIENT_TIMEOUT_NEGATIVE = 5 FKO_ERROR_INVALID_DATA_DECODE_MSGLEN_VALIDFAIL = 6 FKO_ERROR_INVALID_DATA_DECODE_NON_ASCII = 7 FKO_ERROR_INVALID_DATA_DECODE_LT_MIN_FIELDS = 8 FKO_ERROR_INVALID_DATA_DECODE_GT_MAX_FIELDS = 9 FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS = 10 FKO_ERROR_INVALID_DATA_DECODE_ENC_MSG_LEN_MT_T_SIZE = 11 FKO_ERROR_INVALID_DATA_DECODE_RAND_MISSING = 12 FKO_ERROR_INVALID_DATA_DECODE_USERNAME_MISSING = 13 FKO_ERROR_INVALID_DATA_DECODE_USERNAME_TOOBIG = 14 FKO_ERROR_INVALID_DATA_DECODE_USERNAME_DECODEFAIL = 15 FKO_ERROR_INVALID_DATA_DECODE_USERNAME_VALIDFAIL = 16 FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING = 17 FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG = 18 FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL = 19 FKO_ERROR_INVALID_DATA_DECODE_VERSION_MISSING = 20 FKO_ERROR_INVALID_DATA_DECODE_VERSION_TOOBIG = 21 FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING = 22 FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG = 23 FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL = 24 FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_MISSING = 25 FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_TOOBIG = 26 FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_DECODEFAIL = 27 FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_VALIDFAIL = 28 FKO_ERROR_INVALID_DATA_DECODE_ACCESS_VALIDFAIL = 29 FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_MISSING = 30 FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_TOOBIG = 31 FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_DECODEFAIL = 32 FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_VALIDFAIL = 33 FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_MISSING = 34 FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_DECODEFAIL = 35 FKO_ERROR_INVALID_DATA_DECODE_SPA_EXTRA_TOOBIG = 36 FKO_ERROR_INVALID_DATA_DECODE_EXTRA_TOOBIG = 37 FKO_ERROR_INVALID_DATA_DECODE_EXTRA_DECODEFAIL = 38 FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING = 39 FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG = 40 FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL = 41 FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL = 42 FKO_ERROR_INVALID_DATA_ENCODE_MESSAGE_TOOBIG = 43 FKO_ERROR_INVALID_DATA_ENCODE_MSGLEN_VALIDFAIL = 44 FKO_ERROR_INVALID_DATA_ENCODE_DIGEST_VALIDFAIL = 45 FKO_ERROR_INVALID_DATA_ENCODE_DIGEST_TOOBIG = 46 FKO_ERROR_INVALID_DATA_ENCODE_NOTBASE64 = 47 FKO_ERROR_INVALID_DATA_ENCRYPT_MSGLEN_VALIDFAIL = 48 FKO_ERROR_INVALID_DATA_ENCRYPT_DIGESTLEN_VALIDFAIL = 49 FKO_ERROR_INVALID_DATA_ENCRYPT_PTLEN_VALIDFAIL = 50 FKO_ERROR_INVALID_DATA_ENCRYPT_RESULT_MSGLEN_VALIDFAIL = 51 FKO_ERROR_INVALID_DATA_ENCRYPT_CIPHERLEN_DECODEFAIL = 52 FKO_ERROR_INVALID_DATA_ENCRYPT_CIPHERLEN_VALIDFAIL = 53 FKO_ERROR_INVALID_DATA_ENCRYPT_DECRYPTED_MESSAGE_MISSING = 54 FKO_ERROR_INVALID_DATA_ENCRYPT_DECRYPTED_MSGLEN_VALIDFAIL = 55 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_MESSAGE_VALIDFAIL = 56 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_DIGEST_VALIDFAIL = 57 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_MSGLEN_VALIDFAIL = 58 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_RESULT_MSGLEN_VALIDFAIL = 59 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_CIPHER_DECODEFAIL = 60 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_ENCODEDMSG_NULL = 61 FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_ENCODEDMSGLEN_VALIDFAIL = 62 FKO_ERROR_INVALID_DATA_ENCRYPT_TYPE_VALIDFAIL = 63 FKO_ERROR_INVALID_DATA_ENCRYPT_MODE_VALIDFAIL = 64 FKO_ERROR_INVALID_DATA_ENCRYPT_TYPE_UNKNOWN = 65 FKO_ERROR_INVALID_DATA_FUNCS_NEW_ENCMSG_MISSING = 66 FKO_ERROR_INVALID_DATA_FUNCS_NEW_MSGLEN_VALIDFAIL = 67 FKO_ERROR_INVALID_DATA_FUNCS_GEN_KEYLEN_VALIDFAIL = 68 FKO_ERROR_INVALID_DATA_FUNCS_GEN_HMACLEN_VALIDFAIL = 69 FKO_ERROR_INVALID_DATA_FUNCS_GEN_KEY_ENCODEFAIL = 70 FKO_ERROR_INVALID_DATA_FUNCS_GEN_HMAC_ENCODEFAIL = 71 FKO_ERROR_INVALID_DATA_FUNCS_SET_MSGLEN_VALIDFAIL = 72 FKO_ERROR_INVALID_DATA_HMAC_MSGLEN_VALIDFAIL = 73 FKO_ERROR_INVALID_DATA_HMAC_ENCMSGLEN_VALIDFAIL = 74 FKO_ERROR_INVALID_DATA_HMAC_COMPAREFAIL = 75 FKO_ERROR_INVALID_DATA_HMAC_TYPE_VALIDFAIL = 76 FKO_ERROR_INVALID_DATA_HMAC_LEN_VALIDFAIL = 77 FKO_ERROR_INVALID_DATA_MESSAGE_PORT_MISSING = 78 FKO_ERROR_INVALID_DATA_MESSAGE_TYPE_VALIDFAIL = 79 FKO_ERROR_INVALID_DATA_MESSAGE_EMPTY = 80 FKO_ERROR_INVALID_DATA_MESSAGE_CMD_MISSING = 81 FKO_ERROR_INVALID_DATA_MESSAGE_ACCESS_MISSING = 82 FKO_ERROR_INVALID_DATA_MESSAGE_NAT_MISSING = 83 FKO_ERROR_INVALID_DATA_MESSAGE_PORTPROTO_MISSING = 84 FKO_ERROR_INVALID_DATA_NAT_EMPTY = 85 FKO_ERROR_INVALID_DATA_RAND_LEN_VALIDFAIL = 86 FKO_ERROR_INVALID_DATA_SRVAUTH_MISSING = 87 FKO_ERROR_INVALID_DATA_TIMESTAMP_VALIDFAIL = 88 FKO_ERROR_INVALID_DATA_USER_MISSING = 89 FKO_ERROR_INVALID_DATA_USER_FIRSTCHAR_VALIDFAIL = 90 FKO_ERROR_INVALID_DATA_USER_REMCHAR_VALIDFAIL = 91 FKO_ERROR_INVALID_DATA_UTIL_STRTOL_LT_MIN = 92 FKO_ERROR_INVALID_DATA_UTIL_STRTOL_GT_MAX = 93 FKO_ERROR_DATA_TOO_LARGE = 94 FKO_ERROR_INVALID_KEY_LEN = 95 FKO_ERROR_USERNAME_UNKNOWN = 96 FKO_ERROR_INCOMPLETE_SPA_DATA = 97 FKO_ERROR_MISSING_ENCODED_DATA = 98 FKO_ERROR_INVALID_DIGEST_TYPE = 99 FKO_ERROR_INVALID_ALLOW_IP = 100 FKO_ERROR_INVALID_SPA_COMMAND_MSG = 101 FKO_ERROR_INVALID_SPA_ACCESS_MSG = 102 FKO_ERROR_INVALID_SPA_NAT_ACCESS_MSG = 103 FKO_ERROR_INVALID_ENCRYPTION_TYPE = 104 FKO_ERROR_WRONG_ENCRYPTION_TYPE = 105 FKO_ERROR_DECRYPTION_SIZE = 106 FKO_ERROR_DECRYPTION_FAILURE = 107 FKO_ERROR_DIGEST_VERIFICATION_FAILED = 108 FKO_ERROR_INVALID_HMAC_KEY_LEN = 109 FKO_ERROR_UNSUPPORTED_HMAC_MODE = 110 FKO_ERROR_UNSUPPORTED_FEATURE = 111 FKO_ERROR_ZERO_OUT_DATA = 112 FKO_ERROR_UNKNOWN = 113 # Start GPGME-related errors GPGME_ERR_START = 114 FKO_ERROR_MISSING_GPG_KEY_DATA = 115 FKO_ERROR_GPGME_NO_OPENPGP = 116 FKO_ERROR_GPGME_CONTEXT = 117 FKO_ERROR_GPGME_PLAINTEXT_DATA_OBJ = 118 FKO_ERROR_GPGME_SET_PROTOCOL = 119 FKO_ERROR_GPGME_CIPHER_DATA_OBJ = 120 FKO_ERROR_GPGME_BAD_PASSPHRASE = 121 FKO_ERROR_GPGME_ENCRYPT_SIGN = 122 FKO_ERROR_GPGME_CONTEXT_SIGNER_KEY = 123 FKO_ERROR_GPGME_SIGNER_KEYLIST_START = 124 FKO_ERROR_GPGME_SIGNER_KEY_NOT_FOUND = 125 FKO_ERROR_GPGME_SIGNER_KEY_AMBIGUOUS = 126 FKO_ERROR_GPGME_ADD_SIGNER = 127 FKO_ERROR_GPGME_CONTEXT_RECIPIENT_KEY = 128 FKO_ERROR_GPGME_RECIPIENT_KEYLIST_START = 129 FKO_ERROR_GPGME_RECIPIENT_KEY_NOT_FOUND = 130 FKO_ERROR_GPGME_RECIPIENT_KEY_AMBIGUOUS = 131 FKO_ERROR_GPGME_DECRYPT_FAILED = 132 FKO_ERROR_GPGME_DECRYPT_UNSUPPORTED_ALGORITHM = 133 FKO_ERROR_GPGME_BAD_GPG_EXE = 134 FKO_ERROR_GPGME_BAD_HOME_DIR = 135 FKO_ERROR_GPGME_SET_HOME_DIR = 136 FKO_ERROR_GPGME_NO_SIGNATURE = 137 FKO_ERROR_GPGME_BAD_SIGNATURE = 138 FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED = 139 ### End FKO Constants ### class FkoException(Exception): """General exception class for fko. """ pass class Fko: """This class wraps the Firewall KNock OPerator (fwknop) library, libfko. It provides the functionality to manage and process Single Packet Authorization (SPA) data. """ def __init__(self, spa_data=None, key=None): """Constructor for the Fko class. Creates and intitializes the fko context. If no arguments are given, and empty context is create with some default values. See the libfko documentation for details on these defaults. If spa_data and key is supplied, the context is created, then the SPA data is decrypted using the key. If successful, the SPA data is parsed into the context's data structure. If spa_data is supplied without the key, then the encrypted data is stored in the context and can be decoded later (see libfko docs). """ # If there is SPA data, attempt to process it. Otherwise, create # an empty context. # if spa_data != None: self.ctx = _fko.init_ctx_with_data(spa_data, key) else: self.ctx = _fko.init_ctx() def __del__(self): """Destructor for Fko. Destroys the FKO context to clear the (possible sensitive) data and releases the resource allocated to the context. """ _fko.destroy_ctx(self.ctx) ### FKO data functions and operations. ### def version(self): """Returns the fwknop version string. This version represents the supported fwknop SPA message format and features. This has nothing to do with the version of this module. """ return _fko.get_version(self.ctx) def rand_value(self, val=None): """Get or set the random value string of the SPA data. If setting the random value string, you must pass either a 16-character decimal number (to set it to the given string), or an empty string ("")to have a new random value string generated by libfko. If a provided value is not a valid 16-character decimal string, the function will throw an fko.error exception. """ if val != None: _fko.set_rand_value(self.ctx, val) else: return _fko.get_rand_value(self.ctx) def username(self, val=None): """Set or get the username field of the SPA data. If no argument is given, given, this function will return the current value. Otherwise, the username value will be set to the name provided. If an empty string is given, libfko will attempt to determine and set the username by first looking for the environment variable "SPOOF_USER" and use its value if found. Otherwise, it will try to determine the username itself using various system methods, then fallback to the environment variables "LOGNAME" or "USER". If none of those work, the function will throw an fko.error exception. Upon creation of a new Fko object, this value is automatically generated based on the libfko method described above. """ if val != None: _fko.set_username(self.ctx, val) else: return _fko.get_username(self.ctx) def timestamp(self, val=None): """Gets or sets the timestamp value of the SPA data. If no argument is given, the current value is returned. If an argument is provided, it will represent an offset to be applied to the current timestamp value at the time this function was called. Upon creation of a new FKO object, this value is automatically generated based on the time of object creation. """ if val != None: _fko.set_timestamp(self.ctx, val) else: return _fko.get_timestamp(self.ctx) def digest_type(self, val=None): """Gets or sets the digest type. If no argument is given, the current value is returned. Otherwise, digest type will be set to the given value. The digest type parameter is an integer value. Constants have been defined to represent these values. Currently, the supported digest types are: FKO_DIGEST_MD5 - The MD5 message digest. FKO_DIGEST_SHA1 - The SHA1 message digest. FKO_DIGEST_SHA256 - The SHA256 message digest (default). FKO_DIGEST_SHA384 - The SHA384 message digest. FKO_DIGEST_SHA512 - The SHA512 message digest. FKO_DIGEST_SHA3_256 - The SHA3_256 message digest. FKO_DIGEST_SHA3_512 - The SHA3_512 message digest. """ if val != None: _fko.set_spa_digest_type(self.ctx, val) else: return _fko.get_spa_digest_type(self.ctx) def encryption_type(self, val=None): """Get or set the encryption type. If no argument is given, the current value is returned. Otherwise, encryption type will be set to the given value. The encryption type parameter is an integer value. Constants have been defined to represent these values. Currently, the only supported encryption types are: FKO_ENCRYPTION_RIJNDAEL AES - the default libfko encryption algorithm. FKO_ENCRYPTION_GPG GnuPG encryption (if supported by the underlying libfko implementation). """ if val != None: _fko.set_spa_encryption_type(self.ctx, val) else: return _fko.get_spa_encryption_type(self.ctx) def message_type(self, val=None): """Get or set the SPA message type. If no argument is given, the current value is returned. Otherwise, message type will be set to the given value. The message type parameter is an integer value. Constants have been defined to represent this values. Currently, the supported digest types are: FKO_COMMAND_MSG A request to have the fwknop server execute the given command. The format for this type is: ":" For example: "192.168.1.2:uname -a" FKO_ACCESS_MSG A basic access request. This is the most common type in use. The format for this type is: ":/". For example: "192.168.1.2:tcp/22" FKO_NAT_ACCESS_MSG An access request that also provide information for the fwknop server to create a Network Address Translation (NAT to an internal address. The format for this string is: ",". For example: "10.10.1.2,9922" FKO_CLIENT_TIMEOUT_ACCESS_MSG This is an "FKO_ACCESS_REQUEST" with a timeout parameter for the fwknop server. The timeout value is provided via the "client_timeout" data field. FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG This is an "FKO_NAT_ACCESS_REQUEST" with a timeout parameter for the fwknop server. The timeout value is provided via the "client_timeout" data field. FKO_LOCAL_NAT_ACCESS_MSG This is similar to the "FKO_NAT_ACCESS" request except the NAT is to the local to the server (i.e. a service listening on 127.0.0.1). FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCES_MSG This is an "FKO_LOCAL_NAT_ACCESS_REQUEST" with a timeout parameter for the fwknop server. The timeout value is provided via the "client_timeout" data field. """ if val != None: _fko.set_spa_message_type(self.ctx, val) else: return _fko.get_spa_message_type(self.ctx) def spa_message(self, val=None): """Get or set the SPA message string. If no argument is given, the current value is returned. Otherwise, SPA message string will be set to the given value. This is the string that represents the data for the message type as described in the spa_message_type section above. """ if val != None: _fko.set_spa_message(self.ctx, val) else: return _fko.get_spa_message(self.ctx) def spa_nat_access(self, val=None): """Get or set the SPA nat access string. If no argument is given, the current value is returned. Otherwise, SPA nat access string will be set to the given value. """ if val != None: _fko.set_spa_nat_access(self.ctx, val) else: return _fko.get_spa_nat_access(self.ctx) def spa_server_auth(self, val=None): """Get or set the SPA server auth string. If no argument is given, the current value is returned. Otherwise, the SPA server auth string will be set to the given value. """ if val != None: _fko.set_spa_server_auth(self.ctx, val) else: return _fko.get_spa_server_auth(self.ctx) def spa_client_timeout(self, val=None): """Get or set the SPA message client timeout value. This is an integer value. If no argument is given, the current value is returned. Otherwise, the SPA message client timeout value will be set to the given value. """ if val != None: _fko.set_spa_client_timeout(self.ctx, val) else: return _fko.get_spa_client_timeout(self.ctx) def spa_digest(self): """Returns the digest associated with the current data (if available and set). This function is normally not called directly as it is called by other libfko functions during normal processing. """ return _fko.get_spa_digest(self.ctx) def gen_spa_digest(self): """Recalculate the SPA data digest based on the current context's data. This function is normally not called directly as it is called by other libfko functions during normal processing. """ _fko.set_spa_digest(self.ctx) def spa_data(self, val=None): """Get or set the SPA data string. If no argument is given, the current value is returned. This would be the final encrypted and encoded string of data that is suitable for sending to an fwkno server. If an argument is given, it is expected to be an existing encrypted and encoded SPA data string (perhaps data received by an fwknop server). The provided data is stored in the object (the current context). Note: When data is provided via this function, it is not automatically decoded. You would need to call the "decrypt_spa_data(key)" method to complete the decryption, decoding, and parsing process. """ if val != None: _fko.set_spa_data(self.ctx, val) else: return _fko.get_spa_data(self.ctx) def encoded_data(self): """Returns the encoded SPA data as it would be just before the encryption step. This is not generally useful unless you are debugging a data issue. """ return _fko.get_encoded_data(self.ctx) def raw_spa_digest_type(self, val=None): """Get or set the raw spa_digest_type This is an integer value. If no argument is given, the current value is returned. Otherwise, the SPA message client timeout value will be set to the given value. """ if val != None: _fko.set_raw_spa_digest_type(self.ctx, val) else: return _fko.get_raw_spa_digest_type(self.ctx) def raw_spa_digest(self, val=None): """Get or set the raw spa_digest_type """ if val != None: _fko.set_raw_spa_digest(self.ctx, val) else: return _fko.get_raw_spa_digest(self.ctx) def encryption_mode(self, val=None): """Get or set the spa_encryption mode This is an integer value. If no argument is given, the current value is returned. Otherwise, the SPA message client timeout value will be set to the given value. """ if val != None: _fko.set_spa_encryption_mode(self.ctx, val) else: return _fko.get_spa_encryption_mode(self.ctx) def spa_encryption_mode(self, val=None): """Alias for encryption_mode() to maintain backwards compatibility """ return self.encryption_mode(val) def hmac_type(self, val=None): """Get or set the spa_hmac_type This is an integer value. If no argument is given, the current value is returned. Otherwise, the SPA message client timeout value will be set to the given value. """ if val != None: _fko.set_spa_hmac_type(self.ctx, val) else: return _fko.get_spa_hmac_type(self.ctx) def spa_data_final(self, key, hmac_key): """Perform final processing and generation of the SPA message data. This function is the final step in creating a complete encrypted SPA data string suitable for transmission to an fwknop server. It does require all of the requisite SPA data fields be set. Otherwise, it will fail and throw an fko.error exception. We do set the default HMAC digest to SHA256 if an HMAC key was provided and the HMAC mode was not already set. """ if hmac_key and not _fko.get_spa_hmac_type(self.ctx): _fko.set_spa_hmac_type(self.ctx, FKO_HMAC_SHA256) _fko.spa_data_final(self.ctx, key, hmac_key) def gen_spa_data(self, key): """Alias for "spa_data_final()". """ _fko.spa_data_final(self.ctx, key) def encode_spa_data(self): """Encode the raw SPA data. Instructs libfko to perform the base64 encoding of those SPA data fields that need to be encoded, perform some data validation, compute and store the message digest hash for the SPA data. This function is normally not called directly as it is called by other libfko functions during normal processing (i.e during encypt and/or final functions. """ _fko.encode_spa_data(self.ctx) def decode_spa_data(self): """Decode decrypted SPA data. This method hands of the data to the libfko decoding routines which performs the decoding, parsing, and validation of the SPA data that was just decrypted. This function is normally not called directly as it is called by other libfko functions during normal processing. """ _fko.decode_spa_data(self.ctx) def encrypt_spa_data(self, key): """Encrypts the intermediate encoded SPA data stored in the context. The internal libfko encryption function will call the internal "encode_spa_data" if necessary. This function is normally not called directly as it is automatically called from the internal "fko_spa_data_final" function (which is wrapped by this module's "spa_data_final" function). """ _fko.encrypt_spa_data(self.ctx, key) def decrypt_spa_data(self, key): """Decrypt, decode, and parse SPA message data. When given the correct key (passsword), this methoe decrypts, decodes, and parses the encrypted SPA data contained in the current context. Once the data is decrypted, the libfko internal function will also call the libfko decode function to decode, parse, validate, and store the data fields in the context for later retrieval. Note: This function does not need to be called directly if encrypted SPA data and the key was passed to this module's constructor when the object was created, the constructor will decrypt and decode the data at that time. """ _fko.decrypt_spa_data(self.ctx, key) # --DSS def key_gen(self, keyb64, hmac_keyb64): """Generate Rijndael and HMAC keys and base64 encode them """ _fko.key_gen(keyb64, hmac_keyb64) def base64_encode(self, indata): """Base64 encode function """ _fko.base64_encode(indata) def base64_decode(self, indata): """Base64 decode function """ _fko.base64_decode(indata) def verify_hmac(self, hmac_key): """Generate HMAC for the data and verify it against the HMAC included with the data """ _fko.verify_hmac(self.ctx, hmac_key) def set_spa_hmac(self, hmac_key): """Calculate the HMAC for the given data """ _fko.set_spa_hmac(self.ctx, hmac_key) def get_spa_hmac(self): """Return the HMAC for the data in the current context """ return _fko.get_spa_hmac(self.ctx) # GPG-related functions. def gpg_recipient(self, val=None): """Get or set the gpg_recipient. This is the ID or email of the public GPG key of the intended recipient. In order for this function to work, the following conditions must be met: - The underlying libfko implementation must have GPG support. - The encryption_type must be set to "FKO_ENCRYPTION_GPG". - The specified GPG key must exist and be valid. If no argument is given, the current value is returned. Otherwise, gpg_recipient will be set to the given value. """ if val != None: _fko.set_gpg_recipient(self.ctx, val) else: return _fko.get_gpg_recipient(self.ctx) def gpg_signer(self, val=None): """Get or set the gpg_signer. This is the ID or email for the secret GPG key to be used to sign the encryped data. In order for this function to work, the following conditions must be met: - The underlying libfko implementation must have GPG support. - The encryption_type must be set to "FKO_ENCRYPTION_GPG". - The specified GPG key must exist and be valid. If no argument is given, the current value is returned. Otherwise, gpg_signer will be set to the given value. """ if val != None: _fko.set_gpg_signer(self.ctx, val) else: return _fko.get_gpg_signer(self.ctx) def gpg_home_dir(self, val=None): """Get or set the GPG home directory. This is the directory that holds the GPG keyrings, etc. In order for this function to work, the following conditions must be met: - The underlying libfko implementation must have GPG support. - The encryption_type must be set to "FKO_ENCRYPTION_GPG". - The specified GPG home directory must exist. If no argument is given, the current value is returned. Otherwise, gpg_home_dir will be set to the given value. """ if val != None: _fko.set_gpg_home_dir(self.ctx, val) else: return _fko.get_gpg_home_dir(self.ctx) def gpg_signature_verify(self, val=None): """Get or set the GPG signature verification flag. If true (1), then GPG signatures are processed by libfko. This is the default behavior. If set to false (0), then libfko will not even look for or at any GPG signatures and will proceed with a decoding the SPA data. If no argument is given, the current value is returned. Otherwise, the gpg_signature_verify flag will be set to the given value. """ if val != None: _fko.set_gpg_signature_verify(self.ctx, val) else: return _fko.get_gpg_signature_verify(self.ctx) def gpg_ignore_verify_error(self, val=None): """Get or set the GPG signature ignore verification error flag. If true (1), then GPG signatures are processed and retained by libfko, but a bad signature will not prevent the decoding phase. The default is to not ignore errors. If no argument is given, the current value is returned. Otherwise, the gpg_ignore_verify_error flag will be set to the given value. """ if val != None: _fko.set_gpg_ignore_verify_error(self.ctx, val) else: return _fko.get_gpg_ignore_verify_error(self.ctx) def gpg_exe(self, val=None): """Get or set the path the the GPG executable libfko should use. If no argument is given, the current value is returned. Otherwise, gpg_exe will be set to the given value. """ if val != None: _fko.set_gpg_exe(self.ctx, val) else: return _fko.get_gpg_exe(self.ctx) def gpg_signature_id(self): """Get ID of the GPG signature from the last decryption operation. """ return _fko.get_gpg_signature_id(self.ctx) def gpg_signature_fpr(self): """Get Fingerprint of the GPG signature from the last decryption operation. """ return _fko.get_gpg_signature_fpr(self.ctx) def gpg_signature_summary(self): """Get GPGME signature summary value of the GPG signature from the last decryption operation. This value is a bitmask that hold additional information on the signature (see GPGME docs for more information). """ return _fko.get_gpg_signature_summary(self.ctx) def gpg_signature_status(self): """Get error status of the GPG signature from the last decryption operation. This value is a GPGME error code (see GPGME docs for more information). """ return _fko.get_gpg_signature_status(self.ctx) def gpg_signature_id_match(self, val): """Compare the given ID with the id of the GPG signature of the last decryption operation. If the ID's match, then a true value is returned. Otherwise false is returned. """ if _fko.gpg_signature_id_match(self.ctx) > 0: return True return False def gpg_signature_fpr_match(self, val): """Compare the given fingerprint value with the fingerprint of the GPG signature of the last decryption operation. If the ID's match, then a true value is returned. Otherwise false is returned. """ if _fko.gpg_signature_fpr_match(self.ctx) > 0: return True return False def gpg_errstr(self): """Return the last GPG-related error on the current context """ _fko.fko_gpg_errstr(self.ctx) # Error message string function. def errstr(self, val): """Returns the descriptive error message string for the given error code value. """ return _fko.errstr(val) # FKO type lookup functions. def message_type_str(self, val=None): """Returns the message type string for the given value. """ if val == None: val = _fko.get_spa_message_type(self.ctx) if val == FKO_COMMAND_MSG: mts = "Command Message" elif val == FKO_ACCESS_MSG: mts = "Access Message" elif val == FKO_NAT_ACCESS_MSG: mts = "NAT Access Message" elif val == FKO_CLIENT_TIMEOUT_ACCESS_MSG: mts = "Access Message with timeout" elif val == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG: mts = "NAT access Message with timeout" elif val == FKO_LOCAL_NAT_ACCESS_MSG: mts = "Local NAT Access Message" elif val == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG: mts = "Local NAT Access Message with timeout" else: mts = "Unknown SPA message type" return mts def digest_type_str(self, val=None): """Returns the digest type string for the given value. If no value is given, the digest type for the current context is returned. """ if val == None: val = _fko.get_spa_digest_type(self.ctx) if val == FKO_DIGEST_INVALID_DATA: dts = "invalid_data" elif val == FKO_DIGEST_UNKNOWN: dts = "unknown" elif val == FKO_DIGEST_MD5: dts = "MD5" elif val == FKO_DIGEST_SHA1: dts = "SHA1" elif val == FKO_DIGEST_SHA256: dts = "SHA256" elif val == FKO_DIGEST_SHA384: dts = "SHA384" elif val == FKO_DIGEST_SHA512: dts = "SHA512" elif val == FKO_DIGEST_SHA3_256: dts = "SHA3_256" elif val == FKO_DIGEST_SHA3_512: dts = "SHA3_512" else: dts = "Invalid digest type value" return dts def hmac_type_str(self, val=None): """Returns the HMAC type string for the given value. If no value is given, the HMAC type for the current context is returned. """ if val == None: val = _fko.get_spa_hmac_type(self.ctx) if val == FKO_HMAC_INVALID_DATA: ht = "invalid_data" elif val == FKO_HMAC_UNKNOWN: ht = "unknown" elif val == FKO_HMAC_MD5: ht = "MD5" elif val == FKO_HMAC_SHA1: ht = "SHA1" elif val == FKO_HMAC_SHA256: ht = "SHA256" elif val == FKO_HMAC_SHA384: ht = "SHA384" elif val == FKO_HMAC_SHA512: ht = "SHA512" elif val == FKO_HMAC_SHA3_256: ht = "SHA3_256" elif val == FKO_HMAC_SHA3_512: ht = "SHA3_512" else: ht = "Invalid HMAC digest type value" return ht def encryption_type_str(self, val=None): """Returns the encryption type string for the given value. If no value is given, the encryption type for the current context is returned. """ if val == None: val = _fko.get_spa_encryption_type(self.ctx) if val == FKO_ENCRYPTION_INVALID_DATA: ets = "invalid_data" elif val == FKO_ENCRYPTION_UNKNOWN: ets = "unknown" elif val == FKO_ENCRYPTION_RIJNDAEL: ets = "Rijndael (AES)" elif val == FKO_ENCRYPTION_GPG: ets = "GPG" else: ets = "Unknown encryption type" return ets def encryption_mode_str(self, val=None): """Returns the encryption mode string for the given value. If no value is given, the encryption mode for the current context is returned. """ if val == None: val = _fko.get_spa_encryption_mode(self.ctx) if val == FKO_ENC_MODE_UNKNOWN: dts = "unknown" elif val == FKO_ENC_MODE_ECB: dts = "ECB" elif val == FKO_ENC_MODE_CBC: dts = "CBC" elif val == FKO_ENC_MODE_CFB: dts = "CFB" elif val == FKO_ENC_MODE_PCBC: dts = "PCBC" elif val == FKO_ENC_MODE_OFB: dts = "OFB" elif val == FKO_ENC_MODE_CTR: dts = "CTR" elif val == FKO_ENC_MODE_ASYMMETRIC: dts = "ASYMMETRIC" elif val == FKO_ENC_MODE_CBC_LEGACY_IV: dts = "CBC_LEGACY_IV" else: dts = "Invalid encryption mode value" return dts def __call__(self): """Calls the spa_data() method. If an Fko object is called directly, then it will return the SPA data string for that object. """ try: return self.spa_data() except: return None class FkoAccess(): """Class for creating SPA Access Request message strings. """ def _check_port(self, port): """Internal function that validates a port or list of ports. """ plist = [] if type(port) is int: plist.append(port) elif type(port) is list: plist += port else: raise FkoException("Invalid type: not an integer or a list") for p in plist: if type(p) is not int: raise FkoException("Port value not an integer") if p < 1 or p > 65535: raise FkoException("Port value out of range: 1-65535") return plist def __init__(self, host="0.0.0.0", proto="tcp", port=None): """Constructor for the FkoAccess class. The three optional arguments are: - host - hostname or IP address (default is 0.0.0.0). - proto - protocol, which can be "tcp" (default) or "udp". - port - integer or list of integers representing the port(s) access beinbg requested. """ self.host = host self.proto = proto if port is None: self.port = [] else: self.port = self._check_port(port) def setport(self, port): """Set the port(s) for the Access Request. Takes either an integer or a list of integers and replaces the FkoAccess object's requested ports. """ self.port = self._check_port(port) def addport(self, port): """Add the port(s) to the Access Request. Takes either an integer or a list of integers and adds them to the the existing FkoAccess object's requested ports. """ self.port += self._check_port(port) def delport(self, port): """Remove the port(s) from the Access Request. Takes either an integer or a list of integers and removes any matching ports from the FkoAccess object's requested ports list. """ plist = self._check_port(port) try: for p in plist: if p in self.port: self.port.remove(p) except: pass def str(self): """Return the Access Request string. Generates and returns the properly formatted Access Request string based on the object's host, proto, and ports values. """ if len(self.port) < 1: raise FkoException("No port value in FkoAccess") return self.host+','+self.proto+'/'+join(map(str,self.port),",") def __call__(self): """Calls the str() method. If an FkoAccess object is called directly, then it will return the Access Request string for that object. """ return self.str() class FkoNatAccess(): """Class for creating SPA NAT Access Request message strings. """ def __init__(self, ip, port): """Constructor for the FkoNatAccess class. The two required arguments are: - ip - IP address of the NAT destination. - port - Port number of the NAT destination. """ if type(port) is not int: raise FkoException("Port value not an integer") if port < 1 and port > 65535: raise FkoException("Port value out of range 1-65535") self.ip = ip self.port = port def str(self): """Return the NAT Access Request string. Generates and returns the properly formatted NAT Access Request string based on the object's ip and port values. """ return join([self.ip, str(self.port)], ",") def __call__(self): """Calls the str() method. If an FkoNatAccess object is called directly, then it will return the NAT Access Request string for that object. """ return self.str() ###EOF### fwknop-2.6.10/test/0000775000175000017500000000000013332165577011102 500000000000000fwknop-2.6.10/test/long_spa.key0000664000175000017500000000015013332165274013324 00000000000000127.0.0.1: 12345678901234567890 localhost: 12345678901234567890 some.host.through.proxy.com: fwknoptest fwknop-2.6.10/test/local_spa.key0000664000175000017500000000012413332165274013460 00000000000000127.0.0.1: fwknoptest localhost: fwknoptest some.host.through.proxy.com: fwknoptest fwknop-2.6.10/test/invalid3.key0000664000175000017500000000001213332165274013230 00000000000000127.0.0.1 fwknop-2.6.10/test/spa_fuzzing.py0000775000175000017500000003501213332165274013731 00000000000000#!/usr/bin/env python # # Purpose: This script generates SPA packet payloads that are designed to # act as fuzzer against libfko SPA decoding routines. # # Fuzzing file format: # # # # SPA payload formats: # # ::::: # # Example SPA payload (after inner base64 encoding): # # 1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw # import base64 import argparse ### a few constants spa_success = 1 spa_failure = 0 spa_sha256 = 3 do_digest = 1 no_digest = 0 def main(): args = parse_cmdline() print_hdr() spa_payloads = [ # type 1: normal access request # : root : ts : ver :1: 127.0.0.2,tcp/23 "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw", # type 1: normal access request (with multi-port access request) # : root : ts : ver :1: 127.0.0.2,tcp/60001,udp/60001 "9129760493055133:cm9vdA:1399176256:2.0.1:1:MTI3LjAuMC4yLHRjcC82MDAwMSx1ZHAvNjAwMDE", # type 1: normal access request (with optional server_auth field) # : root : ts : ver :1: 127.0.0.2,tcp/23 : passwd "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw:cGFzc3dk", # type 0: command mode # : root : ts : ver :0: 127.0.0.2,echo fwknoptest > /tmp/fwknoptest "3145808919615481:cm9vdA:1397329998:2.0.1:0:MTI3LjAuMC4yLGVjaG8gZndrbm9wdGVzdCA+IC90bXAvZndrbm9wdGVzdA", # type 2: NAT access request # : root : ts : ver :2: 127.0.0.2,tcp/22 : 192.168.1.2,22 "1642197848921959:cm9vdA:1397329740:2.0.1:2:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xLjIsMjI", # type 3: normal access request with client timeout # : root : ts : ver :3: 127.0.0.2,tcp/22 : fw timeout 2 "1548062350109656:cm9vdA:1397330450:2.0.1:3:MTI3LjAuMC4yLHRjcC8yMg:2", # type 4: NAT mode with client timeout # : root : ts : ver :4: 127.0.0.2,tcp/22 : 192.168.10.1,12345 : fw timeout 1234 "1414212790438062:cm9vdA:1397329054:2.0.1:4:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xMC4xLDEyMzQ1:1234", # this one is kind of bogus (the final field decodes to non-printable data) "3184260168681452:c29tZXVzZXI:1397330288:2.0.1:4:MS4xLjEuMSx0Y3AvMjI:MS4yLjMuNCwxMjM0:10:GboVlHuyiwjxmHbH16vGvlKF", # type 5: local NAT request # : root : ts : ver :5: 127.0.0.2,tcp/37172 : 127.0.0.1,22 "8148229791462660:cm9vdA:1397331007:2.0.1:5:MTI3LjAuMC4yLHRjcC8zNzE3Mg:MTI3LjAuMC4xLDIy", # type 6: local NAT request with client timeout # : root : ts : ver :6: 127.0.0.2,tcp/22 : 127.0.0.1,22 : fw timeout 1234 "1918702109191551:cm9vdA:1397329052:2.0.1:6:MTI3LjAuMC4yLHRjcC8yMg:MTI3LjAuMC4xLDIy:1234" ] pkt_id = 1 payload_num = 0 for spa_payload in spa_payloads: payload_num += 1 print "# start tests with payload: ", spa_payload + "\n" \ "# base64 encoded original payload:", base64.b64encode(spa_payload) ### valid payload tests - all digest types pkt_id = valid_payloads(args, spa_payload, payload_num, pkt_id) ### fuzz individual payload fields pkt_id = field_fuzzing(args, spa_payload, payload_num, pkt_id) ### invalid digest types pkt_id = invalid_digest_types(args, spa_payload, payload_num, pkt_id) ### truncated lengths pkt_id = truncated_lengths(args, spa_payload, payload_num, pkt_id) ### remove chunks of chars out of the original SPA payload pkt_id = rm_chunks(args, spa_payload, payload_num, pkt_id) ### SPA payloads that are too long pkt_id = data_extensions(args, spa_payload, payload_num, pkt_id) ### additional embedded ':' chars pkt_id = embedded_separators(args, spa_payload, payload_num, pkt_id) ### non-ascii char tests pkt_id = embedded_chars(args, spa_payload, payload_num, pkt_id) return def field_fuzzing(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") field fuzzing..." repl_start = 0 repl_end = 0 field_num = 0 for idx in range(0, len(spa_payload)): if spa_payload[idx] == ':' or idx == len(spa_payload)-1: field_num += 1 if repl_end > 0: if idx == len(spa_payload)-1: orig_field = spa_payload[repl_end+1:] else: orig_field = spa_payload[repl_end+1:idx] else: orig_field = spa_payload[repl_end:idx] repl_start = repl_end repl_end = idx decoded = orig_field if is_field_b64(orig_field, field_num): decoded = spa_base64_decode(orig_field) ### first round of fuzzing for this field is to take the original ### field and permute it in various ways ### truncation for l in range(1, len(decoded)): pkt_id = write_fuzzing_payload(field_num, decoded[:l], \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) pkt_id = write_fuzzing_payload(field_num, decoded[l:], \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) ### remove chunks for bl in range(1, len(decoded)): for l in range(0, bl): fuzzing_field = decoded[:l] + decoded[l+bl:] pkt_id = write_fuzzing_payload(field_num, fuzzing_field, \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) ### append/prepend data for l in [1, 10, 50, 127, 128, 129, 200, 399, \ 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ 1200, 1499, 1500, 1501, 2000]: for non_ascii in range(0, 5) + range(127, 130) + range(252, 256): new_data = '' for p in range(0, l): new_data += chr(non_ascii) pkt_id = write_fuzzing_payload(field_num, decoded + new_data, \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) pkt_id = write_fuzzing_payload(field_num, new_data + decoded, \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) ### embedded separators for pos in range(0, len(decoded)): fuzzing_field = list(decoded) fuzzing_field[pos] = ':' pkt_id = write_fuzzing_payload(field_num, str(fuzzing_field), \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) ### embedded chars for pos in range(0, len(decoded)): for c in range(0, 31) + range(44, 48) + range(127, 131) + range(253, 255): fuzzing_field = list(decoded) fuzzing_field[pos] = chr(c) pkt_id = write_fuzzing_payload(field_num, str(fuzzing_field), \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) ### now generate fuzzing data for this field for c in range(0, 3) + range(33, 47) + range(65, 67) + range(127, 130) + range(252, 256): for l in [1, 2, 3, 4, 5, 6, 10, 14, 15, 16, 17, 24, 31, 32, 33, \ 63, 64, 127, 128, 129, 150, 220, 230, 254, 255, 256, 257, 258]: fuzzing_field = '' for n in range(0, l): fuzzing_field += chr(c) pkt_id = write_fuzzing_payload(field_num, fuzzing_field, \ orig_field, repl_start, repl_end, spa_payload, \ pkt_id, idx) return pkt_id def write_fuzzing_payload(field_num, fuzzing_field, orig_field, \ repl_start, repl_end, spa_payload, pkt_id, idx): new_payloads = [ spa_payload[:repl_start], ### replace original field with fuzzing field spa_payload[:repl_start], ### prepend fuzzing field to original spa_payload[:repl_start] ### append fuzzing field to original ] if field_num > 1: for i in range(0, len(new_payloads)): new_payloads[i] += ':' field_variants(new_payloads, fuzzing_field, \ orig_field, is_field_b64(orig_field, field_num)) if idx != len(spa_payload)-1: for i in range(0, len(new_payloads)): new_payloads[i] += spa_payload[repl_end:] for s in new_payloads: print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(s) pkt_id += 1 return pkt_id def is_field_b64(orig_field, field_num): ### this accounts for SPA packets with an optional client defined ### firewall timeout field at the end require_b64 = not orig_field.isdigit() if field_num == 1 or field_num in range(3, 6): ### fields: rand val, time stamp, version, and SPA type require_b64 = False elif field_num == 2: ### user field require_b64 = True return require_b64 def field_variants(new_payloads, fuzzing_field, orig_field, require_b64): if require_b64: decoded_orig_field = spa_base64_decode(orig_field) new_payloads[0] += spa_base64_encode(fuzzing_field) new_payloads[1] += spa_base64_encode(fuzzing_field+decoded_orig_field) new_payloads[2] += spa_base64_encode(decoded_orig_field+fuzzing_field) else: new_payloads[0] += fuzzing_field new_payloads[1] += fuzzing_field+orig_field new_payloads[2] += orig_field+fuzzing_field return def spa_base64_decode(b64str): ### account for how fwknop strips '=' chars remainder = len(b64str) % 4 if remainder != 0: for i in range(0, remainder): b64str += '=' return base64.b64decode(b64str) def spa_base64_encode(nonb64str): ### strip '=' chars like fwknop does return base64.b64encode(nonb64str).replace('=', '') def valid_payloads(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") valid payload + valid digest types..." for digest_type in range(0, 6): print str(pkt_id), str(spa_success), str(do_digest), \ str(digest_type), base64.b64encode(spa_payload) pkt_id += 1 return pkt_id def invalid_digest_types(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") invalid digest types..." for digest_type in [-1, 6, 7]: print str(pkt_id), str(spa_success), str(do_digest), \ str(digest_type), base64.b64encode(spa_payload) pkt_id += 1 return pkt_id def truncated_lengths(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") truncated lengths..." for l in range(1, len(spa_payload)): print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(spa_payload[:l]) pkt_id += 1 for l in range(1, len(spa_payload)): print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(spa_payload[l:]) pkt_id += 1 return pkt_id def rm_chunks(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") splice blocks of chars..." for bl in range(1, 20): for l in range(0, len(spa_payload)): new_payload = spa_payload[:l] + spa_payload[l+bl:] print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(new_payload) pkt_id += 1 return pkt_id def data_extensions(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") payloads too long..." for l in [1, 10, 50, 127, 128, 129, 200, 399, \ 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ 1200, 1499, 1500, 1501, 2000]: for non_ascii in range(0, 5) + range(127, 130) + range(252, 256): new_data = '' for p in range(0, l): new_data += chr(non_ascii) ### append print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(spa_payload + new_data) pkt_id += 1 ### prepend print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(new_data + spa_payload) pkt_id += 1 return pkt_id def embedded_separators(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") additional embedded : chars..." for pos in range(0, len(spa_payload)): if spa_payload[pos] == ':': continue new_payload = list(spa_payload) new_payload[pos] = ':' print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(''.join(new_payload)) pkt_id += 1 return pkt_id def embedded_chars(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " (" + spa_payload + ") non-ascii char tests..." for pos in range(0, len(spa_payload)): for c in range(0, 31) + range(44, 48) + range(127, 131) + range(253, 255): new_payload = list(spa_payload) new_payload[pos] = chr(c) ### write out the fuzzing line print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(''.join(new_payload)) pkt_id += 1 return pkt_id def print_hdr(): print "#\n# This file was generated by the fwknop SPA packet fuzzer test/spa_fuzzing.py...\n#\n" \ "# \n#\n" return def parse_cmdline(): ### parse command line args parser = argparse.ArgumentParser() parser.add_argument("-c", "--max-packet-count", type=int, help="packet count", default=1000000) args = parser.parse_args() return args if __name__ == "__main__": main() fwknop-2.6.10/test/test-fwknop.pl0000775000175000017500000105667613332165274013662 00000000000000#!/usr/bin/perl -w # # This is the main driver program for the fwknop test suite. Test definitions # are imported from the tests/ directory. # use Cwd; use File::Copy; use File::Path; use IO::Socket; use Data::Dumper; use Getopt::Long 'GetOptions'; use Getopt::Long 'GetOptionsFromString'; use strict; use POSIX; #==================== config ===================== my $logfile = 'test.log'; our $local_key_file = 'local_spa.key'; our $long_key_file = 'long_spa.key'; ### > 16 bytes our $local_spa_key = 'fwknoptest'; our $local_hmac_key_file = 'local_hmac_spa.key'; my $output_dir = 'output'; our $conf_dir = 'conf'; my $run_dir = 'run'; our $run_tmp_dir_top = 'runtmp'; our $run_tmp_dir = "$run_tmp_dir_top/subdir1/subdir2"; my $cmd_out_tmp = 'cmd.out'; my $server_cmd_tmp = 'server_cmd.out'; my $openssl_cmd_tmp = 'openssl_cmd.out'; my $data_tmp = 'data.tmp'; my $key_tmp = 'key.tmp'; my $enc_save_tmp = 'openssl_save.enc'; my $test_suite_path = 'test-fwknop.pl'; my $username = ''; our $access_include_dir = "$conf_dir/access-include"; my $gpg_dirs_tar = 'gpg_dirs.tar.gz'; my $access_include_dirs_tar = 'access-include.tar.gz'; our $gpg_client_home_dir = "$conf_dir/client-gpg"; our $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw"; our $gpg_client_4096_bit_key_no_pw = "$conf_dir/client-gpg-large-no-pw"; our $gpg_client_subkey_no_pw = "$conf_dir/client-gpg-subkeys-no-pw"; our $replay_pcap_file = "$conf_dir/spa_replay.pcap"; our $multi_pkts_pcap_file = "$conf_dir/multi_pkts.pcap"; our $fcs_pcap_file = "$conf_dir/fcs_spa.pcap"; our $spa_over_http_pcap_file = "$conf_dir/spa_over_http.pcap"; our $spa_x_forwarded_for_pcap_file = "$conf_dir/spa_x_forwarded_for.pcap"; our $lib_dir = '../lib/.libs'; our $default_digest_file = "$run_dir/digest.cache"; our $default_pid_file = "$run_dir/fwknopd.pid"; our $tmp_rc_file = "$run_dir/fwknoprc"; our $rewrite_rc_file = "$run_dir/rewrite_fwknoprc"; our $rewrite_fwknopd_conf = "$run_dir/rewrite_fwknopd.conf"; our $rewrite_access_conf = "$run_dir/rewrite_access.conf"; our $rewrite_include_keys_access_conf = "$run_dir/rewrite_include_keys_access.conf"; our $rewrite_digest_file = "$run_dir/rewrite_digest.cache"; our $save_rc_file = "$run_dir/save_fwknoprc"; our $tmp_pkt_file = "$run_dir/tmp_spa.pkt"; our $tmp_args_file = "$run_dir/args.save"; our $fwknopCmd = '../client/.libs/fwknop'; our $fwknopdCmd = '../server/.libs/fwknopd'; our $gpg_server_key = '361BBAD4'; our $gpg_client_key = '6A3FAD56'; our $gpg_server_key2 = 'EF5AF06A'; our $gpg_client_large_key = '31415ADE'; our $gpg_server_large_key = '40051F51'; #gpg --homedir ./client-gpg-subkeys-no-pw/ --list-keys #./client-gpg-subkeys-no-pw//pubring.gpg #--------------------------------------- #pub 4096R/31415ADE 2015-03-23 #uid fwknop project (client multi subkeys, TESTING ONLY) #sub 4096R/82E1000B 2015-03-23 #sub 2048R/8377E3D8 2015-03-25 #sub 2048R/9CF38326 2015-03-25 our $gpg_client_subkey = '9CF38326'; ### last subkey in the keyring as shown above, ### and GPG_REMOTE_ID must match in access.conf our $loopback_ip = '127.0.0.1'; our $fake_ip = '127.0.0.2'; our $spoof_ip = '1.2.3.4'; our $internal_nat_host = '192.168.1.2'; our $force_nat_host = '192.168.1.123'; our $force_nat_host2 = '123.4.4.4'; our $force_nat_host3 = '8.1.2.3'; our $force_snat_host = '33.3.3.3'; our $default_spa_port = 62201; our $non_std_spa_port = 12345; our $invalid_key_file = 'invalid.key'; our $invalid_key_file2 = 'invalid2.key'; our $invalid_key_file3 = 'invalid2.key'; our $FW_TYPE = 'iptables'; ### default to iptables our $FW_PREFIX = 'IPT'; our $fw_conf_prefix = 'ipt'; my $prefer_iptables = 0; my $fw_bin = ''; my $fw_bin_and_prefix = ''; our $spoof_user = 'testuser'; my $valgrind_cov_dir = 'valgrind-coverage'; my $lcov_results_dir = 'lcov-results'; my $perl_mod_fko_dir = 'FKO'; my $python_fko_dir = 'python_fko'; my $python_script = 'fko-python.py'; my $python_path = ''; our $cmd_exec_test_file = '/tmp/fwknoptest'; my $default_key = 'fwknoptest'; my $asan_dir = 'asan'; my $tests_dir = 'tests'; our $rerun_failed_mode = 0; my @test_files = ( "$tests_dir/configure_args.pl", "$tests_dir/build_security.pl", "$tests_dir/preliminaries.pl", "$tests_dir/code_structure.pl", "$tests_dir/basic_operations.pl", "$tests_dir/cunit_tests.pl", "$tests_dir/rijndael.pl", "$tests_dir/rijndael_cmd_exec.pl", "$tests_dir/rijndael_hmac_cmd_exec.pl", "$tests_dir/rijndael_hmac_cmd_open_close.pl", "$tests_dir/rijndael_replay_attacks.pl", "$tests_dir/rijndael_fuzzing.pl", "$tests_dir/rijndael_backwards_compatibility.pl", "$tests_dir/rijndael_hmac.pl", "$tests_dir/rijndael_hmac_fuzzing.pl", "$tests_dir/fault_injection.pl", "$tests_dir/afl_fuzzing.pl", "$tests_dir/address_sanitizer.pl", "$tests_dir/os_compatibility.pl", "$tests_dir/perl_FKO_module.pl", "$tests_dir/python_fko.pl", "$tests_dir/gpg_no_pw.pl", "$tests_dir/gpg_no_pw_hmac.pl", "$tests_dir/gpg.pl", "$tests_dir/gpg_hmac.pl", ); #================== end config =================== our @build_security_client = (); ### imported from tests/build_security.pl our @build_security_server = (); our @build_security_libfko = (); our @preliminaries = (); ### from tests/preliminaries.pl our @code_structure_errstr = (); ### from tests/code_structure.pl (may include Coccinelle matches eventually) our @configure_args = (); ### from tests/configure_args.pl our @basic_operations = (); ### from tests/basic_operations.pl our @cunit_tests = (); ### from tests/cunit_tests.pl our @rijndael = (); ### from tests/rijndael.pl our @rijndael_cmd_exec = (); ### from tests/rijndael_cmd_exec.pl our @rijndael_hmac_cmd_exec = (); ### from tests/rijndael_hmac_cmd_exec.pl our @rijndael_hmac_cmd_open_close = (); ### from tests/rijndael_hmac_cmd_open_close.pl our @rijndael_replay_attacks = (); ### from tests/rijndael_replay_attacks.pl our @rijndael_hmac = (); ### from tests/rijndael_hmac.pl our @rijndael_fuzzing = (); ### from tests/rijndael_fuzzing.pl our @rijndael_hmac_fuzzing = (); ### from tests/rijndael_hmac_fuzzing.pl our @fault_injection = (); ### from tests/fault_injection.pl our @afl_fuzzing = (); ### from tests/alf_fuzzing.pl our @address_sanitizer = (); ### from tests/address_sanitizer.pl our @gpg_no_pw = (); ### from tests/gpg_now_pw.pl our @gpg_no_pw_hmac = (); ### from tests/gpg_now_pw_hmac.pl our @gpg = (); ### from tests/gpg.pl our @gpg_hmac = (); ### from tests/gpg_hmac.pl our @perl_FKO_module = (); ### from tests/perl_FKO_module.pl our @python_fko = (); ### from tests/python_fko.pl our @os_compatibility = (); ### from tests/os_compatibility.pl our @rijndael_backwards_compatibility = (); ### from tests/rijndael_backwards_compatibility.pl my $passed = 0; my $failed = 0; my $executed = 0; my $test_include = ''; my @tests_to_include = (); my $test_exclude = ''; my @tests_to_exclude = (); my %include_tracking = (); my %exclude_tracking = (); my $do_crash_check = 1; my %valgrind_flagged_fcns = (); my %valgrind_flagged_fcns_unique = (); my $previous_valgrind_coverage_dir = ''; our $uniq_keys = 100; my $test_limit = 0; my $list_mode = 0; my $diff_dir1 = ''; my $diff_dir2 = ''; our $loopback_intf = ''; my $default_pkt_tries = 20; my $send_all_loop_once = 0; my $detect_server_loop_once = 0; my $default_server_tries = 10; my $anonymize_results = 0; my $orig_config_args = ''; my $curr_test_file = 'init'; my $init_file = $curr_test_file; my $config_log = '../config.log'; my $tarfile = 'test_fwknop.tar.gz'; our $key_gen_file = "$output_dir/key_gen"; our $verbose_str = "--verbose --verbose"; my $gdb_test_file = ''; our $resolve_url = 'http://www.cipherdyne.org/cgi-bin/myip/'; ### with trailing slash for test coverage our $resolve_url_with_port = 'http://www.cipherdyne.org:80/cgi-bin/myip'; my $fuzzing_pkts_file = '../perl/FKO/t/fuzzing_spa_packets'; my $fuzzing_pkts_append = 0; my $fuzzing_key = 'testtest'; my $fuzzing_num_pkts = 0; my $fuzzing_test_tag = ''; my $fuzzing_class = 'bogus data'; my %fuzzing_spa_packets = (); my $total_fuzzing_pkts = 0; our $sudo_access_conf = "$run_dir/sudo_access.conf"; my $sudo_conf_testing = ''; my $server_test_file = ''; my $client_only_mode = 0; my $server_only_mode = 0; my $enable_cores_pattern_mode = 0; my $cores_pattern_sh = './generate_cores.sh'; my $enable_fault_injection = 0; my $disable_fault_injection = 0; my $enable_valgrind = 0; my $disable_valgrind = 0; my $enable_valgrind_gen_suppressions = 0; my $valgrind_disable_suppressions = 0; my $valgrind_disable_child_silent = 0; my $valgrind_suppressions_file = cwd() . '/valgrind_suppressions'; our $valgrind_str = ''; my $asan_mode = 0; my $ubsan_mode = 0; my %cached_fw_policy = (); my $cpan_valgrind_mod = 'Test::Valgrind'; my %prev_valgrind_cov = (); my %prev_valgrind_file_titles = (); my $libfko_hdr_file = '../lib/fko.h'; my $libfko_errstr_file = '../lib/fko_error.c'; my $perl_libfko_constants_file = '../perl/FKO/lib/FKO_Constants.pl'; my $python_libfko_constants_file = '../python/fko.py'; our $fko_wrapper_dir = 'fko-wrapper'; our $wrapper_exec_script = 'run.sh'; our $wrapper_exec_script_valgrind = 'run_valgrind.sh'; my $fuzz_spa_payloads_file = $fko_wrapper_dir . '/fuzz_spa_payloads'; our $send_fuzz_payloads_file = $fko_wrapper_dir . '/send_spa_payloads'; my $python_spa_packet = ''; my $pkts_file = ''; my $enable_fuzzing_interfaces_tests = 0; my $enable_client_ip_resolve_test = 0; my $enable_all = 0; my $enable_complete = 0; my $saved_last_results = 0; my $diff_mode = 0; my $enc_dummy_key = 'A'x8; my $fko_obj = (); my $enable_recompilation_warnings_check = 0; my $enable_configure_args_checks = 0; my $enable_profile_coverage_check = 0; my $enable_profile_coverage_init = 0; my $profile_gen_report_sh = './gen-coverage-report.sh'; my $profile_init_sh = './init-lcov.sh'; my $profile_rm_files_sh = './rm-coverage-files.sh'; my $do_profile_init = 0; my $enable_make_distcheck = 0; my $enable_perl_module_checks = 0; my $enable_perl_module_fuzzing_spa_pkt_generation = 0; my $enable_python_module_checks = 0; my $enable_openssl_compatibility_tests = 0; my $enable_cunit_tests = 0; my $openssl_success_ctr = 0; my $openssl_failure_ctr = 0; my $openssl_ctr = 0; my $openssl_hmac_success_ctr = 0; my $openssl_hmac_failure_ctr = 0; my $openssl_hmac_ctr = 0; my $openssl_hmac_hexkey_supported = 0; my $fuzzing_success_ctr = 0; my $fuzzing_failure_ctr = 0; my $fuzzing_ctr = 0; my $include_permissions_warnings = 0; my $lib_view_cmd = ''; my $git_path = ''; our $valgrind_path = ''; our $fiu_run_path = ''; our $sudo_path = ''; our $gcov_path = ''; my $touch_path = ''; my $lcov_path = ''; my $coverage_diff_path = 'coverage_diff.py'; my $genhtml_path = ''; our $killall_path = ''; our $pgrep_path = ''; our $pkill_path = ''; our $openssl_path = ''; our $base64_path = ''; our $pinentry_fail = 0; our $perl_path = ''; our $prove_path = ''; our $ifconfig_path = ''; our $platform = ''; our $help = 0; our $YES = 1; our $NO = 0; our $IGNORE = 2; our $PRINT_LEN = 68; our $USE_PREDEF_PKTS = 1; our $READ_PKTS_FROM_FILE = 4; our $USE_CLIENT = 2; our $USE_PCAP_FILE = 3; our $REQUIRED = 1; our $OPTIONAL = 0; our $OPTIONAL_NUMERIC = 2; our $NEW_RULE_REQUIRED = 1; our $REQUIRE_NO_NEW_RULE = 2; our $NEW_RULE_REMOVED = 1; our $REQUIRE_NO_NEW_REMOVED = 2; our $MATCH_ANY = 1; our $MATCH_ALL = 2; our $REQUIRE_SUCCESS = 0; our $REQUIRE_FAILURE = 1; my $TIMESTAMP_DIFF = 2; my $ENC_RIJNDAEL = 1; my $ENC_GPG = 2; our $LINUX = 1; our $FREEBSD = 2; our $MACOSX = 3; our $OPENBSD = 4; our $start_time = time(); my $SERVER_RECEIVE_CHECK = 1; my $NO_SERVER_RECEIVE_CHECK = 2; my $APPEND_RESULTS = 1; my $NO_APPEND_RESULTS = 2; my %sigs = ( 'SIGHUP' => 1, 'SIGINT' => 2, 'SIGUSR1' => 10, 'SIGUSR2' => 12, 'SIGTSTP' => 20, ); my @sigs_ordered = ( 'SIGHUP', 'SIGINT', 'SIGUSR1', 'SIGUSR2', 'SIGTSTP', ); my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4 my @args_cp = @ARGV; exit 1 unless GetOptions( 'Anonymize-results' => \$anonymize_results, 'fwknop-path=s' => \$fwknopCmd, 'fwknopd-path=s' => \$fwknopdCmd, 'lib-dir=s' => \$lib_dir, ### for LD_LIBRARY_PATH 'loopback-intf=s' => \$loopback_intf, 'test-include=s' => \$test_include, 'include=s' => \$test_include, ### synonym 'test-exclude=s' => \$test_exclude, 'exclude=s' => \$test_exclude, ### synonym 'enable-perl-module-checks' => \$enable_perl_module_checks, 'enable-perl-module-pkt-generation' => \$enable_perl_module_fuzzing_spa_pkt_generation, 'enable-python-module-checks' => \$enable_python_module_checks, 'fuzzing-pkts-file=s' => \$fuzzing_pkts_file, 'fuzzing-pkts-append' => \$fuzzing_pkts_append, 'fuzzing-test-tag=s' => \$fuzzing_test_tag, 'fuzzing-class=s' => \$fuzzing_class, 'prefer-iptables' => \$prefer_iptables, 'enable-recompile-check' => \$enable_recompilation_warnings_check, 'enable-configure-args-checks' => \$enable_configure_args_checks, 'enable-profile-coverage-check' => \$enable_profile_coverage_check, 'enable-cores-pattern' => \$enable_cores_pattern_mode, 'enable-profile-coverage-init' => \$enable_profile_coverage_init, 'enable-ip-resolve' => \$enable_client_ip_resolve_test, 'enable-distcheck' => \$enable_make_distcheck, 'enable-dist-check' => \$enable_make_distcheck, ### synonym 'enable-openssl-checks' => \$enable_openssl_compatibility_tests, 'enable-cunit' => \$enable_cunit_tests, 'gdb-test=s' => \$gdb_test_file, 'List-mode' => \$list_mode, 'test-limit=i' => \$test_limit, 'enable-fault-injection' => \$enable_fault_injection, 'disable-fault-injection' => \$disable_fault_injection, 'enable-valgrind' => \$enable_valgrind, 'disable-valgrind' => \$disable_valgrind, 'valgrind-disable-suppressions' => \$valgrind_disable_suppressions, 'valgrind-disable-child-silent' => \$valgrind_disable_child_silent, 'enable-all' => \$enable_all, 'enable-complete' => \$enable_complete, 'enable-fuzzing-interfaces-tests' => \$enable_fuzzing_interfaces_tests, 'valgrind-path=s' => \$valgrind_path, 'valgrind-suppression-file' => \$valgrind_suppressions_file, 'enable-valgrind-gen-suppressions' => \$enable_valgrind_gen_suppressions, ### can set the following to "output.last/valgrind-coverage" if ### a full test suite run has already been executed with --enable-valgrind 'valgrind-prev-cov-dir=s' => \$previous_valgrind_coverage_dir, 'openssl-path=s' => \$openssl_path, 'fiu-run-path=s' => \$fiu_run_path, 'output-dir=s' => \$output_dir, 'cmd-verbose=s' => \$verbose_str, 'client-only-mode' => \$client_only_mode, 'server-only-mode' => \$server_only_mode, 'rerun-failed-mode' => \$rerun_failed_mode, 'diff' => \$diff_mode, 'diff-dir1=s' => \$diff_dir1, 'diff-dir2=s' => \$diff_dir2, 'help' => \$help ); &usage() if $help; our @last_logfile = (); if ($rerun_failed_mode) { unless (open (RE, "<", "test.log")) { # check for test.log die "[*] Can't find test.log"; } while () { push @last_logfile, $_; } close RE; my $arg_line = ""; for my $line (@last_logfile) { if ($line =~ /args:(.+)/) { $arg_line = $1; last; } } unless ($arg_line) { die "[*] Can't find arguments"; } system("cp -f test.log test.log.bak"); my $ret = 0; my $leftovers = ""; ($ret, $leftovers) = GetOptionsFromString( $arg_line, 'fwknop-path=s' => \$fwknopCmd, 'fwknopd-path=s' => \$fwknopdCmd, 'lib-dir=s' => \$lib_dir, ### for LD_LIBRARY_PATH 'loopback-intf=s' => \$loopback_intf, 'test-include=s' => \$test_include, 'include=s' => \$test_include, ### synonym 'test-exclude=s' => \$test_exclude, 'exclude=s' => \$test_exclude, ### synonym 'enable-perl-module-checks' => \$enable_perl_module_checks, 'enable-perl-module-pkt-generation' => \$enable_perl_module_fuzzing_spa_pkt_generation, 'enable-python-module-checks' => \$enable_python_module_checks, 'fuzzing-pkts-file=s' => \$fuzzing_pkts_file, 'fuzzing-pkts-append' => \$fuzzing_pkts_append, 'fuzzing-test-tag=s' => \$fuzzing_test_tag, 'fuzzing-class=s' => \$fuzzing_class, 'prefer-iptables' => \$prefer_iptables, 'enable-recompile-check' => \$enable_recompilation_warnings_check, 'enable-configure-args-checks' => \$enable_configure_args_checks, 'enable-profile-coverage-check' => \$enable_profile_coverage_check, 'enable-cores-pattern' => \$enable_cores_pattern_mode, 'enable-profile-coverage-init' => \$enable_profile_coverage_init, 'enable-ip-resolve' => \$enable_client_ip_resolve_test, 'enable-distcheck' => \$enable_make_distcheck, 'enable-dist-check' => \$enable_make_distcheck, ### synonym 'enable-openssl-checks' => \$enable_openssl_compatibility_tests, 'enable-cunit' => \$enable_cunit_tests, 'gdb-test=s' => \$gdb_test_file, 'List-mode' => \$list_mode, 'test-limit=i' => \$test_limit, 'enable-fault-injection' => \$enable_fault_injection, 'disable-fault-injection' => \$disable_fault_injection, 'enable-valgrind' => \$enable_valgrind, 'disable-valgrind' => \$disable_valgrind, 'valgrind-disable-suppressions' => \$valgrind_disable_suppressions, 'valgrind-disable-child-silent' => \$valgrind_disable_child_silent, 'enable-all' => \$enable_all, 'enable-complete' => \$enable_complete, 'enable-fuzzing-interfaces-tests' => \$enable_fuzzing_interfaces_tests, 'valgrind-path=s' => \$valgrind_path, 'valgrind-suppression-file' => \$valgrind_suppressions_file, 'enable-valgrind-gen-suppressions' => \$enable_valgrind_gen_suppressions, ### can set the following to "output.last/valgrind-coverage" if ### a full test suite run has already been executed with --enable-valgrind 'valgrind-prev-cov-dir=s' => \$previous_valgrind_coverage_dir, 'openssl-path=s' => \$openssl_path, 'fiu-run-path=s' => \$fiu_run_path, 'output-dir=s' => \$output_dir, 'cmd-verbose=s' => \$verbose_str, 'client-only-mode' => \$client_only_mode, 'server-only-mode' => \$server_only_mode, 'diff' => \$diff_mode, 'diff-dir1=s' => \$diff_dir1, 'diff-dir2=s' => \$diff_dir2 ); } &os_fw_detect(); ### main configuration file paths our %cf = ( "${fw_conf_prefix}_nat" => "$conf_dir/${fw_conf_prefix}_nat_fwknopd.conf", "${fw_conf_prefix}_nat_disable_aging" => "$conf_dir/${fw_conf_prefix}_nat_disable_aging_fwknopd.conf", "${fw_conf_prefix}_snat" => "$conf_dir/${fw_conf_prefix}_snat_fwknopd.conf", "${fw_conf_prefix}_snat_no_translate_ip" => "$conf_dir/${fw_conf_prefix}_snat_no_translate_ip_fwknopd.conf", "${fw_conf_prefix}_snat_translate_ip" => "$conf_dir/${fw_conf_prefix}_snat_translate_ip_fwknopd.conf", 'def' => "$conf_dir/default_fwknopd.conf", 'def_access' => "$conf_dir/default_access.conf", 'portrange_filter' => "$conf_dir/portrange_fwknopd.conf", 'hmac_access' => "$conf_dir/hmac_access.conf", 'include1_hmac_access' => "$conf_dir/include1_hmac_access.conf", 'include2_hmac_access' => "$conf_dir/include2_hmac_access.conf", 'include_r1_hmac_access' => "$conf_dir/include_r1_hmac_access.conf", 'include_r2_hmac_access' => "$conf_dir/include_r2_hmac_access.conf", 'include_m1_hmac_access' => "$conf_dir/include_m1_hmac_access.conf", 'include_def_hmac_access' => "$conf_dir/include_def_hmac_access.conf", 'include_keys1_hmac_access' => "$conf_dir/include_keys1_hmac_access.conf", 'prepend_fwknopd' => "$conf_dir/prepend_fwknopd.conf", "${fw_conf_prefix}_no_nat_dns_fwknopd" => "$conf_dir/${fw_conf_prefix}_no_nat_dns_fwknopd.conf", 'hmac_cmd_access' => "$conf_dir/hmac_cmd_access.conf", 'hmac_cmd_setuid_access' => "$conf_dir/hmac_cmd_setuid_access.conf", 'hmac_cmd_giduid_access' => "$conf_dir/hmac_cmd_giduid_access.conf", 'hmac_get_key_access' => "$conf_dir/hmac_get_key_access.conf", 'hmac_equal_keys_access' => "$conf_dir/hmac_equal_keys_access.conf", 'hmac_no_b64_access' => "$conf_dir/hmac_no_b64_access.conf", 'hmac_md5_access' => "$conf_dir/hmac_md5_access.conf", 'hmac_md5_short_key_access' => "$conf_dir/hmac_md5_short_key_access.conf", 'hmac_md5_long_key_access' => "$conf_dir/hmac_md5_long_key_access.conf", 'hmac_sha1_access' => "$conf_dir/hmac_sha1_access.conf", 'hmac_sha1_short_key_access' => "$conf_dir/hmac_sha1_short_key_access.conf", 'hmac_sha1_long_key_access' => "$conf_dir/hmac_sha1_long_key_access.conf", 'hmac_sha256_access' => "$conf_dir/hmac_sha256_access.conf", 'hmac_sha3_256_access' => "$conf_dir/hmac_sha3_256_access.conf", 'hmac_sha256_digest1_mismatch_access' => "$conf_dir/hmac_sha256_digest1_mismatch_access.conf", 'hmac_sha256_digest2_mismatch_access' => "$conf_dir/hmac_sha256_digest2_mismatch_access.conf", 'hmac_sha256_digest3_mismatch_access' => "$conf_dir/hmac_sha256_digest3_mismatch_access.conf", 'hmac_sha256_digest4_mismatch_access' => "$conf_dir/hmac_sha256_digest4_mismatch_access.conf", 'hmac_sha256_short_key_access' => "$conf_dir/hmac_sha256_short_key_access.conf", 'hmac_sha256_long_key_access' => "$conf_dir/hmac_sha256_long_key_access.conf", 'hmac_sha384_access' => "$conf_dir/hmac_sha384_access.conf", 'hmac_sha384_short_key_access' => "$conf_dir/hmac_sha384_short_key_access.conf", 'hmac_sha384_long_key_access' => "$conf_dir/hmac_sha384_long_key_access.conf", 'hmac_sha512_access' => "$conf_dir/hmac_sha512_access.conf", 'hmac_sha3_512_access' => "$conf_dir/hmac_sha3_512_access.conf", 'hmac_sha512_short_key_access' => "$conf_dir/hmac_sha512_short_key_access.conf", 'hmac_sha512_short_key2_access' => "$conf_dir/hmac_sha512_short_key2_access.conf", 'hmac_sha512_long_key_access' => "$conf_dir/hmac_sha512_long_key_access.conf", 'hmac_simple_keys_access' => "$conf_dir/hmac_simple_keys_access.conf", 'hmac_invalid_type_access' => "$conf_dir/hmac_invalid_type_access.conf", 'hmac_cygwin_access' => "$conf_dir/hmac_no_b64_cygwin_access.conf", 'hmac_cmd_open_close_cycle_access' => "$conf_dir/hmac_cmd_open_close_cycle_access.conf", 'hmac_cmd_open_close_cycle_access2' => "$conf_dir/hmac_cmd_open_close_cycle_access2.conf", 'hmac_cmd_open_close_cycle_access3' => "$conf_dir/hmac_cmd_open_close_cycle_access3.conf", 'hmac_cmd_open_close_cycle_access4' => "$conf_dir/hmac_cmd_open_close_cycle_access4.conf", 'hmac_cmd_open_close_cycle_access5' => "$conf_dir/hmac_cmd_open_close_cycle_access5.conf", 'hmac_cmd_open_close_cycle_access6' => "$conf_dir/hmac_cmd_open_close_cycle_access6.conf", 'hmac_cmd_open_close_cycle_access7' => "$conf_dir/hmac_cmd_open_close_cycle_access7.conf", 'hmac_cmd_open_close_cycle_access8' => "$conf_dir/hmac_cmd_open_close_cycle_access8.conf", 'hmac_cmd_open_close_multi_cycle_access' => "$conf_dir/hmac_cmd_open_close_multi_cycle_access.conf", 'spa_destination' => "$conf_dir/destination_rule_fwknopd.conf", "${fw_conf_prefix}_spa_dst_snat" => "$conf_dir/${fw_conf_prefix}_spa_dst_snat_fwknopd.conf", 'hmac_spa_destination_access' => "$conf_dir/hmac_spa_destination_access.conf", 'hmac_spa_destination2_access' => "$conf_dir/hmac_spa_destination2_access.conf", 'hmac_spa_destination3_access' => "$conf_dir/hmac_spa_destination3_access.conf", 'hmac_spa_destination4_access' => "$conf_dir/hmac_spa_destination4_access.conf", 'hmac_spa_destination5_access' => "$conf_dir/hmac_spa_destination5_access.conf", 'exp_access' => "$conf_dir/expired_stanza_access.conf", 'future_exp_access' => "$conf_dir/future_expired_stanza_access.conf", 'exp_epoch_access' => "$conf_dir/expired_epoch_stanza_access.conf", 'invalid_exp_access' => "$conf_dir/invalid_expire_access.conf", 'require_force_nat_access' => "$conf_dir/require_force_nat_access.conf", "${fw_conf_prefix}_output_chain" => "$conf_dir/${fw_conf_prefix}_output_chain_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain2" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_2_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain3" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_3_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain4" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_4_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain5" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_5_fwknopd.conf", "invalid_${fw_conf_prefix}_input_chain6" => "$conf_dir/invalid_${fw_conf_prefix}_input_chain_6_fwknopd.conf", 'invalid_run_dir_path' => "$conf_dir/invalid_run_dir_path_fwknopd.conf", 'force_nat_access' => "$conf_dir/force_nat_access.conf", 'hmac_force_nat_access' => "$conf_dir/hmac_force_nat_access.conf", 'hmac_force_nat_forward_all_access' => "$conf_dir/hmac_force_nat_forward_all_access.conf", 'hmac_force_snat_access' => "$conf_dir/hmac_force_snat_access.conf", 'hmac_force_masq_access' => "$conf_dir/hmac_force_masq_access.conf", 'hmac_force_masq_no_dnat_access' => "$conf_dir/hmac_force_masq_no_dnat_access.conf", 'hmac_forward_all_access' => "$conf_dir/hmac_forward_all_access.conf", 'hmac_forward_all_masq_access' => "$conf_dir/hmac_forward_all_masq_access.conf", 'hmac_forward_all_and_dna_access' => "$conf_dir/hmac_forward_all_and_dnat_access.conf", 'cmd_access' => "$conf_dir/cmd_access.conf", 'cmd_setuid_access' => "$conf_dir/cmd_setuid_access.conf", 'cmd_giduid_access' => "$conf_dir/cmd_giduid_access.conf", "${fw_conf_prefix}_local_nat" => "$conf_dir/${fw_conf_prefix}_local_nat_fwknopd.conf", "${fw_conf_prefix}_no_flush_init" => "$conf_dir/${fw_conf_prefix}_no_flush_init_fwknopd.conf", "${fw_conf_prefix}_no_flush_exit" => "$conf_dir/${fw_conf_prefix}_no_flush_exit_fwknopd.conf", "${fw_conf_prefix}_no_flush_init_or_exit" => "$conf_dir/${fw_conf_prefix}_no_flush_init_or_exit_fwknopd.conf", 'ipfw_active_expire' => "$conf_dir/ipfw_active_expire_equal_fwknopd.conf", 'hmac_android_access' => "$conf_dir/hmac_android_access.conf", 'hmac_android_fdroid_access' => "$conf_dir/hmac_android_fdroid_access.conf", 'android_access' => "$conf_dir/android_access.conf", 'android_legacy_iv_access' => "$conf_dir/android_legacy_iv_access.conf", 'dual_key_access' => "$conf_dir/dual_key_usage_access.conf", 'dual_key_legacy_iv_access' => "$conf_dir/dual_key_legacy_iv_access.conf", 'hmac_dual_key_access' => "$conf_dir/hmac_dual_key_usage_access.conf", 'no_exit_down_intf' => "$conf_dir/no_exit_down_intf_fwknopd.conf", 'gpg_access' => "$conf_dir/gpg_access.conf", 'gpg_hmac_access' => "$conf_dir/gpg_hmac_access.conf", 'gpg_invalid_exe_access' => "$conf_dir/gpg_invalid_exe_access.conf", 'gpg_hmac_sha512_access' => "$conf_dir/gpg_hmac_sha512_access.conf", 'gpg_hmac_sha3_512_access' => "$conf_dir/gpg_hmac_sha3_512_access.conf", 'legacy_iv_access' => "$conf_dir/legacy_iv_access.conf", 'hmac_fuzzing_access' => "$conf_dir/hmac_fuzzing_access.conf", 'legacy_iv_long_key_access' => "$conf_dir/legacy_iv_long_key_access.conf", 'legacy_iv_long_key2_access' => "$conf_dir/legacy_iv_long_key2_access.conf", 'gpg_no_pw_access' => "$conf_dir/gpg_no_pw_access.conf", 'gpg_no_pw_fpr_access' => "$conf_dir/gpg_no_pw_fpr_access.conf", 'gpg_no_pw_bad_fpr_access' => "$conf_dir/gpg_no_pw_bad_fpr_access.conf", 'gpg_no_pw_no_fpr_access' => "$conf_dir/gpg_no_pw_no_fpr_access.conf", 'gpg_no_sig_no_fpr_access' => "$conf_dir/gpg_no_sig_no_fpr_access.conf", 'gpg_no_pw_hmac_access' => "$conf_dir/gpg_no_pw_hmac_access.conf", 'gpg_no_pw_hmac_clientdir_access' => "$conf_dir/gpg_no_pw_hmac_clientdir_access.conf", 'gpg_no_pw_hmac_serverdir_access' => "$conf_dir/gpg_no_pw_hmac_serverdir_access.conf", 'gpg_no_pw_hmac_sha512_access' => "$conf_dir/gpg_no_pw_hmac_sha512_access.conf", 'gpg_no_sig_verify_access' => "$conf_dir/gpg_no_sig_verify_access.conf", 'gpg_invalid_sig_id_access' => "$conf_dir/gpg_invalid_sig_id_access.conf", 'gpg_large_signing_key_access' => "$conf_dir/gpg_large_signing_key_access.conf", 'gpg_subkey_access' => "$conf_dir/gpg_subkey_access.conf", 'gpg_server_large_key_access' => "$conf_dir/gpg_server_large_key_access.conf", 'tcp_server' => "$conf_dir/tcp_server_fwknopd.conf", 'udp_server' => "$conf_dir/udp_server_fwknopd.conf", 'spa_over_http' => "$conf_dir/spa_over_http_fwknopd.conf", 'spa_x_forwarded_for' => "$conf_dir/spa_x_forwarded_for_fwknopd.conf", 'tcp_pcap_filter' => "$conf_dir/tcp_pcap_filter_fwknopd.conf", 'icmp_pcap_filter' => "$conf_dir/icmp_pcap_filter_fwknopd.conf", 'open_ports_access' => "$conf_dir/open_ports_access.conf", 'open_ports_force_masq_access' => "$conf_dir/open_ports_force_masq_access.conf", 'hmac_open_ports_access' => "$conf_dir/hmac_sha256_open_ports_access.conf", 'multi_gpg_access' => "$conf_dir/multi_gpg_access.conf", 'multi_gpg_no_pw_access' => "$conf_dir/multi_gpg_no_pw_access.conf", 'multi_stanza_access' => "$conf_dir/multi_stanzas_access.conf", 'broken_keys_access' => "$conf_dir/multi_stanzas_with_broken_keys.conf", 'ecb_mode_access' => "$conf_dir/ecb_mode_access.conf", 'ctr_mode_access' => "$conf_dir/ctr_mode_access.conf", 'cfb_mode_access' => "$conf_dir/cfb_mode_access.conf", 'ofb_mode_access' => "$conf_dir/ofb_mode_access.conf", 'open_ports_mismatch' => "$conf_dir/mismatch_open_ports_access.conf", 'require_user_access' => "$conf_dir/require_user_access.conf", 'user_mismatch_access' => "$conf_dir/mismatch_user_access.conf", 'require_src_access' => "$conf_dir/require_src_access.conf", 'invalid_src_access' => "$conf_dir/invalid_source_access.conf", 'no_src_match' => "$conf_dir/no_source_match_access.conf", 'no_subnet_match' => "$conf_dir/no_subnet_source_match_access.conf", 'no_multi_src' => "$conf_dir/no_multi_source_match_access.conf", 'multi_src_access' => "$conf_dir/multi_source_match_access.conf", 'ip_src_match' => "$conf_dir/ip_source_match_access.conf", 'subnet_src_match' => "$conf_dir/ip_source_match_access.conf", 'rc_def_key' => "$conf_dir/fwknoprc_with_default_key", 'rc_def_b64_key' => "$conf_dir/fwknoprc_with_default_base64_key", 'rc_named_key' => "$conf_dir/fwknoprc_named_key", 'rc_hmac_equal_keys' => "$conf_dir/fwknoprc_hmac_equal_keys", 'rc_invalid_b64_key' => "$conf_dir/fwknoprc_invalid_base64_key", 'rc_hmac_b64_key' => "$conf_dir/fwknoprc_default_hmac_base64_key", 'rc_hmac_defaults' => "$conf_dir/fwknoprc_hmac_defaults", 'rc_hmac_http_resolve' => "$conf_dir/fwknoprc_hmac_http_resolve", 'rc_hmac_https_resolve' => "$conf_dir/fwknoprc_hmac_https_resolve", 'rc_hmac_http_only_resolve' => "$conf_dir/fwknoprc_hmac_http_only_resolve", 'rc_hmac_nat_rand_b64_key' => "$conf_dir/fwknoprc_hmac_nat_rand_base64_key", 'rc_hmac_spoof_src_b64_key' => "$conf_dir/fwknoprc_hmac_spoof_src_base64_key", 'rc_hmac_sha512_b64_key' => "$conf_dir/fwknoprc_hmac_sha512_base64_key", 'rc_hmac_b64_key2' => "$conf_dir/fwknoprc_hmac_key2", 'rc_hmac_time_offset_mins' => "$conf_dir/fwknoprc_hmac_time_offset_mins", 'rc_hmac_time_offset_hours' => "$conf_dir/fwknoprc_hmac_time_offset_hours", 'rc_hmac_time_offset_days' => "$conf_dir/fwknoprc_hmac_time_offset_days", 'rc_rand_port_hmac_b64_key' => "$conf_dir/fwknoprc_rand_port_hmac_base64_key", 'rc_gpg_signing_pw' => "$conf_dir/fwknoprc_gpg_signing_pw", 'rc_gpg_named_signing_pw' => "$conf_dir/fwknoprc_named_gpg_signing_pw", 'rc_gpg_hmac_b64_key' => "$conf_dir/fwknoprc_gpg_hmac_key", 'rc_gpg_invalid_gpg_exe' => "$conf_dir/fwknoprc_gpg_invalid_exe", 'rc_gpg_hmac_sha512_b64_key' => "$conf_dir/fwknoprc_gpg_hmac_sha512_key", 'rc_gpg_args_hmac_b64_key' => "$conf_dir/fwknoprc_gpg_args_hmac_key", 'rc_gpg_args_no_pw_hmac_b64_key' => "$conf_dir/fwknoprc_gpg_args_no_pw_hmac_key", 'rc_hmac_simple_key' => "$conf_dir/fwknoprc_hmac_simple_keys", 'rc_hmac_invalid_type' => "$conf_dir/fwknoprc_hmac_invalid_type", 'rc_hmac_invalid_type' => "$conf_dir/fwknoprc_hmac_invalid_type", 'rc_hmac_md5_key' => "$conf_dir/fwknoprc_hmac_md5_key", 'rc_hmac_md5_short_key' => "$conf_dir/fwknoprc_hmac_md5_short_key", 'rc_hmac_md5_long_key' => "$conf_dir/fwknoprc_hmac_md5_long_key", 'rc_hmac_sha1_key' => "$conf_dir/fwknoprc_hmac_sha1_key", 'rc_hmac_sha1_short_key' => "$conf_dir/fwknoprc_hmac_sha1_short_key", 'rc_hmac_sha1_long_key' => "$conf_dir/fwknoprc_hmac_sha1_long_key", 'rc_hmac_sha256_key' => "$conf_dir/fwknoprc_hmac_sha256_key", 'rc_hmac_sha3_256_key' => "$conf_dir/fwknoprc_hmac_sha3_256_key", 'rc_hmac_sha256_short_key' => "$conf_dir/fwknoprc_hmac_sha256_short_key", 'rc_hmac_sha256_long_key' => "$conf_dir/fwknoprc_hmac_sha256_long_key", 'rc_hmac_sha384_key' => "$conf_dir/fwknoprc_hmac_sha384_key", 'rc_hmac_sha384_short_key' => "$conf_dir/fwknoprc_hmac_sha384_short_key", 'rc_hmac_sha384_long_key' => "$conf_dir/fwknoprc_hmac_sha384_long_key", 'rc_hmac_sha512_key' => "$conf_dir/fwknoprc_hmac_sha512_key", 'rc_hmac_sha3_512_key' => "$conf_dir/fwknoprc_hmac_sha3_512_key", 'rc_hmac_sha512_short_key' => "$conf_dir/fwknoprc_hmac_sha512_short_key", 'rc_hmac_sha512_long_key' => "$conf_dir/fwknoprc_hmac_sha512_long_key", 'rc_stanza_list' => "$conf_dir/fwknoprc_stanza_list", 'rc_cmd_open_close_multi_cycle' => "$conf_dir/fwknoprc_hmac_multi_base64_key", 'base64_key_access' => "$conf_dir/base64_key_access.conf", "${fw_conf_prefix}_custom_input_chain" => "$conf_dir/${fw_conf_prefix}_custom_input_chain_fwknopd.conf", "${fw_conf_prefix}_custom_nat_chain" => "$conf_dir/${fw_conf_prefix}_custom_nat_chain_fwknopd.conf", 'disable_aging' => "$conf_dir/disable_aging_fwknopd.conf", 'disable_aging_nat' => "$conf_dir/disable_aging_nat_fwknopd.conf", 'fuzz_source' => "$conf_dir/fuzzing_source_access.conf", 'fuzz_open_ports' => "$conf_dir/fuzzing_open_ports_access.conf", 'fuzz_restrict_ports' => "$conf_dir/fuzzing_restrict_ports_access.conf", ); our $lib_view_str = "LD_LIBRARY_PATH=$lib_dir"; our $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link if ($enable_all or $enable_complete) { $enable_valgrind = 1; $enable_recompilation_warnings_check = 1; $enable_configure_args_checks = 1; $enable_make_distcheck = 1; $enable_client_ip_resolve_test = 1; $enable_perl_module_checks = 1; $enable_python_module_checks = 1; $enable_openssl_compatibility_tests = 1; $enable_cunit_tests = 1; } if ($enable_complete) { $enable_fault_injection = 1; $enable_profile_coverage_check = 1; $enable_fuzzing_interfaces_tests = 1; $enable_cores_pattern_mode = 1; } $enable_valgrind = 0 if $disable_valgrind; $enable_fault_injection = 0 if $disable_fault_injection; unless (-d $output_dir) { mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!"; } ### create an anonymized tar file of test suite results that can be ### emailed around to assist in debugging fwknop communications exit &anonymize_results() if $anonymize_results; exit &diff_test_results() if $diff_mode; ### run an fwknop command under gdb from a previous test run exit &gdb_test_cmd() if $gdb_test_file; $ifconfig_path = &find_command('ifconfig') unless $ifconfig_path; &identify_loopback_intf() unless $list_mode or $client_only_mode; ### make sure everything looks as expected before continuing &init(); if ($enable_valgrind) { $valgrind_str = "$valgrind_path --leak-check=full " . "--show-reachable=yes --track-origins=yes"; unless ($valgrind_disable_suppressions) { $valgrind_str .= " --suppressions=$valgrind_suppressions_file"; } if ($enable_valgrind_gen_suppressions) { $valgrind_str .= ' --gen-suppressions=all'; } unless ($valgrind_disable_child_silent) { $valgrind_str .= ' --child-silent-after-fork=yes'; } } our $intf_str = "-i $loopback_intf --foreground $verbose_str"; our $default_client_args = "$lib_view_str $valgrind_str " . "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " . "$local_key_file --no-save-args $verbose_str"; our $default_client_args_long_key = "$lib_view_str $valgrind_str " . "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " . "$long_key_file --no-save-args $verbose_str"; our $default_client_args_no_get_key = "$lib_view_str " . "$valgrind_str $fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip " . "--no-save-args $verbose_str"; our $default_client_args_no_verbose = "$lib_view_str " . "$valgrind_str $fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip " . '--no-save-args '; our $client_rewrite_rc_args = "$default_client_args_no_get_key " . "--rc-file $rewrite_rc_file --test"; our $client_save_rc_args = "$default_client_args_no_get_key " . "--rc-file $save_rc_file --save-rc-stanza --force-stanza --test"; our $client_save_rc_args_no_verbose = "$default_client_args_no_verbose " . "--rc-file $save_rc_file --save-rc-stanza --force-stanza --test"; our $client_save_rc_args_no_force = "$default_client_args_no_get_key " . "--rc-file $save_rc_file --save-rc-stanza --test"; our $client_save_rc_args_no_test = "$lib_view_str $valgrind_str " . "$fwknopCmd -A tcp/22 -D $loopback_ip --no-save-args " . "--rc-file $save_rc_file --save-rc-stanza --force-stanza -v -v -v"; our $server_rewrite_conf_files = "$lib_view_str $valgrind_str $fwknopdCmd " . "-c $rewrite_fwknopd_conf -a $rewrite_access_conf " . "-d $default_digest_file -p $default_pid_file $intf_str"; our $default_client_hmac_args = "$default_client_args_no_get_key " . "--rc-file $cf{'rc_hmac_b64_key'}"; our $client_hmac_rc_defaults = "$lib_view_str $valgrind_str " . "$fwknopCmd --no-save-args --rc-file $cf{'rc_hmac_defaults'}"; our $client_hmac_rc_http_resolve = "$lib_view_str $valgrind_str " . "$fwknopCmd --no-save-args --rc-file $cf{'rc_hmac_http_resolve'}"; our $client_hmac_rc_https_resolve = "$lib_view_str $valgrind_str " . "$fwknopCmd --no-save-args --rc-file $cf{'rc_hmac_https_resolve'}"; our $client_hmac_rc_http_only_resolve = "$lib_view_str $valgrind_str " . "$fwknopCmd --no-save-args --rc-file $cf{'rc_hmac_http_only_resolve'}"; our $client_ip_resolve_args = "$lib_view_str $valgrind_str " . "$fwknopCmd -A tcp/22 -R -D $loopback_ip --get-key " . "$local_key_file $verbose_str"; our $client_ip_resolve_hmac_args = "$lib_view_str $valgrind_str " . "$fwknopCmd -A tcp/22 -R -D $loopback_ip --rc-file " . "$cf{'rc_hmac_b64_key'} $verbose_str"; our $default_client_gpg_args = "$default_client_args " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_client_key " . "--gpg-home-dir $gpg_client_home_dir"; our $default_client_gpg_args_no_homedir = "$default_client_args " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_client_key "; our $default_client_gpg_args_same_key_signer = "$default_client_args " . "--gpg-recipient-key $gpg_client_key " . "--gpg-signer-key $gpg_client_key "; our $default_client_gpg_args_same_key_recip = "$default_client_args " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_server_key "; our $default_client_gpg_args_no_get_key = "$default_client_args_no_get_key " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_client_key " . "--gpg-home-dir $gpg_client_home_dir"; our $default_client_gpg_args_no_pw = "$default_client_args_no_get_key " . "--gpg-no-signing-pw " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_client_key " . "--gpg-home-dir $gpg_client_home_dir_no_pw"; our $client_gpg_large_key_args_no_pw = "$default_client_args_no_get_key " . "--gpg-no-signing-pw " . "--gpg-recipient-key $gpg_server_key2 " . "--gpg-signer-key $gpg_client_large_key " . "--gpg-home-dir $gpg_client_4096_bit_key_no_pw"; our $client_gpg_subkey_args_no_pw = "$default_client_args_no_get_key " . "--gpg-no-signing-pw " . "--gpg-recipient-key $gpg_server_key2 " . "--gpg-signer-key $gpg_client_subkey " . "--gpg-home-dir $gpg_client_subkey_no_pw"; our $client_gpg_server_4096_args_no_pw = "$default_client_args_no_get_key " . "--gpg-no-signing-pw " . "--gpg-recipient-key $gpg_server_large_key " . "--gpg-signer-key $gpg_client_subkey " . "--gpg-home-dir $gpg_client_subkey_no_pw"; our $client_and_server_gpg_4096_args_no_pw = "$default_client_args_no_get_key " . "--gpg-no-signing-pw " . "--gpg-recipient-key $gpg_server_large_key " . "--gpg-signer-key $gpg_client_large_key " . "--gpg-home-dir $gpg_client_4096_bit_key_no_pw"; our $default_server_conf_args = "-c $cf{'def'} -a $cf{'def_access'} " . "-d $default_digest_file -p $default_pid_file"; our $default_server_hmac_conf_args = "-c $cf{'def'} -a $cf{'hmac_access'} " . "-d $default_digest_file -p $default_pid_file"; our $default_server_gpg_args = "$lib_view_str " . "$valgrind_str $fwknopdCmd -c $cf{'def'} " . "-a $cf{'gpg_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; our $default_server_gpg_args_no_pw = "$lib_view_str " . "$valgrind_str $fwknopdCmd -c $cf{'def'} " . "-a $cf{'gpg_no_pw_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; our $default_server_gpg_args_hmac = "$lib_view_str " . "$valgrind_str $fwknopdCmd -c $cf{'def'} " . "-a $cf{'gpg_hmac_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; our $invalid_gpg_exe_server_args = "$lib_view_str " . "$valgrind_str $fwknopdCmd -c $cf{'def'} " . "-a $cf{'gpg_invalid_exe_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; our $default_server_gpg_args_no_pw_hmac = "$lib_view_str " . "$valgrind_str $fwknopdCmd -c $cf{'def'} " . "-a $cf{'gpg_no_pw_hmac_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; ### point the compiled binaries at the local library path ### instead of any installed libfko instance $ENV{'LD_LIBRARY_PATH'} = $lib_dir; $ENV{'DYLD_LIBRARY_PATH'} = $lib_dir if $lib_view_cmd =~ /otool/; ### import the tests from the various tests/ files &import_test_files(); ### ### main array that defines the tests we will run ### my @tests = ( { 'category' => 'recompilation', 'detail' => 'recompile and look for compilation warnings', 'function' => \&compile_warnings, }, { 'category' => 'make distcheck', 'detail' => 'ensure proper distribution creation', 'function' => \&make_distcheck, }, { 'category' => 'Makefile.am', 'detail' => 'test suite conf/ files included', 'function' => \&test_suite_conf_files, }, { 'category' => 'max coverage', 'detail' => 'interact with terminal for pw - TYPE ANY KEY HERE:', 'cmdline' => "$fwknopCmd -A tcp/22 -a 1.1.1.1 -D $loopback_ip -v -v -v ", 'function' => \&use_terminal_run_client, }, @build_security_client, @build_security_server, @build_security_libfko, @preliminaries, @code_structure_errstr, @basic_operations, @cunit_tests, @rijndael, @rijndael_cmd_exec, @rijndael_hmac_cmd_exec, @rijndael_hmac_cmd_open_close, @rijndael_replay_attacks, @rijndael_backwards_compatibility, @rijndael_fuzzing, @rijndael_hmac, @rijndael_hmac_fuzzing, @fault_injection, @address_sanitizer, @afl_fuzzing, @os_compatibility, @perl_FKO_module, @python_fko, @gpg_no_pw, @gpg_no_pw_hmac, @gpg, @gpg_hmac, ); if ($enable_profile_coverage_check) { push @tests, { 'category' => 'profile coverage', 'detail' => 'gcov profile coverage', 'function' => \&profile_coverage }; } ### the configure args tests recompile fwknop, so only do this ### after the profile coverage stats have been created for the main ### test run push @tests, @configure_args; if ($enable_valgrind) { push @tests, { 'category' => 'valgrind output', 'subcategory' => 'flagged functions', 'detail' => '', 'function' => \&parse_valgrind_flagged_functions }; } if ($do_crash_check) { push @tests, { 'category' => 'Look for crashes', 'detail' => 'checking for segfault/core dump messages', 'function' => \&look_for_crashes }; } my %test_keys = ( 'category' => $REQUIRED, 'subcategory' => $OPTIONAL, 'detail' => $REQUIRED, 'function' => $REQUIRED, 'binary' => $OPTIONAL, 'multi_cmds' => $OPTIONAL, 'cmdline' => $OPTIONAL, 'fwknopd_cmdline' => $OPTIONAL, 'fatal' => $OPTIONAL_NUMERIC, 'key_file' => $OPTIONAL, 'exec_err' => $OPTIONAL, 'server_exec_err' => $OPTIONAL, 'fw_rule_created' => $OPTIONAL, 'fw_rule_removed' => $OPTIONAL, 'sudo_test' => $OPTIONAL, 'sudo_conf' => $OPTIONAL, 'sudo_exec_user' => $OPTIONAL, 'sudo_exec_group' => $OPTIONAL, 'exec_user' => $OPTIONAL, 'server_conf' => $OPTIONAL, 'client_only' => $OPTIONAL_NUMERIC, 'server_only' => $OPTIONAL_NUMERIC, 'pkt' => $OPTIONAL, 'spa_pkts_file' => $OPTIONAL, 'fuzzing_pkt' => $OPTIONAL, 'pkt_prefix' => $OPTIONAL, 'no_ip_check' => $OPTIONAL, 'get_key' => $OPTIONAL, 'get_hmac_key' => $OPTIONAL, 'set_legacy_iv' => $OPTIONAL, 'sleep_cycles' => $OPTIONAL_NUMERIC, 'write_rc_file' => $OPTIONAL, 'save_rc_stanza' => $OPTIONAL, 'client_pkt_tries' => $OPTIONAL_NUMERIC, 'max_pkt_tries' => $OPTIONAL_NUMERIC, 'client_popen' => $OPTIONAL, 'disable_valgrind' => $OPTIONAL, 'wrapper_compile' => $OPTIONAL, 'wrapper_script' => $OPTIONAL, 'wrapper_binary' => $OPTIONAL, 'fiu_run' => $OPTIONAL_NUMERIC, 'fiu_injection_style' => $OPTIONAL, 'fiu_iterations' => $OPTIONAL_NUMERIC, 'server_access_file' => $OPTIONAL, 'server_include_keys_access_file' => $OPTIONAL, 'server_conf_file' => $OPTIONAL, 'digest_cache_file' => $OPTIONAL, 'cmd_cycle_open_file' => $OPTIONAL, 'cmd_cycle_close_file' => $OPTIONAL, 'cmd_exec_file_owner' => $OPTIONAL, 'cmd_exec_file_not_created' => $OPTIONAL, 'user_group_mismatch' => $OPTIONAL, 'sudo_user_group_mismatch' => $OPTIONAL, 'rm_rule_mid_cycle' => $OPTIONAL, 'server_receive_re' => $OPTIONAL, 'no_exit_intf_down' => $OPTIONAL, 'positive_output_matches' => $OPTIONAL, 'negative_output_matches' => $OPTIONAL, 'client_and_server_mode' => $OPTIONAL_NUMERIC, 'insert_rule_before_exec' => $OPTIONAL, 'insert_rule_while_running' => $OPTIONAL, 'insert_duplicate_rule_while_running' => $OPTIONAL, 'fw_dupe_rule_args' => $OPTIONAL, 'expect_server_stopped' => $OPTIONAL, 'ignore_client_error' => $OPTIONAL, 'weak_server_receive_check' => $OPTIONAL, 'search_for_rule_after_exit' => $OPTIONAL, 'rc_positive_output_matches' => $OPTIONAL, 'rc_negative_output_matches' => $OPTIONAL, 'mv_and_restore_replay_cache' => $OPTIONAL, 'relax_receive_cycle_num_check' => $OPTIONAL, 'client_positive_output_matches' => $OPTIONAL, 'client_negative_output_matches' => $OPTIONAL, 'server_positive_output_matches' => $OPTIONAL, 'server_positive_num_matches' => $OPTIONAL, 'server_negative_output_matches' => $OPTIONAL, 'server_negative_num_matches' => $OPTIONAL, 'client_cycles_per_server_instance' => $OPTIONAL_NUMERIC, 'iptables_rm_chains_after_server_start' => $OPTIONAL, ); &validate_test_hashes(); ### make sure no fwknopd instance is currently running die "[*] Please stop the running fwknopd instance." if &global_fwknopd_pgrep_check(); if ($rerun_failed_mode) { my $test_num = 0; my $test_category = ""; my $test_subcategory = ""; my $test_detail = ""; &logr("\nRunning previously failed tests\n"); for my $line (@last_logfile) { if ($line =~ /fail \((\d+)\)/ || $line =~ /valgrind output/ || $line =~ /Look for crashes/ || $line =~ /profile coverage/ ) { $test_num = $line =~ /\((\d+)\)\n/; $test_num = $1; $executed = $test_num - 1; if( $line =~ /\[(.+)\]\s+\[(.+)\]\s+([\w \(\)\/\-\>]+)/) { $test_category = $1; $test_subcategory = $2; $test_detail = $3; } elsif ($line =~ /^\[(.+)\]\s+\[(.+)\]/) { $test_category = $1; $test_subcategory = $2; } elsif ($line =~ /^\[(.+)\]/) { $test_category = $1; } for my $test_hr (@tests) { if ($test_hr->{'category'} eq $test_category && $test_hr->{'detail'} eq $test_detail) { &run_test($test_hr); } elsif ($test_hr->{'category'} eq "valgrind output" && $test_category eq "valgrind output") { &run_test($test_hr); if ($line =~ /pass \(\d+\)/) { $passed--; } } elsif ($test_hr->{'category'} eq "profile coverage" && $test_category eq "profile coverage") { &run_test($test_hr); if ($line =~ /pass \(\d+\)/) { $passed--; } } elsif ($test_hr->{'category'} eq "Look for crashes" && $test_category eq "Look for crashes") { &run_test($test_hr); if ($line =~ /pass \(\d+\)/) { $passed--; } } } } elsif ($line =~ /Run time: ([\d\.]+) minutes/) { my $total_elapsed_seconds = time() - $start_time; my $total_elapsed_minutes = sprintf "%.2f", ($total_elapsed_seconds / 60); $total_elapsed_minutes = $total_elapsed_minutes + $1; &logr(" Run time: $total_elapsed_minutes minutes\n"); } elsif ($line =~ /Run time: ([\d\.]+) seconds/) { my $total_elapsed_seconds = time() - $start_time + $1; if ($total_elapsed_seconds > 60) { my $total_elapsed_minutes = sprintf "%.2f", ($total_elapsed_seconds / 60); &logr(" Run time: $total_elapsed_minutes minutes\n"); } else { &logr(" Run time: $total_elapsed_seconds seconds\n"); } # } elsif ($line =~ /OpenSSL tests passed/) { # } elsif ($line =~ /OpenSSL HMAC tests passed/) { # } elsif ($line =~ /Fuzzing tests passed/) { } elsif ($line =~ /(\d+)\/(\d+)\/(\d+) test buckets passed/) { $passed = $1 + $passed; $executed = $3; &logr("[+] $passed/$failed/$executed test buckets passed/failed/executed\n"); } else { &logr($line); } } exit 0; } ### now that we're ready to run, preserve any previous test ### suite output &preserve_previous_test_run_results(); &logr("\n[+] Starting the fwknop test suite...\n\n" . " args: @args_cp\n\n" ); ### save the results from any previous test suite run ### so that we can potentially compare them with --diff if ($saved_last_results) { &logr(" Saved results from previous run " . "to: ${output_dir}.last/\n\n"); } unless ($list_mode) { copy $init_file, "$output_dir/init" if -e $init_file; } if ($enable_valgrind) { if ($previous_valgrind_coverage_dir) { die "[*] $previous_valgrind_coverage_dir does not exist" unless -d $previous_valgrind_coverage_dir; if (-d "${previous_valgrind_coverage_dir}/valgrind-coverage") { $previous_valgrind_coverage_dir .= '/valgrind-coverage'; } } else { ### try the previous output.last/valgrind-coverage dir first $previous_valgrind_coverage_dir = "${output_dir}.last/$valgrind_cov_dir"; unless (-d $previous_valgrind_coverage_dir) { my $os = 'linux'; $os = 'freebsd' if $platform == $FREEBSD; $previous_valgrind_coverage_dir = "valgrind-coverage/$os"; } } if (-d $previous_valgrind_coverage_dir) { &logr(" Valgrind mode enabled, will import previous coverage from:\n" . " $previous_valgrind_coverage_dir/\n\n"); } } ### print a summary of how many test buckets will be run my $test_buckets = 0; for my $test_hr (@tests) { next unless &process_include_exclude(&get_msg($test_hr)); $test_buckets++; if ($test_limit > 0) { last if $test_buckets >= $test_limit; } } &logr("[+] Total test buckets to execute: $test_buckets\n\n"); ### main loop through all of the tests my $run_flag = 1; for my $test_hr (@tests) { if ($run_flag or $test_hr->{'category'} eq 'valgrind output' or $test_hr->{'category'} eq 'Look for crashes' or $test_hr->{'category'} eq 'profile coverage') { &run_test($test_hr); } if ($test_limit > 0) { $run_flag = 0 if $executed >= $test_limit; } } &logr("\n"); unless ($list_mode) { &remove_permissions_warnings() unless $include_permissions_warnings; &restore_dir($gpg_dirs_tar); &restore_dir($access_include_dirs_tar); } my $total_elapsed_seconds = time() - $start_time; my $total_elapsed_minutes = sprintf "%.2f", ($total_elapsed_seconds / 60); if ($total_elapsed_seconds > 60) { &logr(" Run time: $total_elapsed_minutes minutes\n"); } else { &logr(" Run time: $total_elapsed_seconds seconds\n"); } if (@tests_to_include and keys %include_tracking) { my $tot_included = 0; for my $re (keys %include_tracking) { $tot_included += keys %{$include_tracking{$re}}; for my $test (keys %{$include_tracking{$re}}) { &write_test_file("$re: $test\n", "$output_dir/tests.included"); } } &logr(" Tests included (see $output_dir/tests.included): " . $tot_included . "\n"); } if (@tests_to_exclude and keys %exclude_tracking) { my $tot_excluded = 0; for my $re (keys %exclude_tracking) { $tot_excluded += keys %{$exclude_tracking{$re}}; for my $test (keys %{$exclude_tracking{$re}}) { &write_test_file("$re: $test\n", "$output_dir/tests.excluded"); } } &logr(" Tests excluded (see $output_dir/tests.excluded): " . $tot_excluded . "\n"); } &logr("\n"); if ($enable_openssl_compatibility_tests) { &logr("[+] $openssl_success_ctr/$openssl_failure_ctr/$openssl_ctr " . "OpenSSL tests passed/failed/executed\n"); &logr("[+] $openssl_hmac_success_ctr/$openssl_hmac_failure_ctr/$openssl_hmac_ctr " . "OpenSSL HMAC tests passed/failed/executed\n"); } if ($fuzzing_ctr > 0) { &logr("[+] $fuzzing_success_ctr/$fuzzing_failure_ctr/$fuzzing_ctr " . "Fuzzing tests passed/failed/executed\n"); } &logr("[+] $passed/$failed/$executed test buckets passed/failed/executed\n\n"); unless ($list_mode) { copy $logfile, "$output_dir/$logfile" or die $!; } if ($pinentry_fail) { if ($killall_path) { ### kill all gpg processes in the fwknop client ### process group (this will kill the test suite ### too, but we're already done) system "$killall_path -g fwknop"; } } exit 0; #===================== end main ======================= sub run_test() { my $test_hr = shift; &validate_test_hash($test_hr); ### prepare for test run &rm_tmp_files(); my $msg = &get_msg($test_hr); $msg =~ s/REPLPKTS/-->$total_fuzzing_pkts<-- pkts/; if ($client_only_mode) { return unless $test_hr->{'client_only'} or $test_hr->{'subcategory'} eq 'client' or $test_hr->{'category'} eq 'perl FKO module' or $test_hr->{'category'} eq 'python fko extension'; return if $msg =~ /server/i; } elsif ($server_only_mode) { return unless $test_hr->{'server_only'} or $test_hr->{'subcategory'} eq 'server' or $test_hr->{'category'} eq 'perl FKO module' or $test_hr->{'category'} eq 'python fko extension'; return if $msg =~ /client/i; } if ($list_mode) { if (&process_include_exclude($msg)) { print $msg, "\n"; } else { print "$msg (requires an --enable-* arg, see -h)\n"; } return; } return unless &process_include_exclude($msg); &dots_print($msg); $executed++; $curr_test_file = "$output_dir/$executed.test"; $server_test_file = "$output_dir/${executed}_fwknopd.test"; &write_test_file("[+] TEST: $msg\n", $curr_test_file); $test_hr->{'msg'} = $msg; if ($test_hr->{'mv_and_restore_replay_cache'}) { unlink "${default_digest_file}.mv" if -e "${default_digest_file}.mv"; move $default_digest_file, "${default_digest_file}.mv"; } my $rv = &{$test_hr->{'function'}}($test_hr); ### if we're in valgrind mode, make sure there were no memory leaks if ($enable_valgrind) { for my $file ($curr_test_file, $server_test_file) { next unless -e $file; if ($rv) { &write_test_file("[+] VERDICT: pass ($executed)\n", $file); } else { &write_test_file("[-] VERDICT: fail ($executed)\n", $file); } if (&file_find_regex([qr/^==\d+==\sHEAP\sSUMMARY/], $MATCH_ALL, $NO_APPEND_RESULTS, $file)) { unless (&valgrind_results($file)) { &write_test_file("[-] valgrind criteria failed, setting rv=0.\n", $file); $rv = 0; } } } } if ($rv) { &logr("pass ($executed)\n"); $passed++; } else { &logr("fail ($executed)\n"); $failed++; if ($test_hr->{'fatal'} eq $YES) { die "[*] required test failed, exiting."; } } if ($test_hr->{'mv_and_restore_replay_cache'}) { unlink $default_digest_file if -e $default_digest_file; move "${default_digest_file}.mv", $default_digest_file; } if ($enable_valgrind and &is_valgrind_running()) { if ($pkill_path) { for my $cmd ('memcheck', 'valgrind') { system "$pkill_path -f $cmd"; } } elsif ($killall_path) { for my $cmd ('memcheck', 'valgrind') { system "$killall_path -g -r $cmd > /dev/null 2>&1"; } } } if ($enable_perl_module_fuzzing_spa_pkt_generation) { if ($msg =~ /perl FKO module.*FUZZING/) { print "\n[+] Wrote $fuzzing_num_pkts fuzzing SPA ", "packets to $fuzzing_pkts_file.tmp...\n\n"; exit 0; } } if ($enable_profile_coverage_check) { if ($username) { for my $extension ('*.gcno', '*.gcda', '*.gcov') { system qq/find .. -name $extension | xargs -r chown $username/; } } for my $extension ('*.gcno', '*.gcda', '*.gcov') { system qq/find .. -name $extension | xargs -r chmod a+w/; } } ### clean up tmp files now that the test is complete &rm_tmp_files(); return; } sub precise_sleep { my ($sleeptime) = @_; select(undef, undef, undef, $sleeptime); } sub get_msg() { my $test_hr = shift; my $msg = "[$test_hr->{'category'}]"; $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'}; $msg .= " $test_hr->{'detail'}"; return $msg; } sub process_include_exclude() { my $msg = shift; ### inclusions/exclusions if (@tests_to_include) { my $found = 0; for my $test (@tests_to_include) { if ($msg =~ $test or ($enable_valgrind and $msg =~ /valgrind\soutput/) or ($enable_profile_coverage_check and $msg =~ /profile\scoverage/) or ($msg =~ /segfault.*dump\smessages/) ) { $include_tracking{$test}{$msg} = ''; $found = 1; last; } } return 0 unless $found; } if (@tests_to_exclude) { my $found = 0; for my $test (@tests_to_exclude) { if ($msg =~ $test) { $exclude_tracking{$test}{$msg} = ''; $found = 1; last; } } return 0 if $found; } return 1; } sub gdb_test_cmd() { die "[*] previous test file: $gdb_test_file does not exist." unless -e $gdb_test_file; my $gdb_cmd = ''; open F, "< $gdb_test_file" or die "[*] Could not open $gdb_test_file: $!"; while () { if (/CMD\:\sLD_LIBRARY_PATH=(\S+).*\s($fwknopCmd\s.*)/ or /CMD\:\sLD_LIBRARY_PATH=(\S+).*\s($fwknopdCmd\s.*)/) { $gdb_cmd = "LD_LIBRARY_PATH=$1 gdb --args $2"; last; } } close F; print "\n[+] Running the following command under gdb: $gdb_cmd\n\n"; if ($gdb_cmd) { system $gdb_cmd; } else { die "[*] Could not extract fwknop/fwknopd command from $gdb_test_file"; } return 1; } sub diff_test_results() { $diff_dir1 = "${output_dir}.last" unless $diff_dir1; $diff_dir2 = $output_dir unless $diff_dir2; die "[*] Need results from a previous run before running --diff" unless -d $diff_dir2; die "[*] Current results set does not exist." unless -d $diff_dir1; my %curr_tests = (); my %prev_tests = (); ### Only diff results for matching tests (parse the logfile to see which ### test numbers match across the two test cycles). &build_results_hash(\%curr_tests, $diff_dir1); &build_results_hash(\%prev_tests, $diff_dir2); for my $test_msg (sort {$curr_tests{$a}{'num'} <=> $curr_tests{$b}{'num'}} keys %curr_tests) { my $curr_result = $curr_tests{$test_msg}{'pass_fail'}; my $curr_num = $curr_tests{$test_msg}{'num'}; if (defined $prev_tests{$test_msg}) { print "[+] Diff check: $test_msg\n"; my $prev_result = $prev_tests{$test_msg}{'pass_fail'}; my $prev_num = $prev_tests{$test_msg}{'num'}; if ($curr_result ne $prev_result) { print " ** Verdict diff: current: $curr_result, ", "previous: $prev_result $test_msg\n"; } &diff_results($prev_num, $curr_num, $diff_dir1, $diff_dir2); print "\n"; } } if (-d "$diff_dir1/$valgrind_cov_dir" and -d "$diff_dir2/$valgrind_cov_dir") { &diff_valgrind_results(\%curr_tests, \%prev_tests) } exit 0; } sub diff_valgrind_results() { my ($curr_tests_hr, $prev_tests_hr) = @_; print "\n\n\n[+] Valgrind differences:\n\n"; for my $test_msg (sort {$curr_tests_hr->{$a}->{'num'} <=> $curr_tests_hr->{$b}->{'num'}} keys %$curr_tests_hr) { my $curr_num = $curr_tests_hr->{$test_msg}->{'num'}; if (defined $prev_tests_hr->{$test_msg}) { print "[+] Valgrind diff check: $test_msg\n"; my $prev_result = $prev_tests_hr->{$test_msg}->{'pass_fail'}; my $prev_num = $prev_tests_hr->{$test_msg}->{'num'}; &diff_results($prev_num, $curr_num, "$diff_dir1/$valgrind_cov_dir", "$diff_dir2/$valgrind_cov_dir"); print "\n"; } } return; } sub diff_results() { my ($prev_num, $curr_num, $dir1, $dir2) = @_; ### edit out any valgrind "==354==" prefixes my $valgrind_search_re = qr/^==\d+==\s/; ### remove CMD timestamps my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/; for my $file ("$dir1/${prev_num}.test", "$dir1/${prev_num}_fwknopd.test", "$dir2/${curr_num}.test", "$dir2/${curr_num}_fwknopd.test", ) { system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file; system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file; } if (-e "$dir1/${prev_num}.test" and -e "$dir2/${curr_num}.test") { system "diff -u $dir1/${prev_num}.test " . "$dir2/${curr_num}.test"; } if (-e "$dir1/${prev_num}_fwknopd.test" and -e "$dir2/${curr_num}_fwknopd.test") { system "diff -u $dir1/${prev_num}_fwknopd.test " . "$dir2/${curr_num}_fwknopd.test"; } return; } sub build_results_hash() { my ($hr, $dir) = @_; open F, "< $dir/$logfile" or die "[*] Could not open $dir/$logfile: $!"; while () { if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) { $hr->{$1}{'pass_fail'} = $2; $hr->{$1}{'num'} = $3; } } return; } sub asan_verification() { my $test_hr = shift; my $rv = 1; chdir $asan_dir or die $!; unless (&run_cmd('make clean', "../$cmd_out_tmp", "../$curr_test_file")) { chdir '..' or die $!; return 0; } if ($sudo_path) { unless (&run_cmd("$sudo_path -u $username make", "../$cmd_out_tmp", "../$curr_test_file")) { unless (&run_cmd('make', "../$cmd_out_tmp", "../$curr_test_file")) { $rv = 0; } } } else { unless (&run_cmd('make', "../$cmd_out_tmp", "../$curr_test_file")) { $rv = 0; } } if ($rv) { &run_cmd('./a.out', "../$cmd_out_tmp", "../$curr_test_file"); $rv = 0 unless &is_sanitizer_crash("../$curr_test_file"); } chdir '..' or die $!; return $rv; } sub compile_warnings() { my $curr_pwd = cwd() or die $!; chdir '..' or die $!; &config_recompile(''); ### look for compilation warnings - something like: ### warning: ‘test’ is used uninitialized in this function if (&file_find_regex([qr/\swarning:\s/i, qr/gcc\:.*\sunused/], $MATCH_ANY, $APPEND_RESULTS, "test/$curr_test_file")) { chdir $curr_pwd or die $!; return 0; } chdir $curr_pwd or die $!; ### the new binaries should exist unless (-e $fwknopCmd and -x $fwknopCmd) { &write_test_file("[-] $fwknopCmd does not exist or not executable.\n", $curr_test_file); } unless (-e $fwknopdCmd and -x $fwknopdCmd) { &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n", $curr_test_file); } return 1; } sub profile_coverage() { my $rv = 1; ### check for any *.gcno files - if they don't exist, then fwknop was ### not compiled with profile support unless (glob('../client/*.gcno') and glob('../server/*.gcno')) { &write_test_file("[-] ../client/*.gcno and " . "../server/*.gcno files do not exist.\n", $curr_test_file); return 0; } my $curr_dir = getcwd() or die $!; unless ($lcov_path) { &write_test_file( "[-] lcov command not found, skipping code coverage report generation.", $curr_test_file); return 0; } &run_cmd($profile_gen_report_sh, $cmd_out_tmp, $curr_test_file); if (-d $lcov_results_dir) { move $lcov_results_dir, "$output_dir/$lcov_results_dir"; for my $f ('lcov_coverage.info', 'lcov_coverage_final.info') { move $f, "$output_dir/$f" if -e $f; } } else { &write_test_file("[-] $lcov_results_dir does not exist.", $cmd_out_tmp, $curr_test_file); $rv = 0; } if (-d "${output_dir}.last") { &run_cmd("./$coverage_diff_path", $cmd_out_tmp, $curr_test_file); } if ($username) { for my $extension ('*.gcno', '*.gcda', '*.gcov') { system qq/find .. -name $extension | xargs -r chown $username/; } } for my $extension ('*.gcno', '*.gcda', '*.gcov') { system qq/find .. -name $extension | xargs -r chmod a+w/; } return $rv; } sub fiu_run_fault_injection() { my $test_hr = shift; my $rv = 1; my $iterations = $test_hr->{'fiu_iterations'}; $iterations = 1 if $iterations < 1; ### assume we want at least 1 for (my $i=0; $i < $iterations; $i++) { &run_cmd("$lib_view_str $fiu_run_path -x " . "-c '$test_hr->{'fiu_injection_style'}' $test_hr->{'cmdline'}", $cmd_out_tmp, $curr_test_file); } return $rv; } sub fault_injection_tag() { my $test_hr = shift; my $rv = 1; my $server_was_stopped = 0; my $fw_rule_created = 0; my $fw_rule_removed = 0; my $tag_name = ''; if ($test_hr->{'cmdline'}) { if ($test_hr->{'cmdline'} =~ /fault\-injection\-tag\s(S+)/) { $tag_name = $1; } } elsif ($test_hr->{'fwknopd_cmdline'}) { if ($test_hr->{'fwknopd_cmdline'} =~ /fault\-injection\-tag\s(S+)/) { $tag_name = $1; } } if ($tag_name) { unless ($test_hr->{'detail'} =~ /\s$tag_name/) { &write_test_file( "[-] tag_name '$tag_name' not in test message.\n", $curr_test_file); return 0; } } if ($test_hr->{'pkt'} or ($test_hr->{'cmdline'} and $test_hr->{'fwknopd_cmdline'})) { ### we are testing the fwknopd server if ($test_hr->{'pkt'}) { my @packets = ( { 'proto' => 'udp', 'port' => $default_spa_port, 'dst_ip' => $loopback_ip, 'data' => $test_hr->{'pkt'}, }, ); ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); } else { ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) = &client_server_interaction($test_hr, [], $USE_CLIENT); } $rv = 0 unless $server_was_stopped; if ($test_hr->{'server_positive_output_matches'}) { unless (&file_find_regex( $test_hr->{'server_positive_output_matches'}, $MATCH_ALL, $APPEND_RESULTS, $server_test_file)) { &write_test_file( "[-] server_positive_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } } else { ### we are testing the fwknop client, server, or other command ### and expect an error $rv = not &run_cmd($test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); if ($test_hr->{'positive_output_matches'}) { unless (&file_find_regex( $test_hr->{'positive_output_matches'}, $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] positive_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } } return $rv; } sub fko_wrapper_exec() { my $test_hr = shift; my $make_arg = $test_hr->{'wrapper_compile'}; $make_arg = 'asan' if $asan_mode; $make_arg = 'ubsan' if $ubsan_mode; if ($test_hr->{'wrapper_binary'} =~ m|/fko_wrapper$|) { if ($enable_fuzzing_interfaces_tests) { $make_arg = 'fuzzing'; ### generate the fko-wrapper/fuzz_spa_payloads file ### if necessary - it is consumed by the wrapper in ### -DFUZZING_INTERFACES mode &write_test_file("[-] Generating SPA fuzzing packets " . "file: $fuzz_spa_payloads_file with ./spa_fuzzing.py...\n", $curr_test_file); unless (-e $fuzz_spa_payloads_file) { system "./spa_fuzzing.py > $fuzz_spa_payloads_file"; } } } my $rv = &compile_wrapper($make_arg); if ($rv) { chdir $fko_wrapper_dir or die $!; my $iterations = $test_hr->{'fiu_iterations'}; $iterations = 1 if $iterations < 1; ### assume we want at least 1 if ($test_hr->{'fiu_run'} == $YES) { my $lib_path = $lib_view_str; $lib_path =~ s|_PATH=|_PATH=../|; ### hack for (my $i=0; $i < $iterations; $i++) { &run_cmd("$lib_path $fiu_run_path -x " . "-c '$test_hr->{'fiu_injection_style'}' $test_hr->{'wrapper_binary'}", "../$cmd_out_tmp", "../$curr_test_file"); } } else { &run_cmd("./$test_hr->{'wrapper_script'} $test_hr->{'wrapper_binary'}", "../$cmd_out_tmp", "../$curr_test_file"); if ($test_hr->{'wrapper_script'} =~ /valgrind/) { $rv = 0 unless &valgrind_results("../$curr_test_file"); } } chdir '..' or die $!; if ($test_hr->{'wrapper_binary'} =~ m|/fko_wrapper$|) { if ($enable_fuzzing_interfaces_tests) { ### make sure the send_spa_payloads file exists unless (-e $send_fuzz_payloads_file) { &write_test_file("[-] Generating SPA fuzzing packets " . "file: $send_fuzz_payloads_file for fwknopd consumption...\n", $curr_test_file); unless (-e $send_fuzz_payloads_file) { system "grep PKT_ID $curr_test_file > $send_fuzz_payloads_file"; } } } } $rv = 0 if &is_crash($curr_test_file); } else { ### could not compile, so disable remaining fault injection ### "tag" tests push @tests_to_exclude, qr/fault\sinjection.*\stag\s/; } return $rv; } sub valgrind_results() { my $file = shift; my $rv = 1; $rv = 0 if &file_find_regex( [qr/definitely\slost\:\s[1-9]\d*\sbytes/], $MATCH_ALL, $APPEND_RESULTS, $file); $rv = 0 if &file_find_regex( [qr/indirectly\slost\:\s[1-9]\d*\sbytes/], $MATCH_ALL, $APPEND_RESULTS, $file); return $rv; } sub test_suite_conf_files() { my $make_file = '../Makefile.am'; my $rv = 1; my %makefile_conf_files = (); my %makefile_test_scripts = (); unless (-e $make_file) { &write_test_file("[-] $make_file does not exist.\n", $curr_test_file); return 0; } open F, "< $make_file" or die $!; while () { if (m|test/$conf_dir/(\S+)|) { $makefile_conf_files{$1} = ''; } elsif (m|test/$tests_dir/(\S+)|) { $makefile_test_scripts{$1} = ''; } } close F; for my $f (glob("$conf_dir/*")) { next if -d $f; next unless $f =~ /\.conf/ or $f =~ /fwknop/; if ($f =~ m|$conf_dir/(\S+)|) { unless (defined $makefile_conf_files{$1}) { &write_test_file("[-] test suite conf file $1 not in $make_file.\n", $curr_test_file); $rv = 0; } } } for my $f (glob("$tests_dir/*.pl")) { if ($f =~ m|$tests_dir/(\S+)|) { unless (defined $makefile_test_scripts{$1}) { &write_test_file("[-] test suite script file $1 not in $make_file.\n", $curr_test_file); $rv = 0; } } } return $rv; } sub look_for_crashes() { my $rv = 1; for my $f (glob("$output_dir/*")) { next if -d $f; next unless $f =~ /\.test$/; ### only look for ASAN crashes in normal test files next if &file_find_regex([qr/ASAN.*crash\sverification/i], $MATCH_ALL, $NO_APPEND_RESULTS, $f); $rv = 0 if &is_crash($f); } $do_crash_check = 0; return $rv; } sub is_sanitizer_crash() { my $file = shift; my $rv = 0; if (&file_find_regex([qr/ERROR\:\s\w+Sanitizer/, qr/SUMMARY\:\s\w+Sanitizer/], $MATCH_ANY, $NO_APPEND_RESULTS, $file)) { &write_test_file("[-] Sanitizer crash found in: $file\n", $curr_test_file); $rv = 1; } return $rv; } sub is_crash() { my $file = shift; my $rv = 0; if (&file_find_regex([qr/segmentation\sfault/i, qr/core\sdumped/i], $MATCH_ANY, $NO_APPEND_RESULTS, $file)) { &write_test_file("[-] crash message found in: $file\n", $curr_test_file); $rv = 1; } $rv = 1 if &is_sanitizer_crash($file); ### ASan and valgrind don't appear to be compatible, and and ASan ### will throw an error when the two are mixed if (&file_find_regex([qr/Shadow memory range interleaves/], $MATCH_ANY, $NO_APPEND_RESULTS, $file)) { &write_test_file("[-] Sanitizer infrastructure not " . "compatible with valgrind: $file\n", $curr_test_file); $rv = 1; } return $rv; } sub config_recompile() { my $config_cmd = shift; my $rv = 1; &run_cmd('make clean', $cmd_out_tmp, "test/$curr_test_file"); if ($config_cmd) { &run_cmd($config_cmd, $cmd_out_tmp, "test/$curr_test_file"); } if ($sudo_path) { unless (&run_cmd("$sudo_path -u $username make", $cmd_out_tmp, "test/$curr_test_file")) { $rv = 0 unless &run_cmd('make', $cmd_out_tmp, "test/$curr_test_file"); } } else { $rv = 0 unless &run_cmd('make', $cmd_out_tmp, "test/$curr_test_file"); } unless ($rv) { ### override the failure if makeinfo is missing since this ### this doesn't affect run time operations if (&file_find_regex([qr/makeinfo.*is\smissing/], $MATCH_ALL, $NO_APPEND_RESULTS, $cmd_out_tmp)) { $rv = 1; } } return 1; } sub configure_args_restore_orig() { my $rv = 1; my $curr_pwd = cwd() or die $!; chdir '..' or die $!; unless (&config_recompile($orig_config_args)) { &write_test_file("[-] configure/recompile failure.\n", "test/$curr_test_file"); chdir $curr_pwd or die $!; $rv = 0; } chdir $curr_pwd or die $!; return $rv; } sub configure_args_disable_execvpe() { my $rv = 1; my $curr_pwd = cwd() or die $!; chdir '..' or die $!; unless (&config_recompile('./extras/apparmor/configure_args.sh --disable-execvpe')) { &write_test_file("[-] configure/recompile failure.\n", "test/$curr_test_file"); chdir $curr_pwd or die $!; $rv = 0; } chdir $curr_pwd or die $!; return $rv; } sub configure_args_udp_server_no_libpcap() { my $rv = 1; my $curr_pwd = cwd() or die $!; chdir '..' or die $!; unless (&config_recompile('./extras/apparmor/configure_args.sh --enable-udp-server')) { &write_test_file("[-] configure/recompile failure.\n", "test/$curr_test_file"); $rv = 0; } chdir $curr_pwd or die $!; &run_cmd("$lib_view_str $lib_view_cmd $fwknopdCmd", $cmd_out_tmp, $curr_test_file); if (&file_find_regex([qr/pcap/], $MATCH_ALL, $APPEND_RESULTS, $cmd_out_tmp)) { &write_test_file("[-] fwknopd appears to still link against libpcap.\n", $curr_test_file); $rv = 0; } return $rv; } sub code_structure_search_sources_for_non_ascii_chars() { my $rv = 1; for my $src_dir ('client', 'server', 'win32', 'common', 'lib') { next unless (glob("../$src_dir/*.c"))[0]; &run_cmd($perl_path . q{ -lwne 'print "non-ascii char in $ARGV" and exit 0 if /[^\w\s\x20-\x7e]/' } . "../$src_dir/*.c", $cmd_out_tmp, $curr_test_file); next unless (glob("../$src_dir/*.h"))[0]; &run_cmd($perl_path . q{ -lwne 'print "non-ascii char in $ARGV" and exit 0 if /[^\w\s\x20-\x7e]/' } . "../$src_dir/*.h", $cmd_out_tmp, $curr_test_file); } if (&file_find_regex( [qr/^non\-ascii/], $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] non-ascii char found in source file, setting rv=0\n", $curr_test_file); $rv = 0; } return $rv; } sub code_structure_fko_error_strings() { my $rv = 1; ### parse error codes from lib/fko.h and make sure each is handled in ### fko_errstr(), and that both the perl and python libfko extensions also ### handle each error code. for my $file ($libfko_hdr_file, $libfko_errstr_file, $perl_libfko_constants_file, $python_libfko_constants_file) { unless (-e $file) { &write_test_file("[-] file: $file does not exist.\n", $curr_test_file); return 0; } } ### this is a basic parser that relies on the current structure of fko.h my $found_starting_code = 0; my @fko_error_codes = (); my $starting_code = 'FKO_SUCCESS'; open F, "< $libfko_hdr_file" or die "[*] Could not open $libfko_hdr_file: $!"; while () { if (/$starting_code\s=\s0/) { $found_starting_code = 1; push @fko_error_codes, $starting_code; next; } next unless $found_starting_code; if (/^\s{4}([A-Z]\S+),/) { push @fko_error_codes, $1; } last if $found_starting_code and /^\}\sfko_error_codes_t\;/; } close F; ### now make sure that lib/fko_error.c has an error string for each code ### in order my $found_errstr_func = 0; my $expected_var_index = 0; my $prev_var = $fko_error_codes[0]; open F, "< $libfko_errstr_file" or die "[*] Could not open $libfko_errstr_file: $!"; while () { if (/^fko_errstr\(/) { $found_errstr_func = 1; next; } next unless $found_errstr_func; if (/^\s+case\s(\S+)\:/) { my $var_str = $1; if ($fko_error_codes[$expected_var_index] eq 'GPGME_ERR_START') { $expected_var_index++; } if ($fko_error_codes[$expected_var_index] eq $var_str) { $expected_var_index++; $prev_var = $var_str; } else { &write_test_file("[-] $libfko_errstr_file: expected var $fko_error_codes[$expected_var_index] " . "in position: $expected_var_index in fko_errstr(), previous var: $prev_var\n", $curr_test_file); $rv = 0; last; } } last if $found_errstr_func and /^\}/; } close F; ### validate perl error code constants $expected_var_index = 0; $prev_var = $fko_error_codes[0]; my $found_err_code_arr = 0; open F, "< $perl_libfko_constants_file" or die "[*] Could not open $perl_libfko_constants_file: $!"; while () { if (/our\s\@ERROR_CODES\s=/) { $found_err_code_arr = 1; next; } next unless $found_err_code_arr; if (/^\s{4}(\S+)/) { my $var_str = $1; if ($fko_error_codes[$expected_var_index] eq $var_str) { $expected_var_index++; $prev_var = $var_str; } else { &write_test_file("[-] $perl_libfko_constants_file: perl FKO module - " . "expected var $fko_error_codes[$expected_var_index] " . "at position: $expected_var_index in ERROR_CODES array, previous var: $prev_var\n", $curr_test_file); $rv = 0; last; } } last if $found_err_code_arr and /^\)\;/; } close F; ### same thing, but now validate 'use constant' values too $expected_var_index = 0; $prev_var = $fko_error_codes[0]; my $found_use_constant = 0; my $found_fko_success = 0; open F, "< $perl_libfko_constants_file" or die "[*] Could not open $perl_libfko_constants_file: $!"; while () { if (/^use\sconstant\s\{/) { $found_use_constant = 1; next; } next unless $found_use_constant; if (/^\s{4}$starting_code\s+=\>\s(\d+),/) { my $val = $1; unless ($fko_error_codes[$val] eq $starting_code) { &write_test_file("[-] $perl_libfko_constants_file: perl FKO module " . "- expected var $starting_code " . "value of zero, got $val\n", $curr_test_file); $rv = 0; last; } $found_fko_success = 1; } next unless $found_fko_success; if (/^\s{4}([A-Z]\S+)\s+=\>\s(\d+),/) { my $var_str = $1; my $val = $2; if ($fko_error_codes[$val] eq $var_str) { $expected_var_index++; $prev_var = $var_str; } else { &write_test_file("[-] $perl_libfko_constants_file: perl FKO module " . "- expected var $fko_error_codes[$expected_var_index] " . "in position: $expected_var_index in 'use constants' definition, previous var: $prev_var\n", $curr_test_file); $rv = 0; last; } } last if $found_fko_success and /^\)\;/; } close F; ### validate python error code constants $expected_var_index = 0; $prev_var = $fko_error_codes[0]; $found_use_constant = 0; $found_fko_success = 0; open F, "< $python_libfko_constants_file" or die "[*] Could not open $python_libfko_constants_file: $!"; while () { if (/^$starting_code\s=\s0/) { $found_fko_success = 1; next; } next unless $found_fko_success; if (/^([A-Z]\S+)\s=\s(\d+)/) { my $var_str = $1; my $val = $2; if ($fko_error_codes[$val] eq $var_str) { $expected_var_index++; $prev_var = $var_str; } else { &write_test_file("[-] python extension - expected var $fko_error_codes[$expected_var_index] " . "in position: $expected_var_index in FKO constants section, previous var: $prev_var\n", $curr_test_file); $rv = 0; last; } } last if $found_fko_success and /^\s/; } close F; return $rv; } sub make_distcheck() { ### 'make clean' as root return 0 unless &run_cmd('make -C .. distcheck', $cmd_out_tmp, $curr_test_file); return 1 if &file_find_regex([qr/archives\sready\sfor\sdistribution/], $MATCH_ALL, $APPEND_RESULTS, $curr_test_file); return 0; } sub binary_exists() { my $test_hr = shift; return 0 unless $test_hr->{'binary'}; ### account for different libfko.so paths (e.g. libfko.so.0.3 with no ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X) if ($test_hr->{'binary'} =~ /libfko/) { unless (-e $test_hr->{'binary'}) { my $file = "$lib_dir/libfko.dylib"; if (-e $file) { $test_hr->{'binary'} = $file; $libfko_bin = $file; } else { for my $f (glob("$lib_dir/libfko.so*")) { if (-e $f and -x $f) { $test_hr->{'binary'} = $f; $libfko_bin = $f; last; } } } } } return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'}; return 1; } sub expected_code_version() { my $test_hr = shift; unless (-e '../VERSION') { &write_test_file("[-] ../VERSION file does not exist.\n", $curr_test_file); return 0; } open F, '< ../VERSION' or die $!; my $line = ; close F; if ($line =~ /(\d.*\d)/) { my $version = $1; return 0 unless &run_cmd($test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); return 1 if &file_find_regex([qr/$version/], $MATCH_ALL, $APPEND_RESULTS, $curr_test_file); } return 0; } sub write_rc_file() { my ($rc_hr, $rc_file) = @_; open RC, "> $rc_file" or die "[*] Could not open $rc_file: $!"; for my $hr (@$rc_hr) { print RC "[$hr->{'name'}]\n"; for my $var (keys %{$hr->{'vars'}}) { print RC "$var $hr->{'vars'}->{$var}\n"; } } close RC; return; } sub cunit_tests() { my $test_hr = shift; my $rv = 1; &run_cmd("$lib_view_str $valgrind_str" . $test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); if ($test_hr->{'negative_output_matches'}) { $rv = 0 if &file_find_regex( $test_hr->{'negative_output_matches'}, $MATCH_ANY, $APPEND_RESULTS, $curr_test_file); } return $rv } sub server_start_stop_cycle() { my $test_hr = shift; my $rv = 1; &write_test_file("[+] TEST: " . &get_msg($test_hr) . "\n", $server_test_file); &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -S", $cmd_out_tmp, $curr_test_file); &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -K", $cmd_out_tmp, $curr_test_file); &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -R", $cmd_out_tmp, $curr_test_file); ### send additional signals for code coverage for my $sig_name (@sigs_ordered) { my $sig = $sigs{$sig_name}; &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args " . "-i $loopback_intf $verbose_str", $cmd_out_tmp, $curr_test_file); sleep 1; open F, "< $default_pid_file" or die "[*] Could not open $default_pid_file: $!"; my $pid = ; close F; chomp $pid; if (kill 0, $pid) { &write_test_file( "[+] Sending daemonized fwknopd PID: $pid signal: $sig_name($sig)\n", $curr_test_file); kill $sig, $pid; sleep 1; if (kill 0, $pid) { &run_cmd( "$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -K", $cmd_out_tmp, $curr_test_file); } } else { &write_test_file( "[-] Daemonized fwknopd PID: $pid not running?\n", $curr_test_file); } } &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -S", $cmd_out_tmp, $curr_test_file); &run_cmd("$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -K", $cmd_out_tmp, $curr_test_file); ### now send the signals against a non-daemon fwknopd process for my $sig_name (@sigs_ordered) { my $sig = $sigs{$sig_name}; &do_fwknopd_cmd("$lib_view_str $valgrind_str " . "$fwknopdCmd $default_server_conf_args -i $loopback_intf -f"); open F, "< $default_pid_file" or die "[*] Could not open $default_pid_file: $!"; my $pid = ; close F; chomp $pid; if (kill 0, $pid) { &write_test_file( "[+] Sending foreground fwknopd PID: $pid signal: $sig_name($sig)\n", $curr_test_file); kill $sig, $pid; sleep 1; if (kill 0, $pid) { &run_cmd( "$lib_view_str $valgrind_str $fwknopdCmd $default_server_conf_args -K", $cmd_out_tmp, $curr_test_file); } } else { &write_test_file( "[-] Foreground fwknopd PID: $pid not running?\n", $curr_test_file); } } return $rv; } sub server_conf_files() { my $test_hr = shift; my $rv = 1; if ($test_hr->{'digest_cache_file'}) { &write_server_conf_file($test_hr->{'digest_cache_file'}, $rewrite_digest_file); } if ($test_hr->{'server_access_file'}) { if ($test_hr->{'sudo_user_group_mismatch'} eq $YES) { push @{$test_hr->{'server_access_file'}}, "CMD_SUDO_EXEC_USER $username"; push @{$test_hr->{'server_access_file'}}, "CMD_SUDO_EXEC_GROUP root"; } if ($test_hr->{'user_group_mismatch'} eq $YES) { push @{$test_hr->{'server_access_file'}}, "CMD_EXEC_USER $username"; push @{$test_hr->{'server_access_file'}}, "CMD_EXEC_GROUP root"; } &write_server_conf_file($test_hr->{'server_access_file'}, $rewrite_access_conf); } if ($test_hr->{'server_include_keys_access_file'}) { &write_server_conf_file($test_hr->{'server_include_keys_access_file'}, $rewrite_include_keys_access_conf); } if ($test_hr->{'server_conf_file'}) { &write_server_conf_file($test_hr->{'server_conf_file'}, $rewrite_fwknopd_conf); } $rv = 0 unless &run_cmd($test_hr->{'fwknopd_cmdline'}, $cmd_out_tmp, $curr_test_file); if ($rv == 0) { $rv = 1 if $test_hr->{'exec_err'} eq $YES; } if ($test_hr->{'positive_output_matches'}) { unless (&file_find_regex( $test_hr->{'positive_output_matches'}, $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] positive_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } if ($test_hr->{'negative_output_matches'}) { if (&file_find_regex( $test_hr->{'negative_output_matches'}, $MATCH_ANY, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] negative_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } return $rv; } sub write_server_conf_file() { my ($lines_ar, $file) = @_; open F, "> $file" or die "[*] Could not open $file $!"; &write_test_file("[+] write_server_conf_file(): $file\n", $curr_test_file); for my $line (@$lines_ar) { &write_test_file("$line\n", $curr_test_file); print F $line, "\n"; } close F; return; } sub client_rc_file() { my $test_hr = shift; my $rv = 1; my $rc_file = ''; if ($test_hr->{'write_rc_file'}) { &write_rc_file($test_hr->{'write_rc_file'}, $rewrite_rc_file); $rc_file = $rewrite_rc_file; } elsif ($test_hr->{'save_rc_stanza'}) { &write_rc_file($test_hr->{'save_rc_stanza'}, $save_rc_file); $rc_file = $save_rc_file; } else { &write_test_file( "[-] test hash does not include 'write_rc_file' or 'save_rc_stanza'\n", $curr_test_file); return 0; } if ($test_hr->{'client_popen'}) { $rv = &popen_cmd($test_hr, $cmd_out_tmp, $curr_test_file); } else { $rv = 0 unless &run_cmd($test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); } if ($rv == 0) { $rv = 1 if $test_hr->{'exec_err'} eq $YES; } unless ($test_hr->{'cmdline'} =~ /key\-gen/ or $test_hr->{'cmdline'} =~ /\-k/ or $test_hr->{'exec_err'} eq $YES) { $rv = 0 unless &file_find_regex([qr/Final\sSPA\sData/i], $MATCH_ALL, $NO_APPEND_RESULTS, $curr_test_file); } if ($test_hr->{'positive_output_matches'}) { unless (&file_find_regex( $test_hr->{'positive_output_matches'}, $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] positive_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } if ($test_hr->{'negative_output_matches'}) { if (&file_find_regex( $test_hr->{'negative_output_matches'}, $MATCH_ANY, $APPEND_RESULTS, $curr_test_file)) { &write_test_file( "[-] negative_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } unless (&validate_fko_decode()) { &write_test_file( "[-] validate_fko_decode() returned zero, setting rv=0\n", $curr_test_file); $rv = 0; } if ($test_hr->{'rc_positive_output_matches'}) { unless (&file_find_regex( $test_hr->{'rc_positive_output_matches'}, $MATCH_ALL, $NO_APPEND_RESULTS, $rc_file)) { &write_test_file( "[-] rc_positive_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } if ($test_hr->{'rc_negative_output_matches'}) { if (&file_find_regex( $test_hr->{'rc_negative_output_matches'}, $MATCH_ANY, $NO_APPEND_RESULTS, $rc_file)) { &write_test_file( "[-] rc_negative_output_matches not met, setting rv=0\n", $curr_test_file); $rv = 0; } } return $rv; } sub validate_fko_decode() { return 0 unless -e $curr_test_file; ### make sure that the before and after FKO decode ### sections are the same - this ensures that libfko ### encoding / decoding cycles match up my @before_lines = (); my @after_lines = (); my $found_fko_field_values = 0; my $finished_first_section = 0; open F, "< $curr_test_file" or die "[*] Could not open $curr_test_file: $!"; while () { if (/^FKO\sField\sValues/) { $found_fko_field_values = 1; next; } next unless $found_fko_field_values; if (/Final\sSPA\sData/) { $found_fko_field_values = 0; last if $finished_first_section; $finished_first_section = 1; } if ($found_fko_field_values) { if ($finished_first_section) { push @after_lines, $_ if $_ =~ /\S/; } else { push @before_lines, $_ if $_ =~ /\S/; } } } close F; my $found_difference = 0; for (my $i=0; $i < $#before_lines; $i++) { unless (defined $after_lines[$i]) { $found_difference = 1; last; } if ($before_lines[$i] ne $after_lines[$i]) { chomp $before_lines[$i]; chomp $after_lines[$i]; &write_test_file( "[-] Line mismatch, before '$before_lines[$i]', after '$after_lines[$i]'\n", $curr_test_file); $found_difference = 1; } } if ($found_difference) { return 0; } return 1; } sub insert_dupe_rule() { my $test_hr = shift; ### insert duplicate rules my ($prv, $lib_path, $fwknopd_conf, $access_conf) = &parse_fwknopd_cmdline($test_hr); return unless $prv; &write_test_file("[+] Policy before inserting duplicate rules:\n", $curr_test_file); &run_cmd("LD_LIBRARY_PATH=$lib_path $fwknopdCmd -c " . "$fwknopd_conf -a $access_conf --fw-list", $cmd_out_tmp, $curr_test_file); my $time_now = time(); for (my $i=0; $i < 15; $i++) { my $time_prefix = '_exp_' . ($time_now + 45+$i); ### default timeout &write_test_file("[+] Inserting duplicate rule with expire comment: $time_prefix\n", $curr_test_file); if ($test_hr->{'fw_dupe_rule_args'}) { for my $fw_args (@{$test_hr->{'fw_dupe_rule_args'}}) { my $cp = $fw_args; if ($cp =~ /EXP_TIME/) { $cp =~ s/EXP_TIME/$time_prefix/; } &run_cmd("$fw_bin_and_prefix $cp", $cmd_out_tmp, $curr_test_file); } } else { ### assume SSH &run_cmd("$fw_bin_and_prefix -A FWKNOP_INPUT -p 6 -s $fake_ip -d 0.0.0.0/0 " . "--dport 22 -m comment --comment $time_prefix -j ACCEPT", $cmd_out_tmp, $curr_test_file); } } &write_test_file("[+] Policy after inserting duplicate rules:\n", $curr_test_file); &run_cmd("LD_LIBRARY_PATH=$lib_path $fwknopdCmd -c " . "$fwknopd_conf -a $access_conf --fw-list", $cmd_out_tmp, $curr_test_file); &cache_fw_policy($cmd_out_tmp); until($time_now + 44 < time()) { sleep 1; } return; } sub client_send_spa_packet() { my $test_hr = shift; my $client_cycles = 1; $client_cycles = $test_hr->{'client_cycles_per_server_instance'} if $test_hr->{'client_cycles_per_server_instance'} > 0; return &_client_send_spa_packet($test_hr, $client_cycles, $SERVER_RECEIVE_CHECK); } sub _client_send_spa_packet() { my ($test_hr, $cycle_ctr, $server_receive_check) = @_; my $rv = 1; if ($test_hr->{'get_key'}) { &write_key($test_hr->{'get_key'}->{'key'}, $test_hr->{'get_key'}->{'file'}); } else { &write_key($default_key, $local_key_file); } if ($test_hr->{'get_hmac_key'}) { &write_key($test_hr->{'get_hmac_key'}->{'key'}, $test_hr->{'get_hmac_key'}->{'file'}); } else { &write_key($default_key, $local_key_file); } if (-e $server_cmd_tmp) { my $tries = 0; my $quickcount = 0; my $nummatches = 0; for (;;) { $rv = 1; $tries++; ### default stanza regex match my $server_receive_re = qr/stanza\s.*\sSPA Packet from IP/; $server_receive_re = $test_hr->{'server_receive_re'} if $test_hr->{'server_receive_re'}; my $matches = &file_find_num_matches($server_receive_re, $NO_APPEND_RESULTS, $server_cmd_tmp); if ($test_hr->{'weak_server_receive_check'}) { last if $matches > 0; } else { last if $matches == $cycle_ctr+1; } &write_test_file("[.] client_send_spa_packet() " . "executing client and looking for fwknopd receiving " . "packet, try: $tries\n", $curr_test_file); ### run the client if ($test_hr->{'multi_cmds'}) { for my $cmd (@{$test_hr->{'multi_cmds'}}) { $rv = 0 unless &run_cmd($cmd, $cmd_out_tmp, $curr_test_file); } } else { $rv = 0 unless &run_cmd($test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); } $quickcount = 0; $nummatches = 0; $nummatches = &file_find_num_matches(qr/Final\sSPA\sData/, $NO_APPEND_RESULTS, $server_cmd_tmp); while ($nummatches < 1 && $quickcount < 10 ) { print "."; precise_sleep(.5); $quickcount++; $nummatches = &file_find_num_matches(qr/Final\sSPA\sData/, $NO_APPEND_RESULTS, $server_cmd_tmp); }; if ($test_hr->{'relax_receive_cycle_num_check'}) { $rv = 0 unless &file_find_regex([qr/Final\sSPA\sData/], $MATCH_ALL, $NO_APPEND_RESULTS, $curr_test_file); } else { $rv = 0 unless (&file_find_num_matches(qr/Final\sSPA\sData/, $NO_APPEND_RESULTS, $server_cmd_tmp) > 0) || (&file_find_num_matches(qr/SPA\sPacket\sfrom\sIP:/, $NO_APPEND_RESULTS, $server_cmd_tmp) > 0) || (&file_find_num_matches(qr/SPA\sdata:/, $NO_APPEND_RESULTS, $server_cmd_tmp) > 0); } last if $server_receive_check == $NO_SERVER_RECEIVE_CHECK; if ($test_hr->{'client_pkt_tries'} > 0) { last if $tries == $test_hr->{'client_pkt_tries'}; } else { last if $tries == 10; } sleep 1; } } else { &write_test_file("[.] client_send_spa_packet() " . "server tmp file $server_cmd_tmp does not exist.\n", $curr_test_file); ### run the client if ($test_hr->{'multi_cmds'}) { for my $cmd (@{$test_hr->{'multi_cmds'}}) { $rv = 0 unless &run_cmd($cmd, $cmd_out_tmp, $curr_test_file); } } else { $rv = 0 unless &run_cmd($test_hr->{'cmdline'}, $cmd_out_tmp, $curr_test_file); } $rv = 0 unless &file_find_regex([qr/Final\sSPA\sData/i], $MATCH_ALL, $NO_APPEND_RESULTS, $curr_test_file); } unless ($server_receive_check == $NO_SERVER_RECEIVE_CHECK) { if ($rv) { &write_test_file("[+] fwknopd received SPA packet.\n", $curr_test_file); } else { &write_test_file("[-] fwknopd did not receive SPA packet.\n", $curr_test_file); } } if ($enable_openssl_compatibility_tests) { ### extract the SPA packet from the cmd tmp file my $encoded_msg = ''; my $digest = ''; my $enc_mode = 0; my $is_hmac_type = 1; my $hmac_digest = ''; my $hmac_mode = 'sha256'; open SPA, "< $cmd_out_tmp" or die $!; while () { if (/^\s+Encoded\sData\:\s+(\S+)/) { $encoded_msg = $1; } elsif (/Data\sDigest\:\s(\S+)/) { $digest = $1; } elsif (/Encryption\sMode\:\s+(\d+)/) { $enc_mode = $1; } elsif (/^\s+HMAC\:\s\/) { $is_hmac_type = 0; } elsif (/^\s+HMAC\:\s(\S+)/) { $hmac_digest = $1; } elsif (/^\s+HMAC\sType\:\s\d+\s\((\S+)\)/) { $hmac_mode = lc($1); } } close SPA; $encoded_msg .= ":$digest"; my $ssl_test_flag = $REQUIRE_SUCCESS; $ssl_test_flag = $REQUIRE_FAILURE if $enc_mode != 2; ### CBC mode $ssl_test_flag = $REQUIRE_FAILURE if $is_hmac_type; my $encrypted_msg = &get_spa_packet_from_file($cmd_out_tmp); my $key = ''; my $hmac_key = ''; my $num_hmac_keys = 0; my $b64_decode_key = 0; if ($test_hr->{'key_file'}) { open K, "< $test_hr->{'key_file'}" or die $!; while () { if (/^KEY_BASE64\:?\s+(\S+)/) { $key = $1; $b64_decode_key = 1; } elsif (/^KEY\:?\s+(\S+)/) { $key = $1; } elsif (/^HMAC_KEY_BASE64\:?\s+(\S+)/) { $hmac_key = $1; $b64_decode_key = 1; $num_hmac_keys++; } elsif (/^HMAC_KEY\:?\s+(\S+)/) { $hmac_key = $1; $num_hmac_keys++; } } close K; } $key = $default_key unless $key; unless (&openssl_enc_verification($encrypted_msg, $encoded_msg, '', $key, $b64_decode_key, $ssl_test_flag)) { $rv = 0; } if ($is_hmac_type and $hmac_key) { my $enc_mode = $ENC_RIJNDAEL; $enc_mode = $ENC_GPG if $test_hr->{'msg'} =~ /gpg/i or $test_hr->{'msg'} =~ /gnupg/i; if ($num_hmac_keys == 1) { unless (&openssl_hmac_verification($encrypted_msg, $encoded_msg, '', $hmac_key, $b64_decode_key, $hmac_digest, $hmac_mode, $enc_mode, 0)) { $rv = 0; } } else { #Try for each of the keys my $found_success = 0; open K, "< $test_hr->{'key_file'}" or die $!; while () { if (/^HMAC_KEY_BASE64\:?\s+(\S+)/) { $hmac_key = $1; $b64_decode_key = 1; if (&openssl_hmac_verification($encrypted_msg, $encoded_msg, '', $hmac_key, $b64_decode_key, $hmac_digest, $hmac_mode, $enc_mode, 1)) { $found_success = 1; } } elsif (/^HMAC_KEY\:?\s+(\S+)/) { $hmac_key = $1; if (&openssl_hmac_verification($encrypted_msg, $encoded_msg, '', $hmac_key, $b64_decode_key, $hmac_digest, $hmac_mode, $enc_mode, 1)) { $found_success = 1; } } } close K; if (!$found_success) { $rv = 0; } } } } &write_test_file("[.] client_send_spa_packet() rv: $rv\n", $curr_test_file); return $rv; } sub permissions_check() { my $test_hr = shift; for my $f (keys %cf) { next unless -f $cf{$f}; chmod 0777, $cf{$f} or die $!; } my $rv = &spa_cycle($test_hr); for my $f (keys %cf) { next unless -f $cf{$f}; chmod 0600, $cf{$f} or die $!; } return $rv; } sub rotate_digest_file() { my $test_hr = shift; my $rv = 1; unless (-e $default_digest_file) { open DIGEST, "> $default_digest_file" or die "[*] Could not open $default_digest_file: $!"; print DIGEST "# " . "