Commit 2856b267 authored by Cage, Gregory's avatar Cage, Gregory
Browse files

Refactor code to improve readability and ease of testing

parent 0fae5c9d
Loading
Loading
Loading
Loading
Loading

server_side/README.md

0 → 100644
+1 −0
Original line number Diff line number Diff line
TODO
+106 −135
Original line number Diff line number Diff line
@@ -15,15 +15,10 @@
#include "cjwt/cjwt.h"
#include "log.h"

struct MemoryStruct {
    char *memory;
    size_t size;
};

static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *) userp;
    memory_struct *mem = (memory_struct *) userp;

    char *ptr = realloc(mem->memory, mem->size + realsize + 1);
    if (!ptr) {
@@ -41,78 +36,36 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
}

int verify_token(const char *token, oidc_token_content_t *token_info) {
    CURL *curl;
    CURLcode res;
    struct MemoryStruct chunk;
    const unsigned char *key= NULL;
    const char *token_kid = NULL;
    const cJSON *key_itr = NULL;
    const cJSON *keyset = NULL;
    const cJSON *kid = NULL;
    const cJSON *user = NULL;
    cJSON *keyset_json = NULL;
    cjwt_t *jwt = NULL;
    cjwt_header_t *jwt_header = NULL;
    cjwt_code_t cjwt_return_value = CJWTE_OK;
    
    cJSON *keyset_json = fetch_jwks();

    chunk.memory = malloc(1);  /* will be grown as needed by realloc above */
    chunk.size = 0;    /* no data at this point */

    // Get Key Id from token
    cjwt_return_value = cjwt_get_header(token, strlen(token), OPT_ALLOW_ANY_TIME, &jwt_header);
    if(CJWTE_OK != cjwt_return_value) {
        logit("Could not get KID: %s\n", token);
        free(chunk.memory);
        cjwt_header_destroy(jwt_header);
        return 1;
    }
    token_kid = jwt_header->kid;


    // Fetch Jwks to decrypt token
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    long http_code = 0;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, config.jwks_url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
        curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0);
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            logit("curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
            cjwt_header_destroy(jwt_header);
            return 1;
        } else {
            curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
        }
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    if (http_code != 200) {
        free(chunk.memory);
        cjwt_header_destroy(jwt_header);
        return 1;
    }


    keyset_json = cJSON_Parse(chunk.memory);
    if (keyset_json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            logit("Error before: %s\n", error_ptr);
        }
        free(chunk.memory);
        return 1;
    }

    // Get Key Id from token
    cjwt_header_t *jwt_header = NULL;

    cjwt_code_t cjwt_return_value = cjwt_get_header(token, strlen(token), OPT_ALLOW_ANY_TIME, &jwt_header);

    if(CJWTE_OK != cjwt_return_value) {
        logit("Could not get KID: %s\n", token);
        cjwt_header_destroy(jwt_header);
        return 1;
    }

    const char *token_kid = jwt_header->kid;

    // Search for correct key
    keyset = cJSON_GetObjectItemCaseSensitive(keyset_json, "keys");
    const cJSON *keyset = cJSON_GetObjectItemCaseSensitive(keyset_json, "keys");

    const unsigned char *key = NULL;
    const cJSON *key_itr = NULL;
    const cJSON *kid = NULL;    
    cJSON_ArrayForEach(key_itr, keyset)
    {
        kid = cJSON_GetObjectItemCaseSensitive(key_itr, "kid");
@@ -130,43 +83,31 @@ int verify_token(const char *token, oidc_token_content_t *token_info) {
    // Handle not finding the correct key
    if (!key) {
        logit("Could not find correct key in keyset. Token: %s\n", token);
        free(chunk.memory);
        return 1;
    }

    // Load Cert here
    char* loaded_cert = load_cert(key);

    cJSON_Delete(keyset_json);
    if (loaded_cert == NULL) {
        logit("Loaded cert is null. Token: %s\n", token);
        free(chunk.memory);
        cJSON_Delete(keyset_json);
        return 1;
    }

    // Actually validate token now
    cjwt_t *jwt = NULL;
    cjwt_return_value = cjwt_decode(token, strlen(token), 0, (uint8_t *)loaded_cert, strlen(loaded_cert), time(NULL), 0, &jwt);

    free(loaded_cert);
    if (CJWTE_OK != cjwt_return_value) {
        logit("There was an issue while decoding token: %d\n", cjwt_return_value);
        // free memory
        free(chunk.memory);
        free(loaded_cert);
        // This should recursively free all CJSON stuff from JWKS
        cJSON_Delete(keyset_json);
        cjwt_destroy(jwt);
        return 1;
    }

    user = cJSON_GetObjectItemCaseSensitive(jwt->private_claims, "preferred_username");
    const cJSON *user = cJSON_GetObjectItemCaseSensitive(jwt->private_claims, "preferred_username");

    if (!cJSON_IsString(user) || (user->valuestring == NULL)) {
        logit("Could not find 'preferred_username' claim.\n");
        // free memory
        free(chunk.memory);
        free(loaded_cert);
        // This should recursively free all CJSON stuff from JWKS
        cJSON_Delete(keyset_json);
        cjwt_destroy(jwt);
        return 1;
    }
@@ -174,22 +115,51 @@ int verify_token(const char *token, oidc_token_content_t *token_info) {
    token_info->user = malloc(strlen(user->valuestring));
    strcpy(token_info->user, user->valuestring);

    // free memory
    free(chunk.memory);
    free(loaded_cert);
    // This should recursively free all CJSON stuff from JWKS
    cJSON_Delete(keyset_json);
    cjwt_destroy(jwt);

    return 0;
}

char *load_cert(const char* x509) {
cJSON* fetch_jwks() {
    memory_struct mem;

        EVP_PKEY *pkey = NULL;
        BIO *certbio = NULL;
        BIO *keybio = NULL;
        X509 *cert = NULL;
    mem.memory = malloc(1);  /* will be grown as needed by realloc above */
    mem.size = 0;    /* no data at this point */

    // Fetch Jwks to decrypt token
    CURL *curl;
    CURLcode res;
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    long http_code = 0;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, config.jwks_url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &mem);
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
        curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0);
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            logit("curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
            return NULL;
        } else {
            curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
        }
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
    if (http_code != 200) {
        free(mem.memory);
        return NULL;
    }

    cJSON *keyset_json = cJSON_Parse(mem.memory);
    free(mem.memory);
    return keyset_json;
}

char *load_cert(const char* x509) {

    /* ---------------------------------------------------------- *
    * These function calls initialize openssl for correct work.  *
@@ -206,7 +176,8 @@ char *load_cert(const char* x509) {
    strcat(x509_formatted, x509);
    strcat(x509_formatted, footer);

        certbio = BIO_new(BIO_s_mem());
    X509 *cert = NULL;
    BIO *certbio = BIO_new(BIO_s_mem());
    BIO_write(certbio, x509_formatted, strlen(x509_formatted) + 1);
    if (! (cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
        logit("Error reading cert into memory. x509: %s\n", x509_formatted);
@@ -214,29 +185,29 @@ char *load_cert(const char* x509) {
        free(x509_formatted);
        return NULL;
    }
    BIO_free_all(certbio);

    free(x509_formatted);

    EVP_PKEY *pkey = NULL;
    if ((pkey = X509_get_pubkey(cert)) == NULL) {
        logit("Error getting public key.\n");
            BIO_free_all(certbio);
        X509_free(cert);
        return NULL;
    }
        keybio = BIO_new(BIO_s_mem());
    X509_free(cert);
    BIO *keybio = BIO_new(BIO_s_mem());
    if(!PEM_write_bio_PUBKEY(keybio, pkey)) {
        logit("Error writing public key data in PEM format\n");
            BIO_free_all(certbio);
        EVP_PKEY_free(pkey);
            X509_free(cert);
        return NULL;
    }
    char* key_buf = (char*) malloc(EVP_PKEY_bits(pkey) + 1);
    memset(key_buf, 0, EVP_PKEY_bits(pkey) + 1);
    BIO_read(keybio, key_buf, EVP_PKEY_bits(pkey));

    EVP_PKEY_free(pkey);
        X509_free(cert);
        BIO_free_all(certbio);
    BIO_free_all(keybio);

    return key_buf;
}
 No newline at end of file
+7 −0
Original line number Diff line number Diff line
@@ -3,6 +3,11 @@

#include "config.h"

typedef struct MemoryStruct {
    char *memory;
    size_t size;
} memory_struct;

typedef struct oidc_token_content_t
{
    char *user;
@@ -12,6 +17,8 @@ typedef struct oidc_token_content_t

int verify_token(const char* token, oidc_token_content_t *token_info);

cJSON* fetch_jwks();

char *load_cert(const char* x509);


+16 −14
Original line number Diff line number Diff line
@@ -5,19 +5,21 @@
#include "auth.h"

int main(int argc, char *argv[]) {
    // int res = parse_config("/Users/35y/projects/ndip/oidc-pam/server_side/oidc-pam.json", &config);
    // printf("res: %d\n",res);
    // if (res ==1) {
    //     exit(1);
    // }
    // printf("%s %s %s %d\n",config.client_id,config.client_secret,config.introspection_url,config.enable_2fa);
    printf("args: %s %s\n", argv[1], argv[2]);

    // oidc_token_content_t token_info;
    // res = introspect_token("sssss", &token_info);
    // printf("active: %d\n",token_info.active);
    // if (res == 1) {
    //     exit(1);
    // }
    // cJSON_Delete(config.parsed_object);
    // cJSON_Delete(token_info.parsed_object);
    int res = parse_config(argv[1], &config);
    printf("res: %d\n",res);
    if (res ==1) {
        exit(1);
    }
    printf("%s %s %d\n",config.jwks_url,config.log_file,config.enable_2fa);

    oidc_token_content_t token_info;
    res = verify_token(argv[2], &token_info);
    printf("user: %s\n",token_info.user);
    if (res == 1) {
        exit(1);
    }
    cJSON_Delete(config.parsed_object);
    free(token_info.user);
}
+6 −0
Original line number Diff line number Diff line
{
    "jwks_url": "https://login.microsoftonline.com/db3dbd43-4c4b-4544-9f8a-0553f9f5f25e/discovery/v2.0/keys",
    "check_2fa": false,
    "enable_log": true,
    "log_file": "/home/gzi/tmp/oidc.log"
}