Merge pull request #101 from bill1600/cjwt

cjwt integration into parodus
This commit is contained in:
Weston Schmidt
2017-06-09 11:56:49 -07:00
committed by GitHub
10 changed files with 1082 additions and 23 deletions

View File

@@ -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

View File

@@ -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
#-------------------------------------------------------------------------------

View File

@@ -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)

View File

@@ -5,9 +5,12 @@
*
* Copyright (c) 2015 Comcast
*/
#include <stdio.h>
#include <fcntl.h>
#include "config.h"
#include "ParodusInternal.h"
#include <cjwt/cjwt.h>
/*----------------------------------------------------------------------------*/
/* 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<<alg_val);
}
return mask;
#undef BUFLEN
}
// the algorithm mask indicates which algorithms are allowed
#if 0
unsigned int get_algo_mask (const char *algo_str)
{
unsigned int mask = 0;
char *tok;
int alg_val;
#define BUFLEN 128
char algo_buf[BUFLEN];
strncpy (algo_buf, algo_str, BUFLEN-1);
algo_buf[BUFLEN-1] = 0;
tok = strtok(algo_buf, ":");
while(tok!=NULL)
{
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<<alg_val);
tok = strtok(NULL,":");
}
return mask;
#undef BUFLEN
}
#endif
static int open_input_file (const char *fname)
{
int fd = open(fname, O_RDONLY);
if (fd<0)
{
ParodusError ("File %s open error\n", fname);
abort ();
}
return fd;
}
void read_key_from_file (const char *fname, char *buf, size_t buflen)
{
ssize_t nbytes;
int fd = open_input_file(fname);
nbytes = read(fd, buf, buflen);
if (nbytes < 0)
{
ParodusError ("Read file %s error\n", fname);
close(fd);
abort ();
}
close(fd);
ParodusInfo ("%d bytes read\n", nbytes);
}
// strips ':' characters
// verifies that there exactly 12 characters
static int parse_mac_address (char *target, const char *arg)
{
int count = 0;
int i;
char c;
for (i=0; (c=arg[i]) != 0; i++) {
if (c !=':')
count++;
}
if (count != 12)
return -1; // bad mac address
for (i=0; (c=arg[i]) != 0; i++) {
if (c != ':')
*(target++) = c;
}
*target = 0; // terminating null
return 0;
}
void parseCommandLine(int argc,char **argv,ParodusCfg * cfg)
{
int c;
@@ -50,11 +171,15 @@ void parseCommandLine(int argc,char **argv,ParodusCfg * cfg)
{"parodus-local-url", required_argument, 0, 'l'},
{"partner-id", required_argument, 0, 'p'},
{"seshat-url", required_argument, 0, 'e'},
{"dns-id", required_argument, 0, 'D'},
{JWT_ALGORITHM, required_argument, 0, 'a'},
{JWT_KEY, required_argument, 0, 'k'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long (argc, argv, "m:s:f:d:r:n:b:u:t:o:i:l:p:e:",long_options, &option_index);
c = getopt_long (argc, argv, "m:s:f:d:r:n:b:u:t:o:i:l:p:e:D:a:k",
long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
@@ -78,8 +203,12 @@ void parseCommandLine(int argc,char **argv,ParodusCfg * cfg)
break;
case 'd':
parStrncpy(cfg->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;

View File

@@ -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;
/*----------------------------------------------------------------------------*/

473
src/token.c Normal file
View File

@@ -0,0 +1,473 @@
/**
* @file token.c
*
* @description This file contains operations for using jwt token.
*
* Copyright (c) 2015 Comcast
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
//#include <res_update.h>
#include <netdb.h>
#include <strings.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <cjwt/cjwt.h>
#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<<alg;
if ((alg_mask & cfg->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<rrlen; i++)
{
c = rr_ptr[i];
if (c == ':') {
if (digit_ct >= 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<SEQ_TABLE_SIZE; i++)
{
seq_table[i].rr_ptr = NULL;
seq_table[i].rr_len = 0;
}
// extract and concatenate all the records in rr
for (i=0; i<num_rr_recs; i++) {
ret = ns_parserr(msg_handle, ns_s_an, i, &rr);
if (ret != 0) {
ParodusError ("query_dns: ns_parserr failed: %s\n", strerror (errno));
return ret;
}
if (ns_rr_type(rr) != ns_t_txt)
continue;
++num_txt_recs;
rr_ptr = (const char *)ns_rr_rdata(rr);
ParodusPrint ("Found rr rec: %s\n", rr_ptr);
rr_ptr = strip_rr_data (rr_ptr, &rrlen);
ParodusPrint ("Stripped rr rec: %s\n", rr_ptr);
seq_pos = find_seq_num (rr_ptr, rrlen);
if (seq_pos < 0) {
seq_num = 0;
} else {
rr_ptr += seq_pos;
rrlen -= seq_pos;
seq_num = get_rr_seq_num (rr_ptr, rrlen);
}
ParodusPrint ("Found seq num %d in rr rec %d\n", seq_num, i);
if (seq_num < 0) {
ParodusError ("Seq number not found in rr record %d\n", i);
return -1;
}
if (seq_num > 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; i<num_txt_recs; i++) {
if (NULL == seq_table[i].rr_ptr) {
ParodusError ("Missing rr record number %d\n", i+1);
return -1;
}
}
return num_txt_recs;
}
static int assemble_jwt_from_dns (ns_msg *msg_handle, int num_rr_recs, char *jwt_ans)
{
// slot 0 in the seq table is for the sequence-less record that
// you get when there is only one record.
rr_rec_t seq_table[SEQ_TABLE_SIZE];
int i;
int num_txt_recs = get_rr_seq_table (msg_handle, num_rr_recs, seq_table);
if (num_txt_recs < 0)
return num_txt_recs;
ParodusPrint ("Found %d TXT records\n", num_txt_recs);
jwt_ans[0] = 0;
for (i=1; i<=num_txt_recs; i++)
strncat (jwt_ans, seq_table[i].rr_ptr, seq_table[i].rr_len);
return 0;
}
static int query_dns(const char* dns_txt_record_id,char *jwt_ans)
{
u_char *nsbuf;
ns_msg msg_handle;
int ret;
int l = -1;
if( !dns_txt_record_id || !jwt_ans )
return l;
nsbuf = malloc (NS_MAXBUF);
if (NULL == nsbuf) {
ParodusError ("Unable to allocate nsbuf in query_dns\n");
return -1;
}
l = nquery(dns_txt_record_id,nsbuf);
if (l < 0) {
free (nsbuf);
return l;
}
ParodusInfo ("initparse\n");
ret = ns_initparse(nsbuf, l, &msg_handle);
if (ret != 0) {
ParodusError ("ns_initparse failed\n");
free (nsbuf);
return ret;
}
l = ns_msg_count(msg_handle, ns_s_an);
ParodusInfo ("query_dns: ns_msg_count : %d\n",l);
jwt_ans[0] = 0;
ret = assemble_jwt_from_dns (&msg_handle, l, jwt_ans);
free (nsbuf);
if (ret == 0)
ParodusInfo ("query_dns JWT: %s\n", jwt_ans);
return ret;
}
static void get_dns_txt_record_id (char *buf)
{
ParodusCfg *cfg = get_parodus_cfg();
buf[0] = 0;
sprintf (buf, "%s.%s.webpa.comcast.net", cfg->hw_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;
}

View File

@@ -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)

3
tests/parodus_cmd.sh Executable file
View File

@@ -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

310
tests/test_token.c Normal file
View File

@@ -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 <stdarg.h>
#include <CUnit/Basic.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <resolv.h>
#include <cmocka.h>
#include <assert.h>
#include <cjwt/cjwt.h>
#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<<alg_none) | (1<<alg_rs256);
set_parodus_cfg (&cfg);
jwt1.header.alg = alg_rs256;
ret = validate_algo (&jwt1);
assert_int_equal (ret, 1);
jwt1.header.alg = alg_rs512;
ret = validate_algo (&jwt1);
assert_int_equal (ret, 0);
}
void test_nquery ()
{
int len;
u_char nsbuf[8192];
will_return (__res_ninit, 0);
expect_function_call (__res_ninit);
will_return (__res_nquery, 4096);
expect_function_call (__res_nquery);
expect_function_call (__res_nclose);
len = nquery (txt_record_id, nsbuf);
assert_int_equal (len, 4096);
will_return (__res_ninit, -1);
expect_function_call (__res_ninit);
len = nquery (txt_record_id, nsbuf);
assert_int_equal (len, -1);
will_return (__res_ninit, 0);
expect_function_call (__res_ninit);
will_return (__res_nquery, -1);
expect_function_call (__res_nquery);
len = nquery (txt_record_id, nsbuf);
assert_int_equal (len, -1);
}
void test_valid_b64_char()
{
assert_int_equal (valid_b64_char ('A'), 1);
assert_int_equal (valid_b64_char ('@'), 0);
}
void test_strip_rrdata ()
{
const char *s1 = "\"01:this-is-the-test-string-\"\n";
const char *s2 = "\"01:this-is-the-test-string-\n";
const char *ss1 = "01:this-is-the-test-string-";
int s1len = strlen (ss1);
const char *result;
int rlen;
result = strip_rr_data (s1, &rlen);
assert_int_equal (rlen, s1len);
if (rlen == s1len) {
assert_int_equal (strncmp (result, ss1, rlen), 0);
}
result = strip_rr_data (s2, &rlen);
assert_int_equal (rlen, s1len);
if (rlen == s1len) {
assert_int_equal (strncmp (result, ss1, rlen), 0);
}
}
void test_find_seq_num ()
{
int pos;
const char *s1 = "01:this-is-it";
const char *s2 = "1:this-is-it";
const char *s3 = ":this-is-it";
const char *s4 = ".01:this-is-it";
const char *s5 = "..01:this-is-it";
const char *s6 = "99999";
const char *s7 = "xxxxx";
pos = find_seq_num (s1, strlen(s1));
assert_int_equal (pos, 0);
pos = find_seq_num (s2, strlen(s2));
assert_int_equal (pos, -1);
pos = find_seq_num (s3, strlen(s3));
assert_int_equal (pos, -1);
pos = find_seq_num (s4, strlen(s4));
assert_int_equal (pos, 1);
pos = find_seq_num (s5, strlen(s5));
assert_int_equal (pos, 2);
pos = find_seq_num (s6, strlen(s6));
assert_int_equal (pos, -1);
pos = find_seq_num (s7, strlen(s7));
assert_int_equal (pos, -1);
}
void test_get_rr_seq_num ()
{
int result;
const char *s1 = "01:this-is-it";
const char *s2 = "1:this-is-it";
const char *s3 = ":this-is-it";
const char *s4 = "11:this-is-it";
result = get_rr_seq_num (s1, strlen(s1));
assert_int_equal (result, 1);
result = get_rr_seq_num (s2, strlen(s2));
assert_int_equal (result, -1);
result = get_rr_seq_num (s3, strlen(s3));
assert_int_equal (result, -1);
result = get_rr_seq_num (s4, strlen(s4));
assert_int_equal (result, 11);
}
/*----------------------------------------------------------------------------*/
/* External Functions */
/*----------------------------------------------------------------------------*/
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_analyze_jwt),
cmocka_unit_test(test_validate_algo),
cmocka_unit_test(test_nquery),
cmocka_unit_test(test_valid_b64_char),
cmocka_unit_test(test_strip_rrdata),
cmocka_unit_test(test_find_seq_num),
cmocka_unit_test(test_get_rr_seq_num),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

9
tests/webpa-rs256.pem Normal file
View File

@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkZeQUMqDkMu/dQgTcycJ
/rrgqfKQkoYFTMO7GnK87/OWiftHmtDJYPgktlcHheOPfkGln5ay1WJSitmtWNaH
+RvBFK/ZsXpIuGm7R6wdSc7e6g9fTaSAfNX/+a8VxHUo58AinXxcq4LnHbuaGjEz
jw77TLuZGyUuHiany8O8tc+DbnYKvRquScsccI6z/QwZKFtXUjJZ91hJ97zC8o7N
Ae7n/Jg+Bs0uz9c1/bf/Jqbu6OidFbCr2FN42UupuAZ8DiPp2fWD5Q9qmp1ADk+V
+TeZPxTCq/WB4dzSCd5v/FvFmO8tH6Ptkltij4pke7Dsi80TVRlcMDXAWxSFXOQV
qwIDAQAB
-----END PUBLIC KEY-----