Skip to content
Snippets Groups Projects
Select Git revision
  • 0096f30d28bbe8ede33e52950f9ac31e28492614
  • master default protected
2 results

http-server.c

Blame
  • certvalidator.c 14.79 KiB
    /**
        Example certifcate code
        gcc -o certexample certexample.c -lssl -lcrypto
    */
    #include <openssl/x509.h>
    #include <openssl/asn1.h>
    #include <openssl/crypto.h>
    #include <openssl/asn1t.h>
    #include <openssl/x509v3.h>
    #include <openssl/bio.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include <time.h>
    #include <openssl/asn1_mac.h>
    #define DEBUG 0
    #define LINE_BUFFER 100
    
    const ASN1_TIME *X509_get0_notBefore(const X509 *x);
    char** str_split(const char* a_str, const char a_delim);
    char* concat(char *s1, char *s2);
    char* get_basic_constraints(X509 *cert);
    char* get_key_usage(X509 *cert);
    char* compare_not_before(X509 *cert);
    char* compare_not_after(X509 *cert);
    char* get_SAN(X509 *cert);
    char *get_domain_name(X509 *cert);
    int matches_subject_alternative_name(const char *hostname, X509 *server_cert);
    int get_public_key_length(X509 *cert);
    int validate_basic_constraints(char* basic_constraints);
    int validate_key_usage(char* key_usage);
    int validate_key_length(int length);
    int validate_CN(const char* hostname, char*cn);
    int validate_wildcard_string(const char *hostname, char*hostname_with_wildcard);
    int validate_CN_and_SAN(const char *url, X509 *cert);
    int validate_not_before(X509 *cert);
    int validate_not_after(X509 *cert);
    int validate_certificate(const char *url, X509 *cert);
    int find_first_instanceof(const char *str, char delim);
    char *str_slice_to_end(const char *str, int begin);
    
    int main(int argc, char **argv){
    
        char line[LINE_BUFFER];
        //open the file, create the file to write to
        FILE *csv_input = fopen(argv[1], "r");
        FILE *csv_output = fopen("output_test.csv" ,"w");
        //for each line in the csv file, process each certificate
        OpenSSL_add_all_algorithms();
        ERR_load_BIO_strings();
        ERR_load_crypto_strings();
    
        int n = 0;
        while (fgets(line, LINE_BUFFER, csv_input) != NULL){
    
            if(DEBUG){
                printf("CSV LINE # %d\n", n);
            }
    
            //init all the things we use to describe a certificate
            BIO *certificate_bio = NULL;
            X509 *cert = NULL;
            X509_NAME *cert_issuer = NULL;
            X509_CINF *cert_inf = NULL;
            STACK_OF(X509_EXTENSION) * ext_list;
            certificate_bio = BIO_new(BIO_s_file());
            //here we are able to access each line
    
            //get rid of newline
            line[strlen(line)-1] = '\0';
    
            //split the csv line up into its elements
            char **csv_row_elements = str_split(line, ',');
    
            if(DEBUG){
                printf("\tFILE: %s\n",csv_row_elements[0]);
                printf("\tURL: %s\n",csv_row_elements[1]);
            }
    
            char *certificate_file = csv_row_elements[0];
    
            const char *url = csv_row_elements[1];
            //for some reason splitting keeps mututating the original string
            char *unchanged_url = csv_row_elements[1];
    
            //open up the certificate file specifed by the line in the input csv
            FILE *fp = fopen(certificate_file, "r");
    
            if (!(BIO_read_filename(certificate_bio, certificate_file))){
                fprintf(stderr, "Error in reading cert BIO filename");
                exit(EXIT_FAILURE);
            }
            //load certiifcate
            if (!(cert = PEM_read_bio_X509(certificate_bio, NULL, 0, NULL))){
                fprintf(stderr, "Error in loading certificate");
                exit(EXIT_FAILURE);
            }
    
            if(DEBUG){
                printf ("\tBASIC CONSTRAINT: %s\n",get_basic_constraints(cert));
                printf ("\tBASIC CONSTRAINT VALIDATION: %d\n",validate_basic_constraints(get_basic_constraints(cert)));
                printf ("\tKEY USAGE: %s\n",get_key_usage(cert));
                printf ("\tKEY USAGE VALIDATION: %d\n",validate_key_usage(get_key_usage(cert)));
                printf ("\tKEY LENGTH BITS: %d\n",get_public_key_length(cert));
                printf ("\tKEY LENGTH VALIDATION: %d\n",validate_key_length(get_public_key_length(cert)));
                printf ("\tNot Before compared to Current:  %s\n",compare_not_before(cert));
                printf ("\tNot After compared to Current:  %s\n",compare_not_after(cert));
                printf("\tNOT BEFORE VALIDATION %d\n", validate_not_before(cert));
                printf("\tNOT AFTER VALIDATION %d\n", validate_not_after(cert));
                printf ("\tCommon Name:  %s\n",get_domain_name(cert));
                printf("\tCOMMON NAME AND SAN VALIDATION %d\n", validate_CN_and_SAN(url, cert));
                printf ("%d", 0 || 1);
                printf("FINAL VALIDATION %d\n", validate_certificate(url, cert));
                printf("\t%s\n", unchanged_url);
            }
    
            fprintf(csv_output,"%s,", csv_row_elements[0]);
            fprintf(csv_output,"%s,", unchanged_url);
            //print validation result
            fprintf(csv_output,"%d\n", validate_certificate(url, cert));
    
            //this is for debugging and printing out line numbers
            n++;
    
        }
        exit(0);
    }
    
    char* get_basic_constraints(X509 *cert){
        /*  -returns a string that represents the certificate's basic constraint, either CA is false or true
        -taken from the sample code and modified
        */
        X509_EXTENSION *ex = X509_get_ext(cert, X509_get_ext_by_NID(cert, NID_basic_constraints, -1));
    
        ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex);
    
        char buff[1024];
        OBJ_obj2txt(buff, 1024, obj, 0);
        BUF_MEM *bptr = NULL;
        char *buf = NULL;
    
        BIO *bio = BIO_new(BIO_s_mem());
        X509V3_EXT_print(bio, ex, 0, 0);
    
        BIO_flush(bio);
        BIO_get_mem_ptr(bio, &bptr);
    
        //bptr->data is not NULL terminated - add null character
        buf = (char *)malloc((bptr->length + 1) * sizeof(char));
        memcpy(buf, bptr->data, bptr->length);
        buf[bptr->length] = '\0';
        BIO_flush(bio);
        //Can print or parse value
    
        return buf;
    }
    char* get_key_usage(X509 *cert){
        /*  -returns a string that represents the certificate's key usage
            -taken from the sample code and modified
        */
    
        X509_EXTENSION *ex = X509_get_ext(cert, X509_get_ext_by_NID(cert, NID_ext_key_usage, -1));
    
        ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex);
    
        char buff[1024];
        OBJ_obj2txt(buff, 1024, obj, 0);
    
    
        BUF_MEM *bptr = NULL;
        char *buf = NULL;
    
        BIO *bio = BIO_new(BIO_s_mem());
    
        X509V3_EXT_print(bio, ex, 0, 0);
    
    
        BIO_flush(bio);
        BIO_get_mem_ptr(bio, &bptr);
    
        //bptr->data is not NULL terminated - add null character
        buf = (char *)malloc((bptr->length + 1) * sizeof(char));
        memcpy(buf, bptr->data, bptr->length);
        buf[bptr->length] = '\0';
    
        //Can print or parse value
    
        return buf;
    }
    int get_public_key_length(X509 *cert){
        /*Gets the length of the key and returns its size in bits*/
        EVP_PKEY *public_key = X509_get_pubkey(cert);
    
        RSA *rsa_key = EVP_PKEY_get1_RSA(public_key);
    
        int key_length_bytes = RSA_size(rsa_key);
    
        return (key_length_bytes*8);
    
    }
    char* get_domain_name(X509 *cert){
        //gets the common name for this certifate and returns it as a string
        X509_NAME *x509_name = X509_get_subject_name(cert);
        char *cert_cn = X509_NAME_oneline(x509_name, 0, 0);
        cert_cn =(cert_cn+1);
        char **chain = str_split(cert_cn, '/');
        char *domain = chain[5];
    
        char **domain_clean = str_split(domain, '=');
    
        return domain_clean[1];
    }
    char* compare_not_before(X509 *cert){
        // gets a certificate's not before date and compares it to the current date
        int day, sec;
        const ASN1_TIME *not_before = X509_get_notBefore(cert);
    
        if (!ASN1_TIME_diff(&day, &sec, NULL, not_before)){
            /* Invalid time format */
            return "invalid";
        }
    
        if (day > 0 || sec > 0){
            return "Later";
        }
        else if (day < 0 || sec < 0){
            return "Sooner";
        }
        else{
            return "Same";
        }
        return "invalid";
    }
    char* compare_not_after(X509 *cert){
        // gets a certificate's not after date and compares it to the current date
        int day, sec;
        const ASN1_TIME *not_after = X509_get_notAfter(cert);
    
        if (!ASN1_TIME_diff(&day, &sec, NULL, not_after)){
            /* Invalid time format */
            printf("what the fuck do now\n");
        }
    
        if (day > 0 || sec > 0){
            return "Later";
        }
        else if (day < 0 || sec < 0){
            return "Sooner";
        }
        else{
            return "Same";
        }
    
        return "invalid?";
    }
    char* concat(char *s1, char *s2){
    	//concats two strings
        char *result = malloc(strlen(s1)+strlen(s2)+1);
        strcpy(result, s1);
        strcat(result, s2);
        return result;
    
    }
    char** str_split(const char* a_str, const char a_delim){
        //splits up string by a delimiter
        char** result = 0;
        size_t count = 0;
        char* tmp = (char *)a_str;
        char* copy = (char *)a_str;
        char* last_comma = 0;
        char delim[2];
        delim[0] = a_delim;
        delim[1] = 0;
    
        /* Count how many elements will be extracted. */
        while (*tmp){
            if (a_delim == *tmp){
                count++;
                last_comma = tmp;
            }
            tmp++;
        }
        /* Add space for trailing token. */
        count += last_comma < (a_str + strlen(a_str) - 1);
        /* Add space for terminating null string so caller
           knows where the list of returned strings ends. */
        count++;
        result = malloc(sizeof(char*) * count);
        if (result){
            size_t idx  = 0;
            char* token = strtok(copy, delim);
    
            while (token){
                assert(idx < count);
                *(result + idx++) = strdup(token);
                token = strtok(0, delim);
            }
            assert(idx == count - 1);
            *(result + idx) = 0;
        }
        return result;
    }
    int matches_subject_alternative_name(const char *hostname, X509 *server_cert) {
        //goes through the alternative domain names for a certifate and validates each one
        int i;
        int san_names_nb = -1;
        STACK_OF(GENERAL_NAME) *san_names = NULL;
    
        // Try to extract the names within the SAN extension from the certificate
        san_names = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
    
    
        if (san_names == NULL) {
    
            return 0;
        }
    
        san_names_nb = sk_GENERAL_NAME_num(san_names);
    
        // Check each name within the extension
        for (i=0; i<san_names_nb; i++) {
            const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
    
            if (current_name->type == GEN_DNS) {
                // Current name is a DNS name, let's check it
                char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
                printf ("\t%s\n", dns_name);
                // Make sure there isn't an embedded NUL character in the DNS name
                if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
    
                    return 0;
                    break;
                }
                else { // Compare expected hostname with the DNS name
    
    
    
                    if (validate_wildcard_string(hostname, dns_name) == 1) {
    
                        return 1;
    
                        break;
                    }
                }
            }
        }
        sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
        return 0;
    }
    int validate_key_usage(char* key_usage){
        //gets the key usage as a string from the helper function above, and then validates if it is equal to "TLS Web Server Authentication""
        //key usage may be a bunch of strings, need to get first one if this is the case
    
        if(strlen("TLS Web Server Authentication")!=strlen(key_usage)){
            char **key_usage_clean = str_split(key_usage, ',');
            if(strcmp(key_usage_clean[0], "TLS Web Server Authentication") == 0){
                return 1;
            }
    
            return 0;
    
    
        }
    
        else{
            if(strcmp(key_usage, "TLS Web Server Authentication") == 0){
                return 1;
            }
            return 0;
        }
    
    }
    int validate_basic_constraints(char* basic_constraints){
        //gets the basic constraint from a helper function above and then validates if it is "CA: FALSE"
        if(strcmp(basic_constraints, "CA:FALSE")==0){
            // printf("\tBASIC CONSTRAINT PASS\n");
            return 1;
        }
        return 0;
    }
    int validate_wildcard_string(const char *hostname, char*hostname_with_wildcard){
        //compares a domain with a wildcard with a given url, the wildcard is stripped of its '*' and '.'
        //the string is then compared with the url (also having all things left from the initial '.' removed)
    
        char *hostname_with_wildcard_sliced = str_slice_to_end(hostname_with_wildcard, (find_first_instanceof(hostname_with_wildcard, '.')));
        char *hostname_sliced = str_slice_to_end(hostname, (find_first_instanceof(hostname, '.')));
    
        if(strcasecmp(hostname_with_wildcard_sliced, hostname_sliced)==0){
    
            if (DEBUG){
                printf("\t\tWILDCARD FUNCTION\t\t%s == %s\n", hostname_with_wildcard_sliced, hostname_sliced);
            }
            return 1;
        }
        return 0;
    }
    int validate_key_length(int length){
        //validates whether or not the certifate's key is 2048 bits
        if (length==2048){
            return 1;
        }
        return 0;
    }
    int validate_CN(const char* hostname, char*cn){
        //matches the common name with the given url, if a wildcard is present, it gives the strings to the wildcard validator
        if(cn[0]=='*'){
            return (validate_wildcard_string(hostname, cn));
        }
        else{
            if(strcasecmp(cn, hostname)==0){
                return 1;
            }
            else{
                return 0;
            }
        }
        return 0;
    }
    int validate_CN_and_SAN(const char *url, X509 *cert){
        //gets the result from both the CN and SAN validator and returns true if either are valid
    
        if(validate_CN(url, get_domain_name(cert)) || matches_subject_alternative_name(url, cert)){
            return 1;
        }
        else{return 0;}
    }
    int validate_not_before(X509 *cert){
        //validates the not before date to be consistent with the current time
        if(strcmp(compare_not_before(cert), "Sooner")==0){
            return 1;
        }
        return 0;
    }
    int validate_not_after(X509 *cert){
        //validates the not after date to be consistent with the current time
        if(strcmp(compare_not_after(cert), "Later")==0){
            return 1;
        }
        return 0;
    }
    int validate_certificate(const char *url, X509 *cert){
        //the final validation decision, takes all of the validation results from each aspect of the certificate
        //and makes sure there are no invalid components
        int a =  validate_basic_constraints(get_basic_constraints(cert));
        int b = validate_key_usage(get_key_usage(cert));
        int c = validate_key_length(get_public_key_length(cert));
        int d = validate_not_before(cert);
        int e = validate_not_after(cert);
        int f = validate_CN_and_SAN(url, cert);
        //if any of these is invalid, the certificate is invalid
        if(a & b & c & d & e & f){
            return 1;
        }
        else{
            return 0;
        }
    }
    int find_first_instanceof(const char *str, char delim){
        //gets the first instance of a char in a string
        int i;
        for (i=0;i<=strlen(str);i++){
            if(str[i]==delim){
    
              return i;
            }
        }
        return -1;
    }
    char *str_slice_to_end(const char *str, int begin){
        //gets rid of things left of the index(inclusive) in a string
        //eg str_slice_to_end("www.example.com",3) becomes example.com
    
        char *tmp = (char *)str;
        tmp = (tmp+=begin+1);
        return tmp;
    
    
    }