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(>m)); } 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); + }