Lars Weber wrote: > > Dear OpenSSL-Devlopers ! > > As I have wrote in a mail to openssl-users I would like to add a new > functionality in openssl. As Massimiliano Pala has suggested I send my patch > to this list. > > I have attached a patch to enhance the "ca"-application to check "index.txt" > for expired certs and (if found) mark them. The idea is to run > "openssl ca -updatedb" (new switch) periodically by a cron-job to keep > index.txt up-to-date. > > The patch is a diff -cb against openssl-SNAP-19990505/apps/ca.c. > > Some annotations: > > I have basically adapted the revoke-parts in ca.c for my needs. I have added a > new subroutine called "do_updatedb" which does the checking of index.txt. The > routine returns the number of new marked entry´s or -1 for an Malloc-error. > > To avoid asking for the pass-phrase for the ca-cert I had to wrap this part > with an if-clause. > > The basic problem in checking index.txt was to avoid year 2000 problems: > > I use two flags "db_y2k" and "a_y2k". db_y2k is set to 1 if the year part > of the date-entry in index.txt is less or equal 49, otherwise db_y2k is set to > 0. Same for a_y2k and the actual date. This implies that 00,01,...,49 are > 20xx-years. I hope I have got this right... > > Then db_y2k and a_y2k will be compared: > > db_y2k > a_y2k => cert is valid > db_y2k < a_y2k => cert is not valid > db_y2k = a_y2k => The dates lay in the same interval. Now it is save to > use the standard "strcmp()"-function to compare the > the date-entry in index.txt and the actual date. > > That´s all. > > I am not an advanced C-programmer so feel free to change everything > or reject it completely ;-) > Hi! I am trying and actually succeded to patch the ca.c program (last snapshot) to work with your -updatedb and my -status serialnume switches to the ca-app. I had to move the whole if(updatedb) ... to upper level actually avoiding the password request ( thus removing your if(updatedb = 0 ) on the certificate loading stage). I actually get errors when lounching the -updatedb because a newly issued certificate with the following entry in db gets the 'Expired' flag set (while it shouldn't). Here it is: V 000509111506Z 01 unknown /C=IT/O=OpenCA/OU=Developer/CN=Massimiliano [EMAIL PROTECTED] Do you have some ideas ? Anyway I post the ca.diff patch (use patch -p1 ca.c ca.diff) so we ca work toghether to the problem. I start reviewing your code, if you find out what the problem is, please tell me! Thanks, See you on the bit Stream, Massimiliano Pala ([EMAIL PROTECTED])
--- ca.c Mon May 10 13:27:34 1999 +++ ca-patched.c Mon May 10 13:27:26 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" @@ -147,6 +148,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 +188,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 +208,7 @@ int verbose=0; int gencrl=0; int dorevoke=0; + int doupdatedb=0; long crldays=0; long crlhours=0; long errorline= -1; @@ -214,6 +220,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; @@ -369,6 +376,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 +480,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 +718,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) @@ -1134,6 +1214,7 @@ BIO_printf(bio_err,"Data Base Updated\n"); } } + /*****************************************************************/ ret=0; err: @@ -2161,3 +2242,126 @@ 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; + + + /* 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 ) /* db > a => cert is valid */ + { + if ( db_y2k < a_y2k ) /* db < a => cert is expired */ + { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + } + else if ( strcmp( rrow[DB_exp_date], a_tm_s ) <= 0 ) /* db = a */ + { + rrow[DB_type][0] = 'E'; + rrow[DB_type][1] = '\0'; + cnt++; + } + } + } + } + err: + ASN1_UTCTIME_free( a_tm ); + Free( a_tm_s ); + return (cnt); +}