diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d58646..01a3ece 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,7 +15,7 @@ set(SOURCES main.c mutex.c networking.c nopoll_helpers.c heartBeat.c nopoll_hand 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 - crud_interface.c crud_tasks.c crud_internal.c close_retry.c) + crud_interface.c crud_tasks.c crud_internal.c close_retry.c auth_token.c) if (ENABLE_SESHAT) set(SOURCES ${SOURCES} seshat_interface.c) diff --git a/src/auth_token.c b/src/auth_token.c new file mode 100644 index 0000000..ce24df9 --- /dev/null +++ b/src/auth_token.c @@ -0,0 +1,319 @@ +/** + * Copyright 2015 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. + * + */ +/** + * @file auth_token.c + * + * @description This file is to fetch authorization token during parodus cloud connection. + * + */ + +#include +#include +#include "config.h" +#include "auth_token.h" +#include "ParodusInternal.h" +#include +#include +#include +#include + +#define MAX_BUF_SIZE 128 +#define CURL_TIMEOUT_SEC 25L +#define MAX_CURL_RETRY_COUNT 3 +/*----------------------------------------------------------------------------*/ +/* File Scoped Variables */ +/*----------------------------------------------------------------------------*/ +void createCurlheader(char *mac_header, char *serial_header, char *uuid_header, char *transaction_uuid, struct curl_slist *list, struct curl_slist **header_list); +/*----------------------------------------------------------------------------*/ +/* External Functions */ +/*----------------------------------------------------------------------------*/ +/* +* @brief Initialize curl object with required options. create newToken using libcurl. +* @param[out] newToken auth token string obtained from JWT curl response +* @param[in] len total token size +* @param[in] r_count Number of curl retries on ipv4 and ipv6 mode during failure +* @return returns 0 if success, otherwise failed to fetch auth token and will be retried. +*/ + +int requestNewAuthToken(char *newToken, size_t len, int r_count) +{ + CURL *curl; + CURLcode res; + CURLcode time_res; + struct curl_slist *list = NULL; + struct curl_slist *headers_list = NULL; + + char *mac_header = NULL; + char *serial_header = NULL; + char *uuid_header = NULL; + char *transaction_uuid = NULL; + double total; + long response_code; + + struct token_data data; + data.size = 0; + + curl = curl_easy_init(); + if(curl) + { + //this memory will be dynamically grown by write call back fn as required + data.data = (char *) malloc(sizeof(char) * 1); + if(NULL == data.data) + { + ParodusError("Failed to allocate memory.\n"); + return -1; + } + data.data[0] = '\0'; + + createCurlheader(mac_header, serial_header, uuid_header, transaction_uuid, list, &headers_list); + + curl_easy_setopt(curl, CURLOPT_URL, get_parodus_cfg()->token_server_url); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT_SEC); + + if(get_parodus_cfg()->webpa_interface_used !=NULL && strlen(get_parodus_cfg()->webpa_interface_used) >0) + { + curl_easy_setopt(curl, CURLOPT_INTERFACE, get_parodus_cfg()->webpa_interface_used); + } + /* set callback for writing received data */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_fn); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); + + /* setting curl resolve option as default mode. + If any failure, retry with v4 first and then v6 mode. */ + + if(r_count == 1) + { + ParodusInfo("curl Ip resolve option set as V4 mode\n"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + else if(r_count == 2) + { + ParodusInfo("curl Ip resolve option set as V6 mode\n"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + } + else + { + ParodusInfo("curl Ip resolve option set as default mode\n"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); + } + + /* set the cert for client authentication */ + curl_easy_setopt(curl, CURLOPT_SSLCERT, get_parodus_cfg()->client_cert_path); + + curl_easy_setopt(curl, CURLOPT_CAINFO, get_parodus_cfg()->cert_path); + + /* disconnect if it is failed to validate server's cert */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + ParodusInfo("themis curl response %d http_code %d\n", res, response_code); + + time_res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total); + if(time_res == 0) + { + ParodusInfo("curl response Time: %.1f seconds\n", total); + } + curl_slist_free_all(headers_list); + if(res != 0) + { + ParodusError("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + curl_easy_cleanup(curl); + if(data.data) + { + free(data.data); + data.data = NULL; + } + return -1; + } + else + { + if(response_code == 200) + { + ParodusInfo("cURL success\n"); + strncpy(newToken, data.data, len); + } + } + if(data.data) + { + free(data.data); + data.data = NULL; + } + curl_easy_cleanup(curl); + } + else + { + ParodusError("curl init failure\n"); + return -1; + } + + return 0; +} + +/* +* @brief Fetches authorization token and update to parodus config. + This will do curl retry in case of any failure till it reaches max curl retry count. + * @param[in] cfg Global parodus config structure to update webpa_auth_token +*/ + +void getAuthToken(ParodusCfg *cfg) +{ + int status = -1; + int retry_count = 0; + + if( cfg->hw_mac != NULL && strlen(cfg->hw_mac) !=0 ) + { + if( cfg->client_cert_path !=NULL && strlen(cfg->client_cert_path) !=0 ) + { + while(1) + { + //Fetch new auth token using libcurl + status = requestNewAuthToken(cfg->webpa_auth_token, sizeof(cfg->webpa_auth_token), retry_count); + if(status == 0) + { + ParodusInfo("cfg->webpa_auth_token created successfully\n"); + break; + } + else + { + ParodusError("Failed to create new token\n"); + retry_count++; + ParodusError("Curl execution is failed, retry attempt: %d\n", retry_count); + } + + if(retry_count == MAX_CURL_RETRY_COUNT) + { + ParodusError("Curl retry is reached to max %d attempts, proceeding without token\n", retry_count); + break; + } + } + } + else + { + ParodusError("client_cert_path is NULL, failed to fetch auth token\n"); + } + } + else + { + ParodusError("hw_mac is NULL, failed to fetch auth token\n"); + } +} + +/* @brief callback function for writing libcurl received data + * @param[in] buffer curl delivered data which need to be saved. + * @param[in] size size is always 1 + * @param[in] nmemb size of delivered data + * @param[out] data curl response data saved. +*/ +size_t write_callback_fn(void *buffer, size_t size, size_t nmemb, struct token_data *data) +{ + size_t index = data->size; + size_t n = (size * nmemb); + char* tmp; + + data->size += (size * nmemb); + + tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */ + + if(tmp) { + data->data = tmp; + } else { + if(data->data) { + free(data->data); + } + ParodusError("Failed to allocate memory for data\n"); + return 0; + } + + memcpy((data->data + index), buffer, n); + data->data[data->size] = '\0'; + + return size * nmemb; +} + +/* @brief function to generate random uuid. +*/ +char* generate_trans_uuid() +{ + char *transID = NULL; + uuid_t transaction_Id; + char *trans_id = NULL; + trans_id = (char *)malloc(37); + uuid_generate_random(transaction_Id); + uuid_unparse(transaction_Id, trans_id); + + if(trans_id !=NULL) + { + transID = trans_id; + } + return transID; +} + +/* @brief function to create curl header contains mac, serial number and uuid. + * @param[in] mac_header mac address header key value pair + * @param[in] serial_header serial number key value pair + * @param[in] uuid_header transaction uuid key value pair + * @param[in] list temp curl header list + * @param[out] header_list output curl header list +*/ +void createCurlheader(char *mac_header, char *serial_header, char *uuid_header, char *transaction_uuid, struct curl_slist *list, struct curl_slist **header_list) +{ + mac_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); + if(mac_header !=NULL) + { + snprintf(mac_header, MAX_BUF_SIZE, "X-Midt-Mac-Address: %s", get_parodus_cfg()->hw_mac); + ParodusPrint("mac_header formed %s\n", mac_header); + list = curl_slist_append(list, mac_header); + free(mac_header); + mac_header = NULL; + } + + serial_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); + if(serial_header !=NULL) + { + snprintf(serial_header, MAX_BUF_SIZE, "X-Midt-Serial-Number: %s", get_parodus_cfg()->hw_serial_number); + ParodusPrint("serial_header formed %s\n", serial_header); + list = curl_slist_append(list, serial_header); + free(serial_header); + serial_header = NULL; + } + + transaction_uuid = generate_trans_uuid(); + if(transaction_uuid !=NULL) + { + uuid_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); + if(uuid_header !=NULL) + { + snprintf(uuid_header, MAX_BUF_SIZE, "X-Midt-Uuid: %s", transaction_uuid); + ParodusInfo("uuid_header formed %s\n", uuid_header); + list = curl_slist_append(list, uuid_header); + free(transaction_uuid); + transaction_uuid = NULL; + free(uuid_header); + uuid_header = NULL; + } + } + else + { + ParodusError("Failed to generate transaction_uuid\n"); + } + *header_list = list; +} diff --git a/src/auth_token.h b/src/auth_token.h new file mode 100644 index 0000000..905d527 --- /dev/null +++ b/src/auth_token.h @@ -0,0 +1,57 @@ +/** + * Copyright 2015 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. + * + */ +/** + * @file auth_token.h + * + * @description This file is to fetch authorization token during parodus cloud connection. + * + */ + +#ifndef _AUTH_TOKEN_H_ +#define _AUTH_TOKEN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------------------------*/ +/* Macros */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Data Structures */ +/*----------------------------------------------------------------------------*/ + +struct token_data { + size_t size; + char* data; +}; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +int requestNewAuthToken(char *newToken, size_t len, int r_count); +void getAuthToken(ParodusCfg *cfg); +size_t write_callback_fn(void *buffer, size_t size, size_t nmemb, struct token_data *data); +char* generate_trans_uuid(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/config.c b/src/config.c index 153f832..51a7a4d 100644 --- a/src/config.c +++ b/src/config.c @@ -30,9 +30,6 @@ #include #include -#define MAX_BUF_SIZE 128 -#define CURL_TIMEOUT_SEC 25L -#define MAX_CURL_RETRY_COUNT 3 /*----------------------------------------------------------------------------*/ /* File Scoped Variables */ /*----------------------------------------------------------------------------*/ @@ -40,7 +37,6 @@ static ParodusCfg parodusCfg; static unsigned int rsa_algorithms = (1<token_server_url); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT_SEC); - - if(get_parodus_cfg()->webpa_interface_used !=NULL && strlen(get_parodus_cfg()->webpa_interface_used) >0) - { - curl_easy_setopt(curl, CURLOPT_INTERFACE, get_parodus_cfg()->webpa_interface_used); - } - /* set callback for writing received data */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_fn); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); - - /* setting curl resolve option as default mode. - If any failure, retry with v4 first and then v6 mode. */ - - if(r_count == 1) - { - ParodusInfo("curl Ip resolve option set as V4 mode\n"); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - } - else if(r_count == 2) - { - ParodusInfo("curl Ip resolve option set as V6 mode\n"); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); - } - else - { - ParodusInfo("curl Ip resolve option set as default mode\n"); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); - } - - /* set the cert for client authentication */ - curl_easy_setopt(curl, CURLOPT_SSLCERT, get_parodus_cfg()->client_cert_path); - - curl_easy_setopt(curl, CURLOPT_CAINFO, get_parodus_cfg()->cert_path); - - /* disconnect if it is failed to validate server's cert */ - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); - ParodusInfo("themis curl response %d http_code %d\n", res, response_code); - - time_res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total); - if(time_res == 0) - { - ParodusInfo("curl response Time: %.1f seconds\n", total); - } - curl_slist_free_all(headers_list); - if(res != 0) - { - ParodusError("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - curl_easy_cleanup(curl); - if(data.data) - { - free(data.data); - data.data = NULL; - } - return -1; - } - else - { - if(response_code == 200) - { - ParodusInfo("cURL success\n"); - strncpy(newToken, data.data, len); - } - } - if(data.data) - { - free(data.data); - data.data = NULL; - } - curl_easy_cleanup(curl); - } - else - { - ParodusError("curl init failure\n"); - return -1; - } - - return 0; -} - -/* -* @brief Fetches authorization token and update to parodus config. - This will do curl retry in case of any failure till it reaches max curl retry count. - * @param[in] cfg Global parodus config structure to update webpa_auth_token -*/ - -void getAuthToken(ParodusCfg *cfg) -{ - int status = -1; - int retry_count = 0; - - if( cfg->hw_mac != NULL && strlen(cfg->hw_mac) !=0 ) - { - if( cfg->client_cert_path !=NULL && strlen(cfg->client_cert_path) !=0 ) - { - while(1) - { - //Fetch new auth token using libcurl - status = requestNewAuthToken(cfg->webpa_auth_token, sizeof(cfg->webpa_auth_token), retry_count); - if(status == 0) - { - ParodusInfo("cfg->webpa_auth_token created successfully\n"); - break; - } - else - { - ParodusError("Failed to create new token\n"); - retry_count++; - ParodusError("Curl execution is failed, retry attempt: %d\n", retry_count); - } - - if(retry_count == MAX_CURL_RETRY_COUNT) - { - ParodusError("Curl retry is reached to max %d attempts, proceeding without token\n", retry_count); - break; - } - } - } - else - { - ParodusError("client_cert_path is NULL, failed to fetch auth token\n"); - } - } - else - { - ParodusError("hw_mac is NULL, failed to fetch auth token\n"); - } -} - -/* @brief callback function for writing libcurl received data - * @param[in] buffer curl delivered data which need to be saved. - * @param[in] size size is always 1 - * @param[in] nmemb size of delivered data - * @param[out] data curl response data saved. -*/ -size_t write_callback_fn(void *buffer, size_t size, size_t nmemb, struct token_data *data) -{ - size_t index = data->size; - size_t n = (size * nmemb); - char* tmp; - - data->size += (size * nmemb); - - tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */ - - if(tmp) { - data->data = tmp; - } else { - if(data->data) { - free(data->data); - } - ParodusError("Failed to allocate memory for data\n"); - return 0; - } - - memcpy((data->data + index), buffer, n); - data->data[data->size] = '\0'; - - return size * nmemb; -} - -/* @brief function to generate random uuid. -*/ -char* generate_trans_uuid() -{ - char *transID = NULL; - uuid_t transaction_Id; - char *trans_id = NULL; - trans_id = (char *)malloc(37); - uuid_generate_random(transaction_Id); - uuid_unparse(transaction_Id, trans_id); - - if(trans_id !=NULL) - { - transID = trans_id; - } - return transID; -} - -/* @brief function to create curl header contains mac, serial number and uuid. - * @param[in] mac_header mac address header key value pair - * @param[in] serial_header serial number key value pair - * @param[in] uuid_header transaction uuid key value pair - * @param[in] list temp curl header list - * @param[out] header_list output curl header list -*/ -void createCurlheader(char *mac_header, char *serial_header, char *uuid_header, char *transaction_uuid, struct curl_slist *list, struct curl_slist **header_list) -{ - mac_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); - if(mac_header !=NULL) - { - snprintf(mac_header, MAX_BUF_SIZE, "X-Midt-Mac-Address: %s", get_parodus_cfg()->hw_mac); - ParodusPrint("mac_header formed %s\n", mac_header); - list = curl_slist_append(list, mac_header); - free(mac_header); - mac_header = NULL; - } - - serial_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); - if(serial_header !=NULL) - { - snprintf(serial_header, MAX_BUF_SIZE, "X-Midt-Serial-Number: %s", get_parodus_cfg()->hw_serial_number); - ParodusPrint("serial_header formed %s\n", serial_header); - list = curl_slist_append(list, serial_header); - free(serial_header); - serial_header = NULL; - } - - transaction_uuid = generate_trans_uuid(); - if(transaction_uuid !=NULL) - { - uuid_header = (char *) malloc(sizeof(char)*MAX_BUF_SIZE); - if(uuid_header !=NULL) - { - snprintf(uuid_header, MAX_BUF_SIZE, "X-Midt-Uuid: %s", transaction_uuid); - ParodusInfo("uuid_header formed %s\n", uuid_header); - list = curl_slist_append(list, uuid_header); - free(transaction_uuid); - transaction_uuid = NULL; - free(uuid_header); - uuid_header = NULL; - } - } - else - { - ParodusError("Failed to generate transaction_uuid\n"); - } - *header_list = list; -} - void setDefaultValuesToCfg(ParodusCfg *cfg) { if(cfg == NULL) diff --git a/src/config.h b/src/config.h index 142e704..71cd99d 100644 --- a/src/config.h +++ b/src/config.h @@ -107,11 +107,6 @@ typedef struct unsigned int boot_retry_wait; } ParodusCfg; -struct token_data { - size_t size; - char* data; -}; - #define FLAGS_IPV6_ONLY (1 << 0) #define FLAGS_IPV4_ONLY (1 << 1) #define FLAGS_IPV6_IPV4 (FLAGS_IPV6_ONLY | FLAGS_IPV4_ONLY) @@ -121,7 +116,6 @@ struct token_data { /*----------------------------------------------------------------------------*/ void loadParodusCfg(ParodusCfg * config,ParodusCfg *cfg); -int requestNewAuthToken(char *newToken, size_t len, int r_count); /** * parse command line arguments and create config structure @@ -135,7 +129,6 @@ int requestNewAuthToken(char *newToken, size_t len, int r_count); int parseCommandLine(int argc,char **argv,ParodusCfg * cfg); void setDefaultValuesToCfg(ParodusCfg *cfg); -void getAuthToken(ParodusCfg *cfg); // Accessor for the global config structure. ParodusCfg *get_parodus_cfg(void); void set_parodus_cfg(ParodusCfg *); @@ -156,9 +149,6 @@ void reset_cloud_disconnect_reason(ParodusCfg *cfg); */ int parse_webpa_url (const char *full_url, char **server_addr, unsigned int *port); -size_t write_callback_fn(void *buffer, size_t size, size_t nmemb, struct token_data *data); -char* generate_trans_uuid(); - #ifdef __cplusplus } #endif diff --git a/src/connection.c b/src/connection.c index 5c9cbfa..901eaef 100644 --- a/src/connection.c +++ b/src/connection.c @@ -25,6 +25,7 @@ #include "time.h" #include "token.h" #include "config.h" +#include "auth_token.h" #include "nopoll_helpers.h" #include "mutex.h" #include "spin_thread.h" diff --git a/src/main.c b/src/main.c index 9d2e291..b898306 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,7 @@ #include #include "stdlib.h" #include "config.h" +#include "auth_token.h" #include "conn_interface.h" #include "parodus_log.h" #include