Add `openssl ca -revoke <certfile>' facility which revokes a certificate
specified in <certfile> by updating the entry in the index.txt file.
This way one no longer has to edit the index.txt file manually for
revoking a certificate. The -revoke option does the gory details now.

Submitted by: Massimiliano Pala <madwolf@openca.org>
Cleaned up and integrated by: Ralf S. Engelschall
diff --git a/CHANGES b/CHANGES
index cac99a3..d1cc3cf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,12 @@
 
  Changes between 0.9.2b and 0.9.3
 
+  *) Add `openssl ca -revoke <certfile>' facility which revokes a certificate
+     specified in <certfile> by updating the entry in the index.txt file.
+     This way one no longer has to edit the index.txt file manually for
+     revoking a certificate. The -revoke option does the gory details now.
+     [Massimiliano Pala <madwolf@openca.org>, Ralf S. Engelschall]
+
   *) Fix `openssl crl -noout -text' combination where `-noout' killed the
      `-text' option at all and this way the `-noout -text' combination was
      inconsistent in `openssl crl' with the friends in `openssl x509|rsa|dsa'.
diff --git a/apps/ca.c b/apps/ca.c
index 6492634..6c060ce 100644
--- a/apps/ca.c
+++ b/apps/ca.c
@@ -146,6 +146,7 @@
 " -preserveDN     - Don't re-order the DN\n",
 " -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",
 NULL
 };
 
@@ -181,6 +182,7 @@
 	STACK *policy, TXT_DB *db, BIGNUM *serial, char *startdate,
 	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 check_time_format(char *str);
 #else
 static int add_oid_section();
@@ -199,6 +201,7 @@
 static int certify_spkac();
 static void write_new_certificate();
 static int do_body();
+static int do_revoke();
 static int check_time_format();
 #endif
 
@@ -220,6 +223,7 @@
 	int req=0;
 	int verbose=0;
 	int gencrl=0;
+	int revoke=0;
 	long crldays=0;
 	long crlhours=0;
 	long errorline= -1;
@@ -380,6 +384,12 @@
 			spkac_file = *(++argv);
 			req=1;
 			}
+		else if (strcmp(*argv,"-revoke") == 0)
+			{
+			if (--argc < 1) goto bad;
+			infile= *(++argv);
+			revoke=1;
+			}
 		else
 			{
 bad:
@@ -1078,6 +1088,69 @@
 		PEM_write_bio_X509_CRL(Sout,crl);
 		}
 	/*****************************************************************/
+	if (revoke)
+		{
+        	in=BIO_new(BIO_s_file());
+        	out=BIO_new(BIO_s_file());
+        	if ((in == NULL) || (out == NULL))
+                	{
+                	ERR_print_errors(bio_err);
+                	goto err;
+                	}
+        	if (infile == NULL) 
+                	{
+                	BIO_printf(bio_err,"no input files\n");
+                	goto err;
+                	}
+		else
+			{
+                	if (BIO_read_filename(in,infile) <= 0)
+                		{
+                        	perror(infile);
+                        	BIO_printf(bio_err,"error trying to load '%s' certificate\n",infile);
+                        	goto err;
+                		}
+                	x509=PEM_read_bio_X509(in,NULL,NULL);
+                	if (x509 == NULL)
+                		{
+                        	BIO_printf(bio_err,"unable to load '%s' certificate\n",infile);
+				goto err;
+                		}
+                	j=do_revoke(x509,db);
+
+			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(in);
+                	BIO_free(out);
+                	in=NULL;
+                	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,"Data Base Updated\n"); 
+		        }
+		}
+	/*****************************************************************/
 	ret=0;
 err:
 	BIO_free(hex);
@@ -2068,3 +2141,111 @@
 	}
 	return 1;
 }
+
+static int do_revoke(x509,db)
+X509 *x509;
+TXT_DB *db;
+{
+        ASN1_UTCTIME *tm=NULL;
+        char *row[DB_NUMBER],**rrow,**irow;
+        int ok=-1,i;
+
+        for (i=0; i<DB_NUMBER; i++)
+                row[i]=NULL;
+        row[DB_name]=X509_NAME_oneline(x509->cert_info->subject,NULL,0);
+        row[DB_serial]=BN_bn2hex(ASN1_INTEGER_to_BN(x509->cert_info->serialNumber,NULL));
+        if ((row[DB_name] == NULL) || (row[DB_serial] == NULL))
+                {
+                BIO_printf(bio_err,"Malloc failure\n");
+                goto err;
+                }
+        rrow=TXT_DB_get_by_index(db,DB_name,row);
+        if (rrow == NULL)
+                {
+                BIO_printf(bio_err,"Adding Entry to DB for %s\n", row[DB_name]);
+
+                /* We now just add it to the database */
+                row[DB_type]=(char *)Malloc(2);
+
+                tm=X509_get_notAfter(x509);
+                row[DB_exp_date]=(char *)Malloc(tm->length+1);
+                memcpy(row[DB_exp_date],tm->data,tm->length);
+                row[DB_exp_date][tm->length]='\0';
+
+                row[DB_rev_date]=NULL;
+
+                /* row[DB_serial] done already */
+                row[DB_file]=(char *)Malloc(8);
+
+                /* row[DB_name] done already */
+
+                if ((row[DB_type] == NULL) || (row[DB_exp_date] == NULL) ||
+                        (row[DB_file] == NULL))
+                        {
+                        BIO_printf(bio_err,"Malloc failure\n");
+                        goto err;
+                        }
+                strcpy(row[DB_file],"unknown");
+                row[DB_type][0]='V';
+                row[DB_type][1]='\0';
+
+                if ((irow=(char **)Malloc(sizeof(char *)*(DB_NUMBER+1))) == NULL)
+                        {
+                        BIO_printf(bio_err,"Malloc failure\n");
+                        goto err;
+                        }
+
+                for (i=0; i<DB_NUMBER; i++)
+                        {
+                        irow[i]=row[i];
+                        row[i]=NULL;
+                        }
+                irow[DB_NUMBER]=NULL;
+
+                if (!TXT_DB_insert(db,irow))
+                        {
+                        BIO_printf(bio_err,"failed to update database\n");
+                        BIO_printf(bio_err,"TXT_DB error number %ld\n",db->error);
+                        goto err;
+                        }
+
+                /* Revoke Certificate */
+                do_revoke(x509,db);
+
+                ok=1;
+                goto err;
+
+                }
+        else if (index_serial_cmp(row,rrow))
+                {
+                BIO_printf(bio_err,"ERROR:no same serial number %s\n",
+                           row[DB_serial]);
+                goto err;
+                }
+        else if (rrow[DB_type][0]=='R')
+                {
+                BIO_printf(bio_err,"ERROR:Already revoked, serial number %s\n",
+                           row[DB_serial]);
+                goto err;
+                }
+        else
+                {
+                BIO_printf(bio_err,"Revoking Certificate %s.\n", rrow[DB_serial]);
+                tm=X509_gmtime_adj(tm,0);
+                rrow[DB_type][0]='R';
+                rrow[DB_type][1]='\0';
+                rrow[DB_rev_date]=(char *)Malloc(tm->length+1);
+                memcpy(rrow[DB_rev_date],tm->data,tm->length);
+                rrow[DB_rev_date][tm->length]='\0';
+                }
+        ok=1;
+err:
+        for (i=0; i<DB_NUMBER; i++)
+                {
+                if (row[i] != NULL) 
+                        Free(row[i]);
+                }
+        ASN1_UTCTIME_free(tm);
+        return(ok);
+}
+