This patch adds support for GeneralizedTime for startdate/enddate in
openssl ca. I guess not too many people need certificates beyond 2049
(or before 1950) right now, but having the capability surely can't hurt.

Also, previously it accepted non-GMT times and values without seconds,
both of which are not allowed by RFC 5280 (and previous ones). This is
fixed too.
--- openssl-SNAP-20090303.orig/apps/ca.c	2009-03-03 19:04:00.000000000 +0100
+++ openssl-SNAP-20090303/apps/ca.c	2009-03-03 23:04:24.000000000 +0100
@@ -227,6 +227,9 @@
 static int get_certificate_status(const char *ser_status, CA_DB *db);
 static int do_updatedb(CA_DB *db);
 static int check_time_format(const char *str);
+static int check_time_valid_rfc(const char *str, int numcnt);
+static int check_utctime(const char *str);
+static int check_generalizedtime(const char *str);
 char *make_revocation_str(int rev_type, char *rev_arg);
 int make_revoked(X509_REVOKED *rev, const char *str);
 int old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str);
@@ -1109,9 +1112,10 @@
 			if (startdate == NULL)
 				ERR_clear_error();
 			}
-		if (startdate && !ASN1_UTCTIME_set_string(NULL,startdate))
+		if (startdate && !((ASN1_UTCTIME_set_string(NULL, startdate) && check_utctime(startdate))
+				|| (ASN1_GENERALIZEDTIME_set_string(NULL, startdate) && check_generalizedtime(startdate))))
 			{
-			BIO_printf(bio_err,"start date is invalid, it should be YYMMDDHHMMSSZ\n");
+			BIO_printf(bio_err,"start date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
 			goto err;
 			}
 		if (startdate == NULL) startdate="today";
@@ -1123,9 +1127,10 @@
 			if (enddate == NULL)
 				ERR_clear_error();
 			}
-		if (enddate && !ASN1_UTCTIME_set_string(NULL,enddate))
+		if (enddate && !((ASN1_UTCTIME_set_string(NULL, enddate) && check_utctime(enddate))
+				|| (ASN1_GENERALIZEDTIME_set_string(NULL, startdate) && check_generalizedtime(enddate))))
 			{
-			BIO_printf(bio_err,"end date is invalid, it should be YYMMDDHHMMSSZ\n");
+			BIO_printf(bio_err,"end date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
 			goto err;
 			}
 
@@ -1689,6 +1694,7 @@
 	STRING *irow=NULL;
 	STRING *rrow=NULL;
 	char buf[25];
+	int year;
 
 	tmptm=ASN1_UTCTIME_new();
 	if (tmptm == NULL)
@@ -2007,11 +2013,39 @@
 
 	if (strcmp(startdate,"today") == 0)
 		X509_gmtime_adj(X509_get_notBefore(ret),0);
-	else ASN1_UTCTIME_set_string(X509_get_notBefore(ret),startdate);
+	else
+		{
+		if (!ASN1_UTCTIME_set_string(X509_get_notBefore(ret),startdate))
+			{
+			if (sscanf(startdate, "%4u", &year) != 1)
+				goto err;
+			if (year >= 1950 && year <= 2049)
+				{
+				memmove(startdate, startdate + 2, strlen(startdate) - 1);
+				ASN1_UTCTIME_set_string(X509_get_notBefore(ret),startdate);
+				}
+			else
+				ASN1_GENERALIZEDTIME_set_string(X509_get_notBefore(ret),startdate);
+			}
+		}
 
 	if (enddate == NULL)
 		X509_time_adj_ex(X509_get_notAfter(ret),days, 0, NULL);
-	else ASN1_UTCTIME_set_string(X509_get_notAfter(ret),enddate);
+	else
+		{
+		if (!ASN1_UTCTIME_set_string(X509_get_notAfter(ret),enddate))
+			{
+			if (sscanf(enddate, "%4u", &year) != 1)
+				goto err;
+			if (year >= 1950 && year <= 2049)
+				{
+				memmove(enddate, enddate + 2, strlen(enddate) - 1);
+				ASN1_UTCTIME_set_string(X509_get_notAfter(ret),enddate);
+				}
+			else
+				ASN1_GENERALIZEDTIME_set_string(X509_get_notAfter(ret),enddate);
+			}
+		}
 
 	if (!X509_set_subject_name(ret,subject)) goto err;
 
@@ -2107,7 +2141,7 @@
 		}
 
 	BIO_printf(bio_err,"Certificate is to be certified until ");
-	ASN1_UTCTIME_print(bio_err,X509_get_notAfter(ret));
+	ASN1_TIME_print(bio_err,X509_get_notAfter(ret));
 	if (days) BIO_printf(bio_err," (%ld days)",days);
 	BIO_printf(bio_err, "\n");
 
@@ -2398,11 +2432,15 @@
 static int check_time_format(const char *str)
 	{
 	ASN1_UTCTIME tm;
+	ASN1_GENERALIZEDTIME gtm;
 
 	tm.data=(unsigned char *)str;
 	tm.length=strlen(str);
 	tm.type=V_ASN1_UTCTIME;
-	return(ASN1_UTCTIME_check(&tm));
+	gtm.data=(unsigned char *)str;
+	gtm.length=strlen(str);
+	gtm.type=V_ASN1_GENERALIZEDTIME;
+	return(ASN1_UTCTIME_check(&tm) || ASN1_GENERALIZEDTIME_check(&gtm));
 	}
 
 static int do_revoke(X509 *x509, CA_DB *db, int type, char *value)
@@ -3005,3 +3043,32 @@
 
 	return ret;
 	}
+
+/* As per RFC 5280, both UTCTime and GeneralizedTime must include seconds and be
+ * specified in GMT; additionally, the latter must not include fractional seconds.
+ */
+static int check_time_valid_rfc(const char *str, int numcnt)
+	{
+	int i;
+	
+	if (strlen(str) != numcnt + 1)
+		return 0;
+	if (str[numcnt] != 'Z')
+		return 0;
+	for (i = 0; i < numcnt; i++)
+		{
+			if (str[i] < '0' || str[i] > '9')
+				return 0;
+		}
+	return 1;
+	}
+
+static int check_utctime(const char *str)
+	{
+		return check_time_valid_rfc(str, 12);
+	}
+
+static int check_generalizedtime(const char *str)
+	{
+		return check_time_valid_rfc(str, 14);
+	}

Reply via email to