Hi! Here it comes my patch to the ca application. Please continue reading for full explanation. ************************** 1. ca -extensions ================= I've been trying to issue different kind of certificates such as servers, clients, CAs, but the only way to set correctly the extensions was to: a) modify the config file; b) keeping a config file for every kind of certs; So here it comes the patch. It will add the ability to specify the extensions that should be used for the certificate. To be more precise you can only override the config default keyword (x509extensions) so as to use the extensions-section you like most of the config file. Let's make some example: use in your config file something like this (let's say at the end, but this is not important) >>>>> Begin openssl.cnf <<<<<< .. .. [ ca_cert_ext ] # Extensions for CAs basicConstraints = CA:true #basicConstraints = critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign, keyCertSign subjectAltName=email:copy issuerAltName=issuer:copy [ server_cert_ext ] # Server Extensions basicConstraints=CA:FALSE nsCertType = server # For an object signing certificate this would be used. #nsCertType = objsign # For normal client use this is typical nsCertType = client, email keyUsage = nonRepudiation, digitalSignature, keyEncipherment nsComment = "OpenCA Server Certificate" # PKIX recommendations subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer:always # Import the email address. subjectAltName=email:copy >>>>>> End openssl.cnf <<<<<<<< when issuing certificates for a CA you can use: $ openssl ca -config openssl.cnf -extensions ca_cert_ext -in $request ... while for a server: $ openssl ca -config openssl.cnf -extensions server_cert_ext -in $request ... if you do not use the -extensions flag, then it will be used the standad config keyword x509_extensions if present as usual. 2. ca -updatedb ================ This patch has been given by Lars Weber and I only melt toghether with mine ( thus correcting a little bug) and is capable of updating the index.txt files searching for expired certificates. If you run once a day (or more through a crontab) you'll get an up-to-dated index.txt file. 3. ca -status ============= This patch let you get the status of a certificate based on what you have in your index.txt file. It accepts the serial number (hex) of the certificate you want to know the state (Valid/Expired/Revoked/Unknown) and returns a string with the status. ********************** This Should be all, if you have any questions, please feedback to me... C'you, Massimiliano Pala ([EMAIL PROTECTED]) P.S.: To Apply the diff use the usual 'patch -p1 ca.c ca.diff'. Bye!
--- ca.c Sat May 8 15:00:04 1999 +++ ca-patched.c Tue May 11 19:18:16 1999 @@ -1,4 +1,4 @@ -/* apps/ca.c */ +#/* apps/ca.c */ /* Copyright (C) 1995-1998 Eric Young ([EMAIL PROTECTED]) * All rights reserved. * @@ -61,6 +61,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include "apps.h" @@ -134,6 +135,7 @@ " -days arg - number of days to certify the certificate for\n", " -md arg - md to use, one of md2, md5, sha or sha1\n", " -policy arg - The CA 'policy' to support\n", +" -extensions arg - Specify the extensions section in config file\n", " -keyfile arg - PEM private key file\n", " -key arg - key to decode the private key if it is encrypted\n", " -cert file - The CA certificate\n", @@ -147,6 +149,8 @@ " -batch - Don't ask questions\n", " -msie_hack - msie modifications to handle all those universal strings\n", " -revoke file - Revoke a certificate (given in file)\n", +" -status serial - Shows certificate' status given the serial number\n", +" -updatedb - Checks index.txt for expired certificates and mark them\n", NULL }; @@ -185,6 +189,8 @@ int days, int batch, int verbose, X509_REQ *req, char *ext_sect, LHASH *conf); static int do_revoke(X509 *x509, TXT_DB *db); +static int get_certificate_status(char *ser_status, TXT_DB *db); +static int do_updatedb(TXT_DB *db); static int check_time_format(char *str); static LHASH *conf; static char *key=NULL; @@ -203,6 +209,7 @@ int verbose=0; int gencrl=0; int dorevoke=0; + int doupdatedb=0; long crldays=0; long crlhours=0; long errorline= -1; @@ -214,6 +221,7 @@ char *infile=NULL; char *spkac_file=NULL; char *ss_cert_file=NULL; + char *ser_status=NULL; EVP_PKEY *pkey=NULL; int output_der = 0; char *outfile=NULL; @@ -295,6 +303,11 @@ if (--argc < 1) goto bad; policy= *(++argv); } + else if (strcmp(*argv,"-extensions") == 0) + { + if (--argc < 1) goto bad; + extensions= *(++argv); + } else if (strcmp(*argv,"-keyfile") == 0) { if (--argc < 1) goto bad; @@ -369,6 +382,15 @@ infile= *(++argv); dorevoke=1; } + else if (strcmp(*argv,"-status") == 0) + { + if (--argc < 1) goto bad; + ser_status= *(++argv); + } + else if (strcmp(*argv,"-updatedb") == 0) + { + doupdatedb=1; + } else { bad: @@ -464,6 +486,150 @@ } /*****************************************************************/ + /* we need to load the database file */ + if ((dbfile=CONF_get_string(conf,section,ENV_DATABASE)) == NULL) + { + lookup_fail(section,ENV_DATABASE); + goto err; + } + if (BIO_read_filename(in,dbfile) <= 0) + { + perror(dbfile); + BIO_printf(bio_err,"unable to open '%s'\n",dbfile); + goto err; + } + db=TXT_DB_read(in,DB_NUMBER); + if (db == NULL) goto err; + + /* Lets check some fields */ + for (i=0; i<sk_num(db->data); i++) + { + pp=(char **)sk_value(db->data,i); + if ((pp[DB_type][0] != DB_TYPE_REV) && + (pp[DB_rev_date][0] != '\0')) + { + BIO_printf(bio_err,"entry %d: not revoked yet, but has a +revocation date\n",i+1); + goto err; + } + if ((pp[DB_type][0] == DB_TYPE_REV) && + !check_time_format(pp[DB_rev_date])) + { + BIO_printf(bio_err,"entry %d: invalid revocation date\n", + i+1); + goto err; + } + if (!check_time_format(pp[DB_exp_date])) + { + BIO_printf(bio_err,"entry %d: invalid expiry date\n",i+1); + goto err; + } + p=pp[DB_serial]; + j=strlen(p); + if ((j&1) || (j < 2)) + { + BIO_printf(bio_err,"entry %d: bad serial number length +(%d)\n",i+1,j); + goto err; + } + while (*p) + { + if (!( ((*p >= '0') && (*p <= '9')) || + ((*p >= 'A') && (*p <= 'F')) || + ((*p >= 'a') && (*p <= 'f'))) ) + { + BIO_printf(bio_err,"entry %d: bad serial number +characters, char pos %ld, char is '%c'\n",i+1,(long)(p-pp[DB_serial]),*p); + goto err; + } + p++; + } + } + if (verbose) + { + BIO_set_fp(out,stdout,BIO_NOCLOSE|BIO_FP_TEXT); /* cannot fail */ + TXT_DB_write(out,db); + BIO_printf(bio_err,"%d entries loaded from the database\n", + db->data->num); + BIO_printf(bio_err,"generating indexs\n"); + } + + if (!TXT_DB_create_index(db,DB_serial,NULL,index_serial_hash, + index_serial_cmp)) + { + BIO_printf(bio_err,"error creating serial number +index:(%ld,%ld,%ld)\n",db->error,db->arg1,db->arg2); + goto err; + } + + if (!TXT_DB_create_index(db,DB_name,index_name_qual,index_name_hash, + index_name_cmp)) + { + BIO_printf(bio_err,"error creating name index:(%ld,%ld,%ld)\n", + db->error,db->arg1,db->arg2); + goto err; + } + + /*****************************************************************/ + /* if the certificate status is required we give it */ + if (ser_status) + { + if( get_certificate_status(ser_status,db) != 1) + BIO_printf(bio_err,"Error verifying serial %s!\n", + ser_status); + goto err; + } + + /*****************************************************************/ + /* Update the db file for expired certificates */ + if (doupdatedb) + { + i = do_updatedb (db); + if ( i == -1) + { + BIO_printf(bio_err,"Malloc failure\n"); + goto err; + } + else if ( i == 0 ) + BIO_printf(bio_err,"No entry's found to mark expired - will not touch +index.txt\n"); + else + { + out = BIO_new (BIO_s_file()); + if (out == NULL) + { + ERR_print_errors (bio_err); + goto err; + } + + strncpy (buf[0],dbfile,BSIZE-4); + strcat (buf[0],".new"); + if (BIO_write_filename(out,buf[0]) <= 0) + { + perror(dbfile); + BIO_printf(bio_err,"unable to open '%s'\n",dbfile); + goto err; + } + j=TXT_DB_write(out,db); + if (j <= 0) goto err; + BIO_free(out); + out = NULL; + strncpy (buf[1],dbfile,BSIZE-4); + strcat (buf[1],".old"); + if (rename(dbfile,buf[1]) < 0) + { + BIO_printf(bio_err,"unable to rename %s to %s\n", dbfile, buf[1]); + perror("reason"); + goto err; + } + if (rename(buf[0],dbfile) < 0) + { + BIO_printf(bio_err,"unable to rename %s to %s\n", buf[0],dbfile); + perror("reason"); + rename(buf[1],dbfile); + goto err; + } + BIO_printf(bio_err,"%d entry's marked as expired\n", i); + } + goto err; + } + + /*****************************************************************/ /* we definitly need an public key, so lets get it */ if ((keyfile == NULL) && ((keyfile=CONF_get_string(conf, @@ -558,86 +724,6 @@ } } - /*****************************************************************/ - /* we need to load the database file */ - if ((dbfile=CONF_get_string(conf,section,ENV_DATABASE)) == NULL) - { - lookup_fail(section,ENV_DATABASE); - goto err; - } - if (BIO_read_filename(in,dbfile) <= 0) - { - perror(dbfile); - BIO_printf(bio_err,"unable to open '%s'\n",dbfile); - goto err; - } - db=TXT_DB_read(in,DB_NUMBER); - if (db == NULL) goto err; - - /* Lets check some fields */ - for (i=0; i<sk_num(db->data); i++) - { - pp=(char **)sk_value(db->data,i); - if ((pp[DB_type][0] != DB_TYPE_REV) && - (pp[DB_rev_date][0] != '\0')) - { - BIO_printf(bio_err,"entry %d: not revoked yet, but has a revocation date\n",i+1); - goto err; - } - if ((pp[DB_type][0] == DB_TYPE_REV) && - !check_time_format(pp[DB_rev_date])) - { - BIO_printf(bio_err,"entry %d: invalid revocation date\n", - i+1); - goto err; - } - if (!check_time_format(pp[DB_exp_date])) - { - BIO_printf(bio_err,"entry %d: invalid expiry date\n",i+1); - goto err; - } - p=pp[DB_serial]; - j=strlen(p); - if ((j&1) || (j < 2)) - { - BIO_printf(bio_err,"entry %d: bad serial number length (%d)\n",i+1,j); - goto err; - } - while (*p) - { - if (!( ((*p >= '0') && (*p <= '9')) || - ((*p >= 'A') && (*p <= 'F')) || - ((*p >= 'a') && (*p <= 'f'))) ) - { - BIO_printf(bio_err,"entry %d: bad serial number characters, char pos %ld, char is '%c'\n",i+1,(long)(p-pp[DB_serial]),*p); - goto err; - } - p++; - } - } - if (verbose) - { - BIO_set_fp(out,stdout,BIO_NOCLOSE|BIO_FP_TEXT); /* cannot fail */ - TXT_DB_write(out,db); - BIO_printf(bio_err,"%d entries loaded from the database\n", - db->data->num); - BIO_printf(bio_err,"generating indexs\n"); - } - - if (!TXT_DB_create_index(db,DB_serial,NULL,index_serial_hash, - index_serial_cmp)) - { - BIO_printf(bio_err,"error creating serial number index:(%ld,%ld,%ld)\n",db->error,db->arg1,db->arg2); - goto err; - } - - if (!TXT_DB_create_index(db,DB_name,index_name_qual,index_name_hash, - index_name_cmp)) - { - BIO_printf(bio_err,"error creating name index:(%ld,%ld,%ld)\n", - db->error,db->arg1,db->arg2); - goto err; - } /*****************************************************************/ if (req || gencrl) @@ -687,7 +773,9 @@ goto err; } - extensions=CONF_get_string(conf,section,ENV_EXTENSIONS); + if(!extensions) + extensions=CONF_get_string(conf,section,ENV_EXTENSIONS); + if(extensions) { /* Check syntax of file */ X509V3_CTX ctx; @@ -1134,6 +1222,7 @@ BIO_printf(bio_err,"Data Base Updated\n"); } } + /*****************************************************************/ ret=0; err: @@ -2161,3 +2250,135 @@ return(ok); } +static int get_certificate_status ( char *serial, TXT_DB *db ) +{ + char *row[DB_NUMBER],**rrow; + int ok=-1,i; + + /* Free Resources */ + for (i=0; i<DB_NUMBER; i++) + row[i]=NULL; + /* Malloc needed char spaces */ + row[DB_serial]=( char * ) Malloc ( strlen(serial) +1); + if (row[DB_serial] == NULL) + { + BIO_printf(bio_err,"Malloc failure\n"); + goto err; + } + + /* Copy String from serial to row[DB_serial] */ + memcpy( row[DB_serial], serial, strlen(serial)); + row[DB_serial][strlen(serial)]='\0'; + + /* Make it Upper Case */ + for( i=0; row[DB_serial][i] != '\0'; i++ ) + row[DB_serial][i] = (char) toupper( row[DB_serial][i] ); + + ok=1; + + /* Search for the certificate */ + rrow=TXT_DB_get_by_index(db,DB_serial,row); + if (rrow == NULL) + { + BIO_printf(bio_err,"Serial %s not present in db.\n", + row[DB_serial]); + ok=-1; + goto err; + } + else if (rrow[DB_type][0]=='V') + { + BIO_printf(bio_err,"STATUS: Valid (%c)\n", + rrow[DB_type][0]); + goto err; + } + else if (rrow[DB_type][0]=='R') + { + BIO_printf(bio_err,"STATUS: Revoked (%c)\n", + rrow[DB_type][0]); + goto err; + } + else if (rrow[DB_type][0]=='E') + { + BIO_printf(bio_err,"STATUS: Expired (%c)\n", + rrow[DB_type][0]); + goto err; + } + else + { + BIO_printf(bio_err,"ERROR: Unknown status (%c).\n", + rrow[DB_type][0]); + ok=-1; + } +err: + for (i=0; i<DB_NUMBER; i++) + { + if ((row[i] != NULL) && (row[i] != serial) ) + Free(row[i]); + } + return(ok); +} + +static int do_updatedb (TXT_DB *db) +{ + ASN1_UTCTIME *a_tm = NULL; + int i, cnt = 0; + int db_y2k, a_y2k; /* flags = 1 if y >= 2000 */ + char **rrow, *a_tm_s; + + /* mdified by madwolf */ + a_tm = ASN1_UTCTIME_new(); + + /* get actual time and make a string */ + a_tm = X509_gmtime_adj( a_tm, 0 ); + a_tm_s = (char *) Malloc( a_tm->length+1 ); + if ( a_tm_s == NULL ) + { + cnt = -1; + goto err; + } + + memcpy( a_tm_s, a_tm->data, a_tm->length ); + a_tm_s[a_tm->length] = '\0'; + + if ( strncmp( a_tm_s, "49", 2 ) <= 0 ) + a_y2k = 1; + else + a_y2k = 0; + + for (i = 0; i < sk_num( db->data ); i++) + { + rrow = (char **) sk_value( db->data, i ); + if ( rrow[DB_type][0] == 'V' ) + { + /* ignore entry's that are not valid */ + if ( strncmp( rrow[DB_exp_date], "49", 2 ) <= 0 ) + db_y2k = 1; + else + db_y2k = 0; + + if ( db_y2k == a_y2k ) + { + /* all on the same y2k side */ + if ( strcmp( rrow[DB_exp_date], a_tm_s ) <= 0 ) + { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + } + } + else if ( db_y2k < a_y2k ) + { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + } + + } + } + +err: + + ASN1_UTCTIME_free( a_tm ); + Free( a_tm_s ); + return (cnt); +}