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: David Howells <dhowells@redhat.com> Reviewed-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
Родитель
770f2b9876
Коммит
fd19a3d195
|
@ -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,
|
||||||
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 {
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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]);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче