diff --git a/.travis.yml b/.travis.yml index a1fa0ef..85ea005 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: install: - sudo apt-get update -qq - - sudo apt-get install -y -qq libcunit1 libcunit1-dev uuid-dev valgrind + - sudo apt-get install -y -qq check libcunit1 libcunit1-dev uuid-dev valgrind script: - mkdir build diff --git a/CMakeLists.txt b/CMakeLists.txt index 154b3af..3312b05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,8 @@ include_directories(${INCLUDE_DIR} ${INCLUDE_DIR}/libparodus ${INCLUDE_DIR}/cimplog ${INCLUDE_DIR}/libseshat - ) + ${INCLUDE_DIR}/cjwt + ) # Compile options/flags #------------------------------------------------------------------------------- @@ -60,7 +61,7 @@ ExternalProject_Add(trower-base64 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_prefix/trower-base64 GIT_REPOSITORY https://github.com/Comcast/trower-base64.git GIT_TAG "master" - CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} + CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DBUILD_TESTING=OFF ) add_library(libtrower-base64 STATIC SHARED IMPORTED) add_dependencies(libtrower-base64 trower-base64) @@ -110,7 +111,7 @@ ExternalProject_Add(cJSON PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_prefix/cJSON GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git GIT_TAG "aafb64a1c549b7b927e339df6d35b1d5059dc235" - CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} + CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DBUILD_TESTING=OFF ) add_library(libcJSON STATIC SHARED IMPORTED) add_dependencies(libcJSON cJSON) @@ -193,6 +194,34 @@ ExternalProject_Add(libseshat add_library(liblibseshat STATIC SHARED IMPORTED) add_dependencies(liblibseshat libseshat) +# libcjwt external dependency +#------------------------------------------------------------------------------- +ExternalProject_Add(cjwt + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_prefix/cjwt + GIT_REPOSITORY https://github.com/Comcast/cjwt.git + GIT_TAG "master" + CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DBUILD_TESTING=OFF +) +add_library(libcjwt STATIC SHARED IMPORTED) +add_dependencies(libcjwt cjwt) + +if (UCLIBC) +# libucresolv external dependency +#------------------------------------------------------------------------------- +ExternalProject_Add(ucresolv + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/_prefix/ucresolv + GIT_REPOSITORY https://github.com/Comcast/libucresolv.git + GIT_TAG "master" + CMAKE_ARGS += -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} +) +add_library(libucresolv STATIC SHARED IMPORTED) +add_dependencies(libucresolv ucresolv) +include_directories(${INCLUDE_DIR} + ${INCLUDE_DIR}/ucresolv + ) + +endif (UCLIBC) + if (BUILD_TESTING) # cmocka external dependency #------------------------------------------------------------------------------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 12dff17..4b5cdbf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,8 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -set(SOURCES main.c mutex.c networking.c nopoll_helpers.c nopoll_handlers.c ParodusInternal.c - string_helpers.c time.c config.c conn_interface.c connection.c spin_thread.c client_list.c service_alive.c upstream.c downstream.c thread_tasks.c partners_check.c) +set(SOURCES main.c mutex.c networking.c nopoll_helpers.c nopoll_handlers.c + ParodusInternal.c string_helpers.c time.c config.c conn_interface.c + connection.c spin_thread.c client_list.c service_alive.c + upstream.c downstream.c thread_tasks.c partners_check.c token.c) add_executable(parodus ${SOURCES}) @@ -32,5 +34,7 @@ target_link_libraries (parodus -lcjson -lpthread -lrt + -lresolv + -lcjwt ) install (TARGETS parodus DESTINATION bin) diff --git a/src/config.c b/src/config.c index d19c343..5bd2525 100644 --- a/src/config.c +++ b/src/config.c @@ -5,9 +5,12 @@ * * Copyright (c) 2015 Comcast */ - + +#include +#include #include "config.h" #include "ParodusInternal.h" +#include /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ @@ -29,6 +32,124 @@ void set_parodus_cfg(ParodusCfg *cfg) parodusCfg = *cfg; } +const char *get_tok (const char *src, int delim, char *result, int resultsize) +{ + int i; + char c; + int endx = resultsize-1; + + memset (result, 0, resultsize); + for (i=0; (c=src[i]) != 0; i++) { + if (c == delim) + break; + if (i < endx) + result[i] = c; + } + if (c == 0) + return NULL; + return src + i + 1; +} + +// the algorithm mask indicates which algorithms are allowed +unsigned int get_algo_mask (const char *algo_str) +{ + unsigned int mask = 0; +#define BUFLEN 16 + char tok[BUFLEN]; + int alg_val; + + while(NULL != algo_str) + { + algo_str = get_tok (algo_str, ':', tok, BUFLEN); + alg_val = cjwt_alg_str_to_enum (tok); + if ((alg_val < 0) || (alg_val >= num_algorithms)) { + ParodusError("Invalid jwt algorithm %s\n", tok); + abort (); + } + mask |= (1<= num_algorithms)) { + ParodusError("Invalid jwt algorithm %s\n", tok); + abort (); + } + mask |= (1<hw_mac, optarg,sizeof(cfg->hw_mac)); - ParodusInfo("hw_mac is %s\n",cfg->hw_mac); + if (parse_mac_address (cfg->hw_mac, optarg) == 0) { + ParodusInfo ("hw_mac is %s\n",cfg->hw_mac); + } else { + ParodusError ("Bad mac address %s\n", optarg); + abort (); + } break; case 'e': @@ -126,6 +255,30 @@ void parseCommandLine(int argc,char **argv,ParodusCfg * cfg) parStrncpy(cfg->local_url, optarg,sizeof(cfg->local_url)); ParodusInfo("parodus local_url is %s\n",cfg->local_url); break; + case 'D': + // like 'fabric' or 'test' + // this parameter is used, along with the hw_mac parameter + // to create the dns txt record id + parStrncpy(cfg->dns_id, optarg,sizeof(cfg->dns_id)); + ParodusInfo("parodus dns_id is %s\n",cfg->dns_id); + break; + + case 'a': + // the command line argument is a list of allowed algoritms, + // separated by colons, like "RS256:RS512:none" + cfg->jwt_algo = get_algo_mask (optarg); + ParodusInfo("jwt_algo is %u\n",cfg->jwt_algo); + break; + case 'k': + // if the key argument has a '.' character in it, then it is + // assumed to be a file, and the file is read in. + if (strchr (optarg, '.') == NULL) { + parStrncpy(cfg->jwt_key, optarg,sizeof(cfg->jwt_key)); + } else { + read_key_from_file (optarg, cfg->jwt_key, sizeof(cfg->jwt_key)); + } + ParodusInfo("jwt_key is %s\n",cfg->jwt_key); + break; case 'p': parStrncpy(cfg->partner_id, optarg,sizeof(cfg->partner_id)); @@ -240,6 +393,17 @@ void loadParodusCfg(ParodusCfg * config,ParodusCfg *cfg) } + if( strlen(pConfig->dns_id) !=0) + { + strncpy(cfg->dns_id, pConfig->dns_id,strlen(pConfig->dns_id)+1); + } + else + { + ParodusInfo("parodus dns-id is NULL. adding default\n"); + strncpy(cfg->dns_id, DNS_ID, strlen(DNS_ID)+1); + + } + if( strlen(pConfig->partner_id) !=0) { strncpy(cfg->partner_id, pConfig->partner_id,strlen(pConfig->partner_id)+1); @@ -256,9 +420,18 @@ void loadParodusCfg(ParodusCfg * config,ParodusCfg *cfg) else { ParodusInfo("seshat_url is NULL. Read from tmp file\n"); - + } + if(strlen(pConfig->jwt_key )!=0) + { + strncpy(cfg->jwt_key, pConfig->jwt_key,strlen(pConfig->jwt_key)+1); + } + else + { + strcpy(cfg->jwt_key, "\0"); + ParodusPrint("jwt_key is NULL. set to empty\n"); } + cfg->jwt_algo = pConfig->jwt_algo; cfg->boot_time = pConfig->boot_time; cfg->secureFlag = 1; cfg->webpa_ping_timeout = pConfig->webpa_ping_timeout; diff --git a/src/config.h b/src/config.h index 8326fa0..408e382 100644 --- a/src/config.h +++ b/src/config.h @@ -36,7 +36,10 @@ extern "C" { #define WEBPA_PROTOCOL_VALUE "WebPA-1.6" #define WEBPA_PATH_URL "/api/v2/device" +#define JWT_ALGORITHM "jwt-algo" +#define JWT_KEY "jwt-key" #define PARODUS_UPSTREAM "tcp://127.0.0.1:6666" +#define DNS_ID "fabric" /*----------------------------------------------------------------------------*/ /* Data Structures */ @@ -59,9 +62,12 @@ typedef struct char webpa_protocol[16]; char webpa_uuid[64]; unsigned int secureFlag; + char dns_id[64]; char local_url[124]; char partner_id[64]; char seshat_url[128]; + unsigned int jwt_algo; // bit mask set for each allowed algorithm + char jwt_key[4096]; // may be read in from a pem file } ParodusCfg; /*----------------------------------------------------------------------------*/ diff --git a/src/token.c b/src/token.c new file mode 100644 index 0000000..02e22bf --- /dev/null +++ b/src/token.c @@ -0,0 +1,473 @@ +/** + * @file token.c + * + * @description This file contains operations for using jwt token. + * + * Copyright (c) 2015 Comcast + */ +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "config.h" +#include "parodus_log.h" + +#define JWT_MAXBUF 8192 + +#ifdef NS_MAXMSG +#if NS_MAXMSG > JWT_MAXBUF +#define NS_MAXBUF JWT_MAXBUF +#else +#define NS_MAXBUF NS_MAXMSG +#endif +#else +#define NS_MAXBUF JWT_MAXBUF +#endif + +#define TXT_REC_ID_MAXSIZE 128 + +#define MAX_RR_RECS 10 +#define SEQ_TABLE_SIZE (MAX_RR_RECS + 1) + +typedef struct { + const char *rr_ptr; + int rr_len; +} rr_rec_t; + +/*----------------------------------------------------------------------------*/ +/* Macros */ +/*----------------------------------------------------------------------------*/ +#define ENDPOINT_NAME "endpoint" + +/*----------------------------------------------------------------------------*/ +/* File Scoped Variables */ +/*----------------------------------------------------------------------------*/ +/* none */ + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ +/* none */ + +/*----------------------------------------------------------------------------*/ +/* External Functions */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Internal functions */ +/*----------------------------------------------------------------------------*/ + + +static void show_times (time_t exp_time, time_t cur_time) +{ + char exp_buf[30]; + char cur_buf[30]; + ctime_r (&exp_time, exp_buf); + exp_buf[strlen(exp_buf)-1] = 0; + ctime_r (&cur_time, cur_buf); + cur_buf[strlen(cur_buf)-1] = 0; + ParodusInfo ("Exp: %d %s, Current: %d %s\n", + (int)exp_time, exp_buf+4, (int)cur_time, cur_buf+4); +} + +// returns 1 if insecure, 0 if secure, -1 if error +int analyze_jwt (const cjwt_t *jwt) +{ + cJSON *claims = jwt->private_claims; + cJSON *endpoint = NULL; + time_t exp_time, cur_time; + int http_match; + + if (!claims) { + ParodusError ("Private claims not found in jwt\n"); + return -1; + } + + endpoint = cJSON_GetObjectItem(claims, ENDPOINT_NAME); + if (!endpoint) { + ParodusError ("Endpoint claim not found in jwt\n"); + return -1; + } + + http_match = strncmp(endpoint->valuestring,"http:",5); + ParodusInfo ("is_http strncmp: %d\n", http_match); + exp_time = jwt->exp.tv_sec; + if (0 == exp_time) { + ParodusError ("exp not found in JWT payload\n"); + } else { + cur_time = time(NULL); + show_times (exp_time, cur_time); + if (exp_time < cur_time) { + ParodusError ("JWT has expired\n"); + return -1; + } + } + + return (http_match == 0); +} + +bool validate_algo(const cjwt_t *jwt) +{ + // return true if jwt->header.alg is included in the set + // of allowed algorithms specified by cfg->jwt_algo + ParodusCfg *cfg = get_parodus_cfg(); + int alg = jwt->header.alg; + int alg_mask; + + if ((alg < 0) || (alg >= num_algorithms)) + return false; + alg_mask = 1<jwt_algo) == 0) { + ParodusError ("Algorithm %d not allowed (mask %d)\n", alg, alg_mask); + return false; + } + return true; +} + + +int nquery(const char* dns_txt_record_id,u_char *nsbuf) +{ + + int len; + struct __res_state statp; + + /* Initialize resolver */ + memset (&statp, 0, sizeof(__res_state)); + statp.options |= RES_DEBUG; + if (res_ninit(&statp) < 0) { + ParodusError ("res_ninit error: can't initialize statp.\n"); + return (-1); + } + + ParodusInfo ("Domain : %s\n", dns_txt_record_id); + memset (nsbuf, 0, NS_MAXBUF); + len = res_nquery(&statp, dns_txt_record_id, ns_c_any, ns_t_txt, nsbuf, NS_MAXBUF); + if (len < 0) { + const char *msg = hstrerror (statp.res_h_errno); + ParodusError ("Error in res_nquery: %s\n", msg); + return len; + } + res_nclose (&statp); + if (len >= NS_MAXBUF) { + ParodusError ("res_nquery error: ns buffer too small.\n"); + return -1; + } + + return len; + +} + +bool valid_b64_char (char c) +{ + if ((c>='A') && (c<='Z')) + return true; + if ((c>='a') && (c<='z')) + return true; + if ((c>='0') && (c<='9')) + return true; + if ((c=='/') || (c=='+') || (c=='-') || (c=='_')) + return true; + return false; +} + +static bool is_digit (char c) +{ + return (bool) ((c>='0') && (c<='9')); +} + +// strip quotes and newlines from rr rec +const char *strip_rr_data (const char *rr_ptr, int *rrlen) +{ + int len; + const char *optr = rr_ptr; + char c; + + len = strlen (optr); + if (len > 0) { + c = optr[0]; + if (!is_digit(c)) { + optr++; + len--; + } + } + if (len > 0) { + if (!valid_b64_char (optr[len-1])) + len--; + } + if (len > 0) { + if (!valid_b64_char (optr[len-1])) + len--; + } + *rrlen = len; + return optr; +} + +// return offset to seq number in record +// return -1 if not found +int find_seq_num (const char *rr_ptr, int rrlen) +{ + char c; + int i; + int digit_ct = 0; + + for (i=0; i= 2) + return i - 2; + else + return -1; + } + if (is_digit (c)) + digit_ct++; + else + digit_ct = 0; + } + return -1; +} + +// get seq num in rr rec +// return -1 if not formatted correctly +int get_rr_seq_num (const char *rr_ptr, int rrlen) +{ + char c; + int lo, hi; + if (rrlen < 3) + return -1; + if (rr_ptr[2] != ':') + return -1; + c = rr_ptr[0]; + if (is_digit (c)) + hi = c - '0'; + else + return -1; + c = rr_ptr[1]; + if (is_digit (c)) + lo = c - '0'; + else + return -1; + return (10*hi) + lo; +} + +// scan rr recs and build seq table using seq numbers in the recs +// return num_txt_recs +static int get_rr_seq_table (ns_msg *msg_handle, int num_rr_recs, rr_rec_t *seq_table) +{ + ns_rr rr; + const char *rr_ptr; + int seq_pos; + int rrlen; + int i, ret, seq_num; + int num_txt_recs = 0; + + if (num_rr_recs > MAX_RR_RECS) { + ParodusError ("num rr recs (%d) to big, > %d\n", num_rr_recs, MAX_RR_RECS); + return -1; + } + // clear seq table + for (i=0; i num_rr_recs) { + ParodusError ("Invalid seq number (too big) in rr record %d\n", i); + return -1; + } + if (NULL != seq_table[seq_num].rr_ptr) { + ParodusError ("Duplicate rr record number %d\n", seq_num); + return -1; + } + if (seq_num != 0) { + rr_ptr += 3; // skip the seq number + rrlen -= 3; + } + seq_table[seq_num].rr_ptr = rr_ptr; + seq_table[seq_num].rr_len = rrlen; + } + + if (NULL != seq_table[0].rr_ptr) { + // sequence-less record should not be used when there + // are multiple records + if (num_txt_recs > 1) { + ParodusError ("Seq number not found in rr record\n"); + return -1; + } + // when there is only one record, use the sequence-less record + seq_table[1].rr_ptr = seq_table[0].rr_ptr; + seq_table[1].rr_len = seq_table[0].rr_len; + } + + // check if we got them all + for (i=1; ihw_mac, cfg->dns_id); + ParodusInfo("dns_txt_record_id %s\n", buf); +} + +int allow_insecure_conn(void) +{ + int insecure=0, ret = -1; + char *jwt_token, *key; + cjwt_t *jwt = NULL; + char dns_txt_record_id[TXT_REC_ID_MAXSIZE]; + + jwt_token = malloc (NS_MAXBUF); + if (NULL == jwt_token) { + ParodusError ("Unable to allocate jwt_token in allow_insecure_conn\n"); + insecure = -1; + goto end; + } + + get_dns_txt_record_id (dns_txt_record_id); + + //Querying dns for jwt token + ret = query_dns(dns_txt_record_id, jwt_token); + if(ret){ + insecure = -1; + goto end; + } + + //Decoding the jwt token + key = get_parodus_cfg()->jwt_key; + ret = cjwt_decode( jwt_token, 0, &jwt, ( const uint8_t * )key,strlen(key) ); + + if(ret) { + if (ret == ENOMEM) + ParodusError ("Memory allocation failed in JWT decode\n"); + else + ParodusError ("CJWT decode error\n"); + insecure = -1; + goto end; + }else{ + ParodusPrint("Decoded CJWT successfully\n"); + + //validate algo from --jwt_algo + if( validate_algo(jwt) ){ + insecure = analyze_jwt (jwt); + } + } + cjwt_destroy(&jwt); + +end: + if (NULL != jwt_token) + free (jwt_token); + ParodusPrint ("Allow Insecure %d\n", insecure); + return insecure; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 05b80c5..867b254 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2016 Comcast Cable Communications Management, LLC +# Copyright 2016 Comcast Cable Communications Management, LLCD # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,8 +17,12 @@ set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DTEST ") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -g -fprofile-arcs -ftest-coverage -O0") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -O0") set (PARODUS_COMMON_SRC ../src/string_helpers.c ../src/mutex.c ../src/time.c ../src/config.c ../src/spin_thread.c) -set (PARODUS_COMMON_LIBS gcov -lcunit -lcimplog -llibseshat -lwrp-c -luuid -lpthread -lm -lmsgpackc -lcjson -ltrower-base64 -lnopoll -lnanomsg -Wl,--no-as-needed -lrt) - +set (PARODUS_COMMON_LIBS gcov -lcunit -lcimplog -llibseshat -lwrp-c + -luuid -lpthread -lmsgpackc + -lnopoll -lnanomsg -Wl,--no-as-needed + -lcjwt -lcjson -ltrower-base64 -lssl -lcrypto -lrt -lm +) +set (PARODUS_CONN_LIBS -lresolv) if(NOT DISABLE_VALGRIND) set (MEMORY_CHECK valgrind --leak-check=full --show-reachable=yes -v) endif () @@ -104,36 +108,51 @@ target_link_libraries (test_nopoll_handlers -lnopoll -lcunit -lcimplog -Wl,--no- # test_connection #------------------------------------------------------------------------------- add_test(NAME test_connection COMMAND ${MEMORY_CHECK} ./test_connection) -add_executable(test_connection test_connection.c ../src/connection.c ${PARODUS_COMMON_SRC}) -target_link_libraries (test_connection ${PARODUS_COMMON_LIBS} -lcmocka) +#add_executable(test_connection test_connection.c ../src/connection.c ${PARODUS_COMMON_SRC}) +#target_link_libraries (test_connection ${PARODUS_COMMON_LIBS} -lcmocka) +add_executable(test_connection test_connection.c ../src/connection.c ../src/token.c ${PARODUS_COMMON_SRC}) +target_link_libraries (test_connection ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS} -lcmocka) #------------------------------------------------------------------------------- # test_connection - function createNopollConnection #------------------------------------------------------------------------------- add_test(NAME test_createConnection COMMAND ${MEMORY_CHECK} ./test_createConnection) +#add_executable(test_createConnection test_createConnection.c ../src/connection.c ../src/string_helpers.c ../src/config.c) +#target_link_libraries (test_createConnection ${PARODUS_COMMON_LIBS} -lcmocka) add_executable(test_createConnection test_createConnection.c ../src/connection.c ../src/string_helpers.c ../src/config.c) -target_link_libraries (test_createConnection ${PARODUS_COMMON_LIBS} -lcmocka) +target_link_libraries (test_createConnection ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS} -lcmocka ) #------------------------------------------------------------------------------- # test_client_list #------------------------------------------------------------------------------- add_test(NAME test_client_list COMMAND ${MEMORY_CHECK} ./test_client_list) -add_executable(test_client_list test_client_list.c ../src/client_list.c ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c ../src/downstream.c ../src/connection.c ../src/nopoll_handlers.c ../src/ParodusInternal.c ../src/thread_tasks.c ../src/conn_interface.c ../src/partners_check.c ${PARODUS_COMMON_SRC}) -target_link_libraries (test_client_list ${PARODUS_COMMON_LIBS}) +#add_executable(test_client_list test_client_list.c ../src/client_list.c ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c ../src/downstream.c ../src/connection.c ../src/nopoll_handlers.c ../src/ParodusInternal.c ../src/thread_tasks.c ../src/conn_interface.c ../src/partners_check.c ${PARODUS_COMMON_SRC}) +#target_link_libraries (test_client_list ${PARODUS_COMMON_LIBS}) +add_executable(test_client_list test_client_list.c ../src/client_list.c + ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c + ../src/downstream.c ../src/connection.c ../src/nopoll_handlers.c + ../src/ParodusInternal.c ../src/thread_tasks.c ../src/conn_interface.c + ../src/partners_check.c ../src/token.c ${PARODUS_COMMON_SRC}) +target_link_libraries (test_client_list ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS}) #------------------------------------------------------------------------------- # test_service_alive #------------------------------------------------------------------------------- add_test(NAME test_service_alive COMMAND ${MEMORY_CHECK} ./test_service_alive) -add_executable(test_service_alive test_service_alive.c ../src/client_list.c ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c ../src/nopoll_handlers.c ../src/config.c ../src/connection.c ../src/ParodusInternal.c ../src/downstream.c ../src/thread_tasks.c ../src/conn_interface.c ../src/partners_check.c ${PARODUS_COMMON_SRC}) -target_link_libraries (test_service_alive ${PARODUS_COMMON_LIBS}) +#add_executable(test_service_alive test_service_alive.c ../src/client_list.c ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c ../src/nopoll_handlers.c ../src/config.c ../src/connection.c ../src/ParodusInternal.c ../src/downstream.c ../src/thread_tasks.c ../src/conn_interface.c ../src/partners_check.c ${PARODUS_COMMON_SRC}) +#target_link_libraries (test_service_alive ${PARODUS_COMMON_LIBS}) +add_executable(test_service_alive test_service_alive.c ../src/client_list.c ../src/service_alive.c ../src/upstream.c ../src/networking.c ../src/nopoll_helpers.c ../src/nopoll_handlers.c ../src/config.c ../src/connection.c ../src/token.c ../src/ParodusInternal.c ../src/downstream.c ../src/thread_tasks.c ../src/conn_interface.c ../src/partners_check.c ${PARODUS_COMMON_SRC}) +target_link_libraries (test_service_alive ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS}) #------------------------------------------------------------------------------- # test_config #------------------------------------------------------------------------------- add_test(NAME test_config COMMAND ${MEMORY_CHECK} ./test_config) add_executable(test_config test_config.c ../src/config.c ../src/string_helpers.c) -target_link_libraries (test_config -lcmocka -lm -Wl,--no-as-needed -lrt -lcimplog) +target_link_libraries (test_config -lcmocka + -Wl,--no-as-needed -lcimplog + -lcjwt -lcjson -ltrower-base64 -lssl -lcrypto -lrt -lm +) #------------------------------------------------------------------------------- # test_upstream @@ -177,6 +196,13 @@ add_test(NAME test_partners_check COMMAND ${MEMORY_CHECK} ./test_partners_check) add_executable(test_partners_check test_partners_check.c ../src/partners_check.c) target_link_libraries (test_partners_check -lcmocka -lwrp-c -llibseshat ${PARODUS_COMMON_LIBS}) +#------------------------------------------------------------------------------- +# test_token - token.c tests +#------------------------------------------------------------------------------- +add_test(NAME test_token COMMAND ${MEMORY_CHECK} ./test_token) +add_executable(test_token test_token.c ../src/token.c ../src/string_helpers.c ../src/config.c) +target_link_libraries (test_token ${PARODUS_COMMON_LIBS} ${PARODUS_JWT_LIBS} -lcmocka ) + if (INTEGRATION_TESTING) #------------------------------------------------------------------------------- # simple_connection test @@ -191,10 +217,36 @@ add_executable(simple_connection simple_connection.c ${PARODUS_COMMON_SRC} ../src/nopoll_helpers.c ../src/nopoll_handlers.c ../src/connection.c + #../src/token.c ../src/ParodusInternal.c ../src/client_list.c ../src/partners_check.c ../src/service_alive.c) -target_link_libraries (simple_connection ${PARODUS_COMMON_LIBS}) +target_link_libraries (simple_connection ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS} ) +#------------------------------------------------------------------------------- +# simple test +#------------------------------------------------------------------------------- +add_test(NAME simple COMMAND ${MEMORY_CHECK} ./simple) +add_executable(simple simple.c + ../src/upstream.c + ../src/conn_interface.c + ../src/downstream.c + ../src/thread_tasks.c + ../src/networking.c + ../src/nopoll_helpers.c + ../src/nopoll_handlers.c + ../src/string_helpers.c + ../src/mutex.c + ../src/time.c + ../src/config.c + ../src/connection.c + #../src/token.c + ../src/ParodusInternal.c + ../src/spin_thread.c + ../src/client_list.c + ../src/partners_check.c + ../src/service_alive.c) + +target_link_libraries (simple ${PARODUS_CONN_LIBS} ${PARODUS_COMMON_LIBS} gcov -lnopoll -lnanomsg ) endif (INTEGRATION_TESTING) diff --git a/tests/parodus_cmd.sh b/tests/parodus_cmd.sh new file mode 100755 index 0000000..8971c7c --- /dev/null +++ b/tests/parodus_cmd.sh @@ -0,0 +1,3 @@ +./parodus --hw-model=TG1682 --hw-serial-number=Fer23u948590 --hw-manufacturer=ARRISGroup,Inc. --hw-mac=aabbccddeeff --hw-last-reboot-reason=unknown --fw-name=TG1682_DEV_master_2016000000sdy --boot-time=123589 --webpa-ping-time=180 --webpa-backoff-max=0 --webpa-inteface-used=p7p1 --webpa-url=fabric-beta.webpa.comcast.net --jwt-algo=none:RS256 --jwt-key=../../tests/webpa-rs256.pem --dns-id=test + + diff --git a/tests/test_token.c b/tests/test_token.c new file mode 100644 index 0000000..a9d735d --- /dev/null +++ b/tests/test_token.c @@ -0,0 +1,310 @@ +/** + * Copyright 2010-2016 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../src/ParodusInternal.h" +#include "../src/connection.h" +#include "../src/config.h" + +const char *header = "{ \"alg\": \"RS256\", \"typ\": \"JWT\"}"; + +time_t exp_time_good = 2147483647; // 1/18/2038 +time_t exp_time_bad = 1463955372; // 5/22/2016 + + +const char *payload_good = "{" \ + "\"iss\": \"SHA256:jdcRysFunWUAT852huQoIM9GN6k2s5c7iTMTMgujPAk\"," \ + "\"endpoint\": \"https://fabric.webpa.comcast.net:8080/\"}"; + +const char *payload_insec = "{" \ + "\"iss\": \"SHA256:jdcRysFunWUAT852huQoIM9GN6k2s5c7iTMTMgujPAk\"," \ + "\"endpoint\": \"http://fabric.webpa.comcast.net:8080/\"}"; + +// missing endpoint +const char *payload_no_end = "{" \ + "\"iss\": \"SHA256:jdcRysFunWUAT852huQoIM9GN6k2s5c7iTMTMgujPAk\"}"; + +const char *txt_record_id = "aabbccddeeff.test.webpa.comcast.net"; + +cjwt_t jwt1; // secure, payload good +cjwt_t jwt2; // secure, payload good, but expired +cjwt_t jwt3; // insecure +cjwt_t jwt4; // missing endpoint + +// internal functions in token.c to be tested +extern int analyze_jwt (const cjwt_t *jwt); +extern bool validate_algo(const cjwt_t *jwt); +extern int nquery(const char* dns_txt_record_id,u_char *nsbuf); +extern bool valid_b64_char (char c); +extern const char *strip_rr_data (const char *rr_ptr, int *rrlen); +extern int find_seq_num (const char *rr_ptr, int rrlen); +extern int get_rr_seq_num (const char *rr_ptr, int rrlen); + + +int setup_test_jwts (void) +{ + memset (&jwt1, 0, sizeof(cjwt_t)); + jwt1.exp.tv_sec = exp_time_good; + jwt1.private_claims = cJSON_Parse ((char *) payload_good); + if (NULL == jwt1.private_claims) { + printf ("Invalid json struct payload_good\n"); + return -1; + } + + memset (&jwt2, 0, sizeof(cjwt_t)); + jwt2.exp.tv_sec = exp_time_bad; + jwt2.private_claims = cJSON_Parse ((char *) payload_good); + if (NULL == jwt2.private_claims) { + printf ("Invalid json struct payload_good\n"); + return -1; + } + + memset (&jwt3, 0, sizeof(cjwt_t)); + jwt3.exp.tv_sec = exp_time_good; + jwt3.private_claims = cJSON_Parse ((char *) payload_insec); + if (NULL == jwt3.private_claims) { + printf ("Invalid json struct payload_insec\n"); + return -1; + } + + memset (&jwt4, 0, sizeof(cjwt_t)); + jwt4.exp.tv_sec = exp_time_good; + jwt4.private_claims = cJSON_Parse ((char *) payload_no_end); + if (NULL == jwt4.private_claims) { + printf ("Invalid json struct payload_good\n"); + return -1; + } + + return 0; +} + + + +/*----------------------------------------------------------------------------*/ +/* Mocks */ +/*----------------------------------------------------------------------------*/ + +int ns_initparse(const u_char *nsbuf, int l, ns_msg *msg_handle) +{ + UNUSED(nsbuf); UNUSED(l); UNUSED(msg_handle); + function_called (); + return (int) mock(); +} + +int ns_parserr(ns_msg *msg_handle, ns_sect sect, int rec, ns_rr *rr) +{ + UNUSED(msg_handle); UNUSED(sect); UNUSED(rec); UNUSED(rr); + function_called (); + return (int) mock(); +} + +int __res_ninit (res_state statp) +{ + UNUSED(statp); + function_called (); + return (int) mock(); +} + +int __res_nquery (res_state statp, const char * txt_record, + int class, int type_, u_char * buf, int bufsize) +{ + UNUSED(statp); UNUSED(txt_record); UNUSED(class); UNUSED(type_); + UNUSED(buf); UNUSED(bufsize); + + function_called (); + return (int) mock(); +} + +void __res_nclose (res_state statp) +{ + UNUSED (statp); + function_called (); +} + +void test_analyze_jwt () +{ + int ret = setup_test_jwts (); + assert_int_equal (ret, 0); + ret = analyze_jwt (&jwt1); + assert_int_equal (ret, 0); + ret = analyze_jwt (&jwt2); + assert_int_equal (ret, -1); + ret = analyze_jwt (&jwt3); + assert_int_equal (ret, 1); + ret = analyze_jwt (&jwt4); + assert_int_equal (ret, -1); + +} + +void test_validate_algo () +{ + bool ret; + ParodusCfg cfg; + cfg.jwt_algo = (1<