[pisa-src] r2902 - trunk/community-operator/client/pisa-cert-info.c

  • From: Nikou Gholizadeh <nikou.gholizadeh@xxxxxxxxxxxxxx>
  • To: pisa-src@xxxxxxxxxxxxx
  • Date: Mon, 27 Feb 2012 14:36:03 +0100

Author: nikou
Date: Mon Feb 27 14:36:00 2012
New Revision: 2902

Log:
tool to extraxt certification information

Added:
   trunk/community-operator/client/pisa-cert-info.c

Added: trunk/community-operator/client/pisa-cert-info.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/community-operator/client/pisa-cert-info.c    Mon Feb 27 14:36:00 
2012        (r2902)
@@ -0,0 +1,534 @@
+/**
+ * @file
+ * This program will determine whether certain properties of a certificate 
(which is
+ *  contained in a file) are valid. Currently, only looking at the dates 
between which
+ *  the certificate is deemed valid (and comparing them to the current system 
time) is
+ *  implemented.
+ *
+ * @brief check a certificate according to a specified property
+ * @author Christoph Viethen <christoph.viethen@xxxxxxxxxxxxxx>
+ */
+#include <inttypes.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+//#include "cryptlib.h"
+//#include <openssl/buffer.h>
+//#include <openssl/bn.h>
+#ifndef NO_RSA
+//#include <openssl/rsa.h>
+#endif
+#ifndef NO_DSA
+//#include <openssl/dsa.h>
+#endif
+//#include <openssl/objects.h>
+//#include <openssl/x509v3.h>
+
+/**
+ * calculates a correction value that needs to be added to the second parameter
+ *  of X509_cmp_time() in case we are using a buggy version of OpenSSL
+ *
+ * It is assumed that asn1_time points to an ASN1_TIME structure that has been
+ *  verified to be correct (e.g. by ASN1_TIME_check()).
+ *
+ * @param asn1_time   the time struct (first param to X509_cmp_time()) that we 
want to compare against later
+ * @return            correction value, which may be pos., neg., or 0
+ */
+
+int X509_print_fp_cop(FILE *fp, X509 *x);
+int X509_print_cop(BIO *bp, X509 *x);
+
+static void t1(BIO *bp) { BIO_write(bp,"<tr><td>",8); }
+static void t2(BIO *bp) { BIO_write(bp,"</td><td>",9); }
+static void t3(BIO *bp) { BIO_write(bp,"</td></tr>\n",11); }
+
+static time_t calculate_correction(ASN1_TIME *asn1_time)
+{
+    time_t offset        = 0;
+
+    char *asn1_time_data = 0;
+
+    if ((asn1_time_data = calloc(1, asn1_time->length + 1))) {
+        strncpy(asn1_time_data, (char *) asn1_time->data, asn1_time->length);
+
+        size_t asn1_time_length = strlen(asn1_time_data);
+
+        /* does the time string contain an offset of +/-hhmm against UTC?
+         *  - would have to be more than 5 digits long, or we don't even need 
to look */
+        if (asn1_time_length > 5) {
+            if (asn1_time_data[asn1_time_length - 5] == '+' ||
+                asn1_time_data[asn1_time_length - 5] == '-') {
+                int pos = asn1_time_length - 4;
+
+                offset  = (asn1_time_data[pos] - '0') * 10;
+                offset += (asn1_time_data[++pos] - '0');
+                offset *= 60;
+
+                offset += (asn1_time_data[++pos] - '0') * 10;
+                offset += (asn1_time_data[++pos] - '0');
+                offset *= 60;
+
+                if (asn1_time_data[asn1_time_length - 5] == '-') {
+                    offset = -offset;
+                }
+            }
+        }
+        free(asn1_time_data);
+    }
+
+    /* the bug in OpenSSL causes the offset to get substracted instead of 
added, or added
+     * instead of substracted; accordingly, the mistake can be corrected by 
performing the
+     * originally intended operation twice */
+    return 2 * offset;
+}
+
+/**
+ * determines whether OpenSSL has a buggy X509_cmp_time() and whether our 
workaround
+ *  does actually work
+ *
+ * @return            truth value "buggy or not buggy"
+*/
+static bool openssl_is_buggy(void)
+{
+    bool is_buggy             = false;
+
+    /* "8201020700-0500" is the X.680 UTCTime representation of the point in 
time
+     * that POSIX calls "378820800 seconds since January 1st, 1970 (the epoch) 
*/
+    time_t secs_since_epoch   = 378820800 - 1;
+    ASN1_TIME *const utc_time = ASN1_TIME_new();
+
+    if (utc_time && ASN1_UTCTIME_set_string(utc_time, "8201020700-0500")) {
+        /* comparing the UTCTime with the mentioned number of seconds minus 1 
should
+         * give the result of "1", meaning that the first parameter is a later 
point
+         * in time than the second */
+        const int cmp_result = X509_cmp_time(utc_time, &secs_since_epoch);
+
+        /* on buggy OpenSSL versions, we don't get the expected result "1" */
+        if (cmp_result != 1) {
+            /* find out whether our workaround works ... */
+            secs_since_epoch += calculate_correction(utc_time);
+
+            if (X509_cmp_time(utc_time, &secs_since_epoch) == 1) {
+                secs_since_epoch += 2;
+                if (X509_cmp_time(utc_time, &secs_since_epoch) == -1) {
+                    /* only if we reach this point, the bug exists *and* the
+                     *  workaround is capable of getting around it */
+                    is_buggy = true;
+                }
+            }
+        }
+        ASN1_TIME_free(utc_time);
+    }
+    return is_buggy;
+}
+
+/**
+ * will determine the number to be "okay", i.e. suitable for using it as a 
parameter to check_dates(),
+ *  if it is a positive natural number (in decimal) with a length of max. 8 
digits
+ *
+ * @param string      string representation of the number
+ * @return            truth value "number is okay or not"
+ */
+/**static bool number_ok(char *string)
+{
+    if (string != NULL) {
+        size_t len;
+
+        // allow one leading '+'
+        if (string[0] == '+') {
+            string++;
+        }
+
+if ((len = strlen(string)) > 0 && (len <= 8)) {
+    for (size_t i = 0; i < len; i++) {
+        if (string[i] < '0' || string[i] > '9') {
+            return false;
+        }
+    }
+    return true;
+}
+}
+return false;
+}
+*/
+/**
+* determines whether the current system time is between the "notBefore" and 
"notAfter"
+*  boundaries of the certificate, with a certain offset before the "notAfter" 
point in time
+*  in case the num_seconds_string is a string with a numerical value
+*
+* @param cert       the certificate
+* @param num_seconds_string string, containing a positive number of seconds, 
assumed to have been checked by number_ok()
+* @return            truth value "current time could be successfully 
determined to be between boundaries, or not"
+*/
+
+
+//########################################################################################
+
+
+int X509_print_fp_cop(FILE *fp, X509 *x)
+{
+BIO *b;
+int ret;
+
+if ((b=BIO_new(BIO_s_file())) == NULL)
+        {
+//              X509err(X509_F_X509_PRINT_FP,ERR_R_BUF_LIB);
+        return(0);
+        }
+BIO_set_fp(b,fp,BIO_NOCLOSE);
+ret=X509_print_cop(b, x);
+BIO_free(b);
+return(ret);
+}
+int X509_print_cop(BIO *bp, X509 *x)
+{
+long l;
+int ret=0,i,j,n;
+//      char *m=NULL;
+char *s;
+X509_CINF *ci;
+        ASN1_INTEGER *bs;
+EVP_PKEY *pkey=NULL;
+        const char *neg;
+X509_EXTENSION *ex;
+ASN1_STRING *str=NULL;
+
+ci=x->cert_info;
+//if (BIO_write(bp,"Certificate:<br>\n<table>\n",25) <= 0) goto err;
+//if (BIO_write(bp,"    Data:\n",10) <= 0) goto err;
+BIO_printf(bp,"<h3><strong>@TR<<Certification Data>></strong></h3>\n");
+BIO_printf(bp,"<table style=\"width: 90%; margin-left: 2.5em; text-align: 
left; font-size: 0.8em;\" border=\"0\" cellpadding=\"2\" cellspacing=\"1\" 
summary=\"@TR<<Certification>>\">\n");
+
+
+BIO_printf(bp,"<tr><td><table style=\"margin-left: 0.2em; text-align: left;\" 
border=\"0\" cellpadding=\"1\" cellspacing= \"8\" 
summary=\"@TR<<Certification>>\">\n");
+l=X509_get_version(x);
+if (BIO_printf(bp,"<tr><td>Version</td><td>%lu (0x%lx)</td></tr>\n",l+1,l) <= 
0) goto err;
+
+t1(bp);
+if (BIO_write(bp,"Serial Number",13) <= 0) goto err;
+t2(bp);
+
+bs=X509_get_serialNumber(x);
+if (bs->length <= 4)
+        {
+        l=ASN1_INTEGER_get(bs);
+        if (l < 0)
+                {
+                l= -l;
+                neg="-";
+                }
+        else
+                neg="";
+        if (BIO_printf(bp," %s%lu (%s0x%lx)",neg,l,neg,l) <= 0)
+                goto err;
+        }
+else
+        {
+        neg=(bs->type == V_ASN1_NEG_INTEGER)?" (Negative)":"";
+        if (BIO_printf(bp,"\n%12s%s","",neg) <= 0) goto err;
+
+        for (i=0; i<bs->length; i++)
+                {
+                if (BIO_printf(bp,"%02x%c",bs->data[i],
+                        ((i+1 == bs->length)?'\n':':')) <= 0)
+                        goto err;
+                }
+        }
+t3(bp);
+
+i=OBJ_obj2nid(ci->signature->algorithm);
+if (BIO_printf(bp,"<tr><td>Signature Algorithm</td><td> %s</td></tr>\n",
+        (i == NID_undef)?"UNKNOWN":OBJ_nid2ln(i)) <= 0)
+        goto err;
+
+t1(bp);
+if (BIO_write(bp,"Issuer",6) <= 0) goto err;
+t2(bp);
+if (!X509_NAME_print(bp,X509_get_issuer_name(x),16)) goto err;
+t3(bp);
+
+t1(bp);
+//if (BIO_write(bp,"\n        Validity\n",18) <= 0) goto err;
+if (BIO_write(bp,"Valid Not Before",16) <= 0) goto err;
+t2(bp);
+if (!ASN1_TIME_print(bp,X509_get_notBefore(x))) goto err;
+t3(bp);
+
+t1(bp);
+if (BIO_write(bp,"Valid Not After",15) <= 0) goto err;
+t2(bp);
+if (!ASN1_TIME_print(bp,X509_get_notAfter(x))) goto err;
+t3(bp);
+
+t1(bp);
+if (BIO_write(bp,"Subject",7) <= 0) goto err;
+t2(bp);
+if (!X509_NAME_print(bp,X509_get_subject_name(x),16)) goto err;
+t3(bp);
+
+//if (BIO_write(bp,"\n        Subject Public Key Info:\n",34) <= 0)
+//      goto err;
+i=OBJ_obj2nid(ci->key->algor->algorithm);
+if (BIO_printf(bp,"<tr><td>Public Key Algorithm</td><td>%s<br>\n",
+(i == NID_undef)?"UNKNOWN":OBJ_nid2ln(i)) <= 0) goto err;
+
+pkey=X509_get_pubkey(x);
+if (pkey == NULL)
+{
+BIO_printf(bp,"%12sUnable to load Public Key\n","");
+//              ERR_print_errors(bp);
+}
+else
+#ifndef NO_RSA
+if (pkey->type == EVP_PKEY_RSA)
+{
+BIO_printf(bp,"%12sRSA Public Key: (%d bit)\n","",
+BN_num_bits(pkey->pkey.rsa->n));
+        RSA_print(bp,pkey->pkey.rsa,16);
+        }
+else
+#endif
+#ifndef NO_DSA
+if (pkey->type == EVP_PKEY_DSA)
+        {
+        BIO_printf(bp,"%12sDSA Public Key:\n","");
+        DSA_print(bp,pkey->pkey.dsa,16);
+        }
+else
+#endif
+        BIO_printf(bp,"%12sUnknown Public Key:\n","");
+t3(bp);
+BIO_printf(bp,"</table>\n");
+EVP_PKEY_free(pkey);
+
+n=X509_get_ext_count(x);
+if (n > 0)
+        {
+
+        BIO_printf(bp,"<h3><strong>@TR<<X509v3 extensions>></strong></h3>\n");
+        BIO_printf(bp,"<table style=\"width: 90%; margin-left: 2.5em; 
text-align: left; font-size: 1em;\" border=\"0\" cellpadding=\"2\" 
cellspacing=\"1\" summary=\"@TR<<Certification>>\">" );
+        BIO_printf(bp,"<tr><td><table style=\"margin-left: 0.2em; text-align: 
left;\" border=\"0\" cellpadding=\"1\" cellspacing=\"8\" 
summary=\"@TR<<Certification>>\">\n ");
+        for (i=0; i<n; i++)
+                {
+                ASN1_OBJECT *obj;
+                ex=X509_get_ext(x,i);
+                t1(bp);
+                //if (BIO_printf(bp,"%12s","") <= 0) goto err;
+                obj=X509_EXTENSION_get_object(ex);
+                i2a_ASN1_OBJECT(bp,obj);
+                j=X509_EXTENSION_get_critical(ex);
+                t2(bp);
+                if (BIO_printf(bp,"%1s",j?"critical":"") <= 0) goto err;
+        //      if(!X509V3_EXT_print(bp, ex, 0,0))
+        //              {
+        //                      //BIO_printf(bp, "%16s", "");
+        //              M_ASN1_OCTET_STRING_print(bp,ex->value);
+        //              }
+                //if (BIO_write(bp,"\n",1) <= 0) goto err;
+                t3(bp);
+                }
+        }
+
+i=OBJ_obj2nid(x->sig_alg->algorithm);
+if (BIO_printf(bp,"<tr><td>Signature Algorithm</td><td>%s<br>\n",
+        (i == NID_undef)?"UNKNOWN":OBJ_nid2ln(i)) <= 0) goto err;
+
+n=x->signature->length;
+        s=(char *)x->signature->data;
+        for (i=0; i<n; i++)
+                {
+                if ((i%18) == 0)
+                        if (BIO_write(bp,"\n        ",9) <= 0) goto err;
+                if (BIO_printf(bp,"%02x%s",(unsigned char)s[i],
+                        ((i+1) == n)?"":":") <= 0) goto err;
+                }
+        if (BIO_write(bp,"\n",1) != 1) goto err;
+        if (!X509_CERT_AUX_print(bp, x->aux, 0)) goto err;
+        ret=1;
+err:
+        if (str != NULL) ASN1_STRING_free(str);
+//      if (m != NULL) Free(m);
+t3(bp);
+BIO_write(bp,"\n</table>",9);
+        return(ret);
+        }
+
+//################################################################################################################
+
+
+static bool check_dates(X509 *cert, char *num_seconds_string)
+{
+    ASN1_TIME *notBefore;
+    ASN1_TIME *notAfter;
+    long version;
+ //   EVP_PKEY *PUBKEY;
+//    EVP_PKEY *REQPUBKEY;
+    int Output;
+    time_t now1, now2;
+
+    time_t notBefore_correction = 0;
+    time_t notAfter_correction  = 0;
+
+    int32_t num_seconds         = 0;
+
+    bool result                 = false;
+
+    /* first, convert the time stamps to GeneralizedTime format */
+    notBefore = ASN1_TIME_to_generalizedtime(X509_get_notBefore(cert), NULL);
+    notAfter  = ASN1_TIME_to_generalizedtime(X509_get_notAfter(cert), NULL);
+    version = X509_get_version(cert);
+//    signaturtyp = X509_get_signature_type(?);
+//    PUBKEY = X509_get_pubkey(cert);
+//    REQPUBKEY = X509_REQ_extract_key(cert);
+    Output = X509_print_fp_cop(stdout, cert);
+
+//   fprintf(stdout, "notBefore %s\n",notBefore->data);
+
+//    fprintf(stdout, "notAfter %s\n",notAfter->data);
+      fprintf(stdout, "%s\n", "");
+//    fprintf(stdout, "PUBKEY %d\n", PUBKEY->type);
+//    fprintf(stdout, "REQPUBKEY %d\n", REQPUBKEY->type);
+    if ((num_seconds_string == NULL || (sscanf(num_seconds_string, "%"SCNd32, 
&num_seconds) == 1)) &&
+        (notBefore != NULL) && (notAfter != NULL)) {
+        if (openssl_is_buggy()) {
+            notBefore_correction = calculate_correction(notBefore);
+            notAfter_correction  = calculate_correction(notAfter);
+        }
+
+        /* find out the number of seconds since the epoch now, and use this 
constant
+         * value for both following comparisons */
+        const time_t now0 = time(NULL);
+
+        /* check whether the current system time is inbetween, inclusively, 
the notBefore and
+         * the notAfter time stamps of the certificate; offset the latter with 
num_seconds */
+        now1 = now0 + notBefore_correction;
+        now2 = now0 + notAfter_correction + num_seconds;
+
+        /* we might be running right after system startup, with no available 
time server,
+         * so the system might be believing we live shortly after Jan. 1st, 
1970, so it might
+         * happen that "now1" or "now2" ended up smaller than 0 */
+        if ((now1 > 0) && (now2 > 0) && (X509_cmp_time(notBefore, &now1) < 0) 
&& (X509_cmp_time(notAfter, &now2) > 0)) {
+            result = true;
+        }
+    }
+
+    /* ASN1_TIME_to_generalizedtime() calls above included implicit 
ASN1_GENERALIZEDTIME_new(),
+     *  so free the memory again */
+    if (notBefore != NULL) {
+        ASN1_TIME_free(notBefore);
+    }
+    if (notAfter != NULL) {
+        ASN1_TIME_free(notAfter);
+    }
+
+    return result;
+}
+
+/**
+ * outputs usage information
+ *
+ * @param output      the file where the output should go - must be specified
+ */
+static void usage(FILE *output)
+{
+
+    fprintf(output, "Nikous' Programm \n\n");
+    fprintf(output, "pisa-cert-check - check properties of an X.509 
certificate\n\n");
+
+    fprintf(output, "Usage:\n");
+    fprintf(output, "pisa-cert-check <cert-filename> <property> {<opt. 
param>}\n\n");
+
+    fprintf(output, "with property being one of:\n\n");
+
+    fprintf(output, " dates    check whether current system time has at least 
the value of the\n");
+    fprintf(output, "           certificate's \"notBefore\" and at most that 
of the \"notAfter\"\n");
+    fprintf(output, "           time stamp\n\n");
+
+    fprintf(output, "          with an optional number of seconds added as 
parameter: handle\n");
+    fprintf(output, "           \"notBefore\" as above, but check for current 
system time to have\n");
+    fprintf(output, "           at most the value of the \"notAfter\" time 
stamp minus the indicated\n");
+    fprintf(output, "           number of seconds\n\n");
+
+    fprintf(output, "Example:\n");
+    fprintf(output, "pisa-cert-check /etc/hip/cert dates 600\n");
+    fprintf(output, "(makes sure certificate is currently valid and will not 
be expiring earlier\n");
+    fprintf(output, " than 10 minutes from now)\n\n");
+}
+
+/**
+ * main() function; will process the command-line parameters, doing some 
simple checks
+ *  for their validity, and then call the appropriate processing function 
depending on the
+ *  value of "property" command-line param
+ *
+ * @param argc        argument counter
+ * @param argv        argument values
+ * @return            truth value "certificate property could be verified to 
be in order, or not"
+ */
+int main(int argc, char **argv)
+{
+    char *filename     = NULL;
+//    char *property     = NULL;
+//    char *extra_param  = NULL;
+
+    char passphrase[1] = { 0 };
+
+    FILE *cert_file    = NULL;
+    X509 *cert         = NULL;
+
+    bool result        = false;
+
+//    fprintf(stdout,"arhumente %d",argc);
+
+    /* give usage help if required */
+    if (argc == 1 ||
+        (argc == 2 && strlen(argv[1]) == 2 && !strcmp(argv[1], "-h")) ||
+        (argc == 2 && strlen(argv[1]) == 6 && !strcmp(argv[1], "--help"))) {
+        usage(stdout);
+        exit(EXIT_SUCCESS);
+    }
+
+    /* process command-line parameters */
+
+    if (argc == 2 ) {
+        filename = argv[1];
+    } else {
+        usage(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    /* open the file the name of which was passed as parameter */
+    if (!(cert_file = fopen(filename, "r"))) {
+        perror(filename);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!(cert = PEM_read_X509_AUX(cert_file, NULL, NULL, passphrase))) {
+        rewind(cert_file);
+        if (!(cert = d2i_X509_fp(cert_file, NULL))) {
+            fprintf(stderr, "%s: reading the certificate file failed\n", 
filename);
+            fclose(cert_file);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+
+
+    result = check_dates(cert,NULL);
+
+
+    /* clean up */
+    X509_free(cert);
+    fclose(cert_file);
+
+    /* return result of certificate check */
+    if (result == true) {
+        exit(EXIT_SUCCESS);
+    } else {
+        exit(EXIT_FAILURE);
+    }
+}
-- 
This is the pisa developer mailing list. Please also subscribe to the main pisa 
list at:
//www.freelists.org/list/pisa

Other related posts: