Commit fd19a3d1 authored by David Howells's avatar David Howells

PKCS#7: Improve and export the X.509 ASN.1 time object decoder

Make the X.509 ASN.1 time object decoder fill in a time64_t rather than a
struct tm to make comparison easier (unfortunately, this makes readable
display less easy) and export it so that it can be used by the PKCS#7 code
too.

Further, tighten up its parsing to reject invalid dates (eg. weird
characters, non-existent hour numbers) and unsupported dates (eg. timezones
other than 'Z' or dates earlier than 1970).
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 770f2b98
...@@ -472,60 +472,105 @@ int x509_process_extension(void *context, size_t hdrlen, ...@@ -472,60 +472,105 @@ int x509_process_extension(void *context, size_t hdrlen,
return 0; return 0;
} }
/* /**
* Record a certificate time. * x509_decode_time - Decode an X.509 time ASN.1 object
* @_t: The time to fill in
* @hdrlen: The length of the object header
* @tag: The object tag
* @value: The object value
* @vlen: The size of the object value
*
* Decode an ASN.1 universal time or generalised time field into a struct the
* kernel can handle and check it for validity. The time is decoded thus:
*
* [RFC5280 §4.1.2.5]
* CAs conforming to this profile MUST always encode certificate validity
* dates through the year 2049 as UTCTime; certificate validity dates in
* 2050 or later MUST be encoded as GeneralizedTime. Conforming
* applications MUST be able to process validity dates that are encoded in
* either UTCTime or GeneralizedTime.
*/ */
static int x509_note_time(struct tm *tm, size_t hdrlen, int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag, unsigned char tag,
const unsigned char *value, size_t vlen) const unsigned char *value, size_t vlen)
{ {
static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };
const unsigned char *p = value; const unsigned char *p = value;
unsigned year, mon, day, hour, min, sec, mon_len;
#define dec2bin(X) ((X) - '0') #define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; }) #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
if (tag == ASN1_UNITIM) { if (tag == ASN1_UNITIM) {
/* UTCTime: YYMMDDHHMMSSZ */ /* UTCTime: YYMMDDHHMMSSZ */
if (vlen != 13) if (vlen != 13)
goto unsupported_time; goto unsupported_time;
tm->tm_year = DD2bin(p); year = DD2bin(p);
if (tm->tm_year >= 50) if (year >= 50)
tm->tm_year += 1900; year += 1900;
else else
tm->tm_year += 2000; year += 2000;
} else if (tag == ASN1_GENTIM) { } else if (tag == ASN1_GENTIM) {
/* GenTime: YYYYMMDDHHMMSSZ */ /* GenTime: YYYYMMDDHHMMSSZ */
if (vlen != 15) if (vlen != 15)
goto unsupported_time; goto unsupported_time;
tm->tm_year = DD2bin(p) * 100 + DD2bin(p); year = DD2bin(p) * 100 + DD2bin(p);
if (year >= 1950 && year <= 2049)
goto invalid_time;
} else { } else {
goto unsupported_time; goto unsupported_time;
} }
tm->tm_year -= 1900; mon = DD2bin(p);
tm->tm_mon = DD2bin(p) - 1; day = DD2bin(p);
tm->tm_mday = DD2bin(p); hour = DD2bin(p);
tm->tm_hour = DD2bin(p); min = DD2bin(p);
tm->tm_min = DD2bin(p); sec = DD2bin(p);
tm->tm_sec = DD2bin(p);
if (*p != 'Z') if (*p != 'Z')
goto unsupported_time; goto unsupported_time;
mon_len = month_lengths[mon];
if (mon == 2) {
if (year % 4 == 0) {
mon_len = 29;
if (year % 100 == 0) {
year /= 100;
if (year % 4 != 0)
mon_len = 28;
}
}
}
if (year < 1970 ||
mon < 1 || mon > 12 ||
day < 1 || day > mon_len ||
hour < 0 || hour > 23 ||
min < 0 || min > 59 ||
sec < 0 || sec > 59)
goto invalid_time;
*_t = mktime64(year, mon, day, hour, min, sec);
return 0; return 0;
unsupported_time: unsupported_time:
pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
tag, (int)vlen, (int)vlen, value); tag, (int)vlen, value);
return -EBADMSG;
invalid_time:
pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
tag, (int)vlen, value);
return -EBADMSG; return -EBADMSG;
} }
EXPORT_SYMBOL_GPL(x509_decode_time);
int x509_note_not_before(void *context, size_t hdrlen, int x509_note_not_before(void *context, size_t hdrlen,
unsigned char tag, unsigned char tag,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct x509_parse_context *ctx = context; struct x509_parse_context *ctx = context;
return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
} }
int x509_note_not_after(void *context, size_t hdrlen, int x509_note_not_after(void *context, size_t hdrlen,
...@@ -533,7 +578,7 @@ int x509_note_not_after(void *context, size_t hdrlen, ...@@ -533,7 +578,7 @@ int x509_note_not_after(void *context, size_t hdrlen,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct x509_parse_context *ctx = context; struct x509_parse_context *ctx = context;
return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
} }
/* /*
......
...@@ -23,8 +23,8 @@ struct x509_certificate { ...@@ -23,8 +23,8 @@ struct x509_certificate {
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */ struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */
struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */ struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */
struct tm valid_from; time64_t valid_from;
struct tm valid_to; time64_t valid_to;
const void *tbs; /* Signed data */ const void *tbs; /* Signed data */
unsigned tbs_size; /* Size of signed data */ unsigned tbs_size; /* Size of signed data */
unsigned raw_sig_size; /* Size of sigature */ unsigned raw_sig_size; /* Size of sigature */
...@@ -49,6 +49,9 @@ struct x509_certificate { ...@@ -49,6 +49,9 @@ struct x509_certificate {
*/ */
extern void x509_free_certificate(struct x509_certificate *cert); extern void x509_free_certificate(struct x509_certificate *cert);
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
extern int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen);
/* /*
* x509_public_key.c * x509_public_key.c
......
...@@ -302,14 +302,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) ...@@ -302,14 +302,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
} }
pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]); pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
cert->valid_from.tm_min, cert->valid_from.tm_sec);
pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n",
cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1,
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
cert->valid_to.tm_min, cert->valid_to.tm_sec);
pr_devel("Cert Signature: %s + %s\n", pr_devel("Cert Signature: %s + %s\n",
pkey_algo_name[cert->sig.pkey_algo], pkey_algo_name[cert->sig.pkey_algo],
hash_algo_name[cert->sig.pkey_hash_algo]); hash_algo_name[cert->sig.pkey_hash_algo]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment