support parsing PEM CRL files and printing revoke contexts
This commit is contained in:
@@ -408,6 +408,17 @@ command = {
|
|||||||
argument="ocsp-response-file ..."
|
argument="ocsp-response-file ..."
|
||||||
help = "Print the OCSP responses"
|
help = "Print the OCSP responses"
|
||||||
}
|
}
|
||||||
|
command = {
|
||||||
|
name = "revoke-print"
|
||||||
|
option = {
|
||||||
|
long = "verbose"
|
||||||
|
type = "flag"
|
||||||
|
help = "verbose"
|
||||||
|
}
|
||||||
|
min_args="1"
|
||||||
|
argument="ocsp/crl files"
|
||||||
|
help = "Print the OCSP/CRL files"
|
||||||
|
}
|
||||||
command = {
|
command = {
|
||||||
name = "request-create"
|
name = "request-create"
|
||||||
option = {
|
option = {
|
||||||
|
@@ -1135,6 +1135,45 @@ ocsp_print(struct ocsp_print_options *opt, int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
revoke_print(struct revoke_print_options *opt, int argc, char **argv)
|
||||||
|
{
|
||||||
|
hx509_revoke_ctx revoke_ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = hx509_revoke_init(context, &revoke_ctx);
|
||||||
|
if (ret)
|
||||||
|
errx(1, "hx509_revoke_init: %d", ret);
|
||||||
|
|
||||||
|
while(argc--) {
|
||||||
|
char *s = *argv++;
|
||||||
|
|
||||||
|
if (strncmp(s, "crl:", 4) == 0) {
|
||||||
|
s += 4;
|
||||||
|
|
||||||
|
ret = hx509_revoke_add_crl(context, revoke_ctx, s);
|
||||||
|
if (ret)
|
||||||
|
errx(1, "hx509_revoke_add_crl: %s: %d", s, ret);
|
||||||
|
|
||||||
|
} else if (strncmp(s, "ocsp:", 4) == 0) {
|
||||||
|
s += 5;
|
||||||
|
|
||||||
|
ret = hx509_revoke_add_ocsp(context, revoke_ctx, s);
|
||||||
|
if (ret)
|
||||||
|
errx(1, "hx509_revoke_add_ocsp: %s: %d", s, ret);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
errx(1, "unknown option to verify: `%s'\n", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hx509_revoke_print(context, revoke_ctx, stdout);
|
||||||
|
if (ret)
|
||||||
|
warnx("hx509_revoke_print: %d", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@@ -561,27 +561,18 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
load_crl(const char *path, time_t *t, CRLCertificateList *crl)
|
crl_parser(hx509_context context, const char *type,
|
||||||
|
const hx509_pem_header *header,
|
||||||
|
const void *data, size_t len, void *ctx)
|
||||||
{
|
{
|
||||||
size_t length, size;
|
CRLCertificateList *crl = (CRLCertificateList *)ctx;
|
||||||
struct stat sb;
|
size_t size;
|
||||||
void *data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(crl, 0, sizeof(*crl));
|
if (strcasecmp("X509 CRL", type) != 0)
|
||||||
|
return HX509_CRYPTO_SIG_INVALID_FORMAT;
|
||||||
|
|
||||||
ret = rk_undumpdata(path, &data, &length);
|
ret = decode_CRLCertificateList(data, len, crl, &size);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = stat(path, &sb);
|
|
||||||
if (ret)
|
|
||||||
return errno;
|
|
||||||
|
|
||||||
*t = sb.st_mtime;
|
|
||||||
|
|
||||||
ret = decode_CRLCertificateList(data, length, crl, &size);
|
|
||||||
rk_xfree(data);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -590,9 +581,47 @@ load_crl(const char *path, time_t *t, CRLCertificateList *crl)
|
|||||||
free_CRLCertificateList(crl);
|
free_CRLCertificateList(crl);
|
||||||
return HX509_CRYPTO_SIG_INVALID_FORMAT;
|
return HX509_CRYPTO_SIG_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList *crl)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
size_t length;
|
||||||
|
void *data;
|
||||||
|
FILE *f;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(crl, 0, sizeof(*crl));
|
||||||
|
|
||||||
|
ret = stat(path, &sb);
|
||||||
|
if (ret)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
*t = sb.st_mtime;
|
||||||
|
|
||||||
|
if ((f = fopen(path, "r")) == NULL)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
rk_cloexec_file(f);
|
||||||
|
|
||||||
|
ret = hx509_pem_read(context, f, crl_parser, crl);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (ret == HX509_PARSING_KEY_FAILED) {
|
||||||
|
|
||||||
|
ret = rk_undumpdata(path, &data, &length);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = crl_parser(context, "X509 CRL", NULL, data, length, crl);
|
||||||
|
rk_xfree(data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a CRL file to the revokation context.
|
* Add a CRL file to the revokation context.
|
||||||
*
|
*
|
||||||
@@ -644,7 +673,8 @@ hx509_revoke_add_crl(hx509_context context,
|
|||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = load_crl(path,
|
ret = load_crl(context,
|
||||||
|
path,
|
||||||
&ctx->crls.val[ctx->crls.len].last_modfied,
|
&ctx->crls.val[ctx->crls.len].last_modfied,
|
||||||
&ctx->crls.val[ctx->crls.len].crl);
|
&ctx->crls.val[ctx->crls.len].crl);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -674,7 +704,6 @@ hx509_revoke_add_crl(hx509_context context,
|
|||||||
* @ingroup hx509_revoke
|
* @ingroup hx509_revoke
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
hx509_revoke_verify(hx509_context context,
|
hx509_revoke_verify(hx509_context context,
|
||||||
hx509_revoke_ctx ctx,
|
hx509_revoke_ctx ctx,
|
||||||
@@ -781,7 +810,7 @@ hx509_revoke_verify(hx509_context context,
|
|||||||
if (ret == 0 && crl->last_modfied != sb.st_mtime) {
|
if (ret == 0 && crl->last_modfied != sb.st_mtime) {
|
||||||
CRLCertificateList cl;
|
CRLCertificateList cl;
|
||||||
|
|
||||||
ret = load_crl(crl->path, &crl->last_modfied, &cl);
|
ret = load_crl(context, crl->path, &crl->last_modfied, &cl);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
free_CRLCertificateList(&crl->crl);
|
free_CRLCertificateList(&crl->crl);
|
||||||
crl->crl = cl;
|
crl->crl = cl;
|
||||||
@@ -1064,6 +1093,140 @@ printable_time(time_t t)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
print_ocsp(hx509_context context, struct revoke_ocsp *ocsp, FILE *out)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
fprintf(out, "signer: ");
|
||||||
|
|
||||||
|
switch(ocsp->ocsp.tbsResponseData.responderID.element) {
|
||||||
|
case choice_OCSPResponderID_byName: {
|
||||||
|
hx509_name n;
|
||||||
|
char *s;
|
||||||
|
_hx509_name_from_Name(&ocsp->ocsp.tbsResponseData.responderID.u.byName, &n);
|
||||||
|
hx509_name_to_string(n, &s);
|
||||||
|
hx509_name_free(&n);
|
||||||
|
fprintf(out, " byName: %s\n", s);
|
||||||
|
free(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case choice_OCSPResponderID_byKey: {
|
||||||
|
char *s;
|
||||||
|
hex_encode(ocsp->ocsp.tbsResponseData.responderID.u.byKey.data,
|
||||||
|
ocsp->ocsp.tbsResponseData.responderID.u.byKey.length,
|
||||||
|
&s);
|
||||||
|
fprintf(out, " byKey: %s\n", s);
|
||||||
|
free(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_hx509_abort("choice_OCSPResponderID unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "producedAt: %s\n",
|
||||||
|
printable_time(ocsp->ocsp.tbsResponseData.producedAt));
|
||||||
|
|
||||||
|
fprintf(out, "replies: %d\n", ocsp->ocsp.tbsResponseData.responses.len);
|
||||||
|
|
||||||
|
for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
|
||||||
|
const char *status;
|
||||||
|
switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
|
||||||
|
case choice_OCSPCertStatus_good:
|
||||||
|
status = "good";
|
||||||
|
break;
|
||||||
|
case choice_OCSPCertStatus_revoked:
|
||||||
|
status = "revoked";
|
||||||
|
break;
|
||||||
|
case choice_OCSPCertStatus_unknown:
|
||||||
|
status = "unknown";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
status = "element unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "\t%zu. status: %s\n", i, status);
|
||||||
|
|
||||||
|
fprintf(out, "\tthisUpdate: %s\n",
|
||||||
|
printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
|
||||||
|
if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate)
|
||||||
|
fprintf(out, "\tproducedAt: %s\n",
|
||||||
|
printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "appended certs:\n");
|
||||||
|
if (ocsp->certs)
|
||||||
|
ret = hx509_certs_iter_f(context, ocsp->certs, hx509_ci_print_names, out);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
print_crl(hx509_context context, struct revoke_crl *crl, FILE *out)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
hx509_name n;
|
||||||
|
char *s;
|
||||||
|
_hx509_name_from_Name(&crl->crl.tbsCertList.issuer, &n);
|
||||||
|
hx509_name_to_string(n, &s);
|
||||||
|
hx509_name_free(&n);
|
||||||
|
fprintf(out, " issuer: %s\n", s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, " thisUpdate: %s\n",
|
||||||
|
printable_time(_hx509_Time2time_t(&crl->crl.tbsCertList.thisUpdate)));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
hx509_revoke_print(hx509_context context,
|
||||||
|
hx509_revoke_ctx ctx,
|
||||||
|
FILE *out)
|
||||||
|
{
|
||||||
|
int saved_ret = 0, ret;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
for (n = 0; n < ctx->ocsps.len; n++) {
|
||||||
|
struct revoke_ocsp *ocsp = &ctx->ocsps.val[n];
|
||||||
|
|
||||||
|
fprintf(out, "OCSP %s\n", ocsp->path);
|
||||||
|
|
||||||
|
ret = print_ocsp(context, ocsp, out);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(out, "failure printing OCSP: %d\n", ret);
|
||||||
|
saved_ret = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < ctx->crls.len; n++) {
|
||||||
|
struct revoke_crl *crl = &ctx->crls.val[n];
|
||||||
|
|
||||||
|
fprintf(out, "CRL %s\n", crl->path);
|
||||||
|
|
||||||
|
ret = print_crl(context, crl, out);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(out, "failure printing CRL: %d\n", ret);
|
||||||
|
saved_ret = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return saved_ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print the OCSP reply stored in a file.
|
* Print the OCSP reply stored in a file.
|
||||||
*
|
*
|
||||||
@@ -1081,7 +1244,6 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
|
|||||||
{
|
{
|
||||||
struct revoke_ocsp ocsp;
|
struct revoke_ocsp ocsp;
|
||||||
int ret;
|
int ret;
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (out == NULL)
|
if (out == NULL)
|
||||||
out = stdout;
|
out = stdout;
|
||||||
@@ -1098,67 +1260,7 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(out, "signer: ");
|
ret = print_ocsp(context, &ocsp, out);
|
||||||
|
|
||||||
switch(ocsp.ocsp.tbsResponseData.responderID.element) {
|
|
||||||
case choice_OCSPResponderID_byName: {
|
|
||||||
hx509_name n;
|
|
||||||
char *s;
|
|
||||||
_hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
|
|
||||||
hx509_name_to_string(n, &s);
|
|
||||||
hx509_name_free(&n);
|
|
||||||
fprintf(out, " byName: %s\n", s);
|
|
||||||
free(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case choice_OCSPResponderID_byKey: {
|
|
||||||
char *s;
|
|
||||||
hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
|
|
||||||
ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
|
|
||||||
&s);
|
|
||||||
fprintf(out, " byKey: %s\n", s);
|
|
||||||
free(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
_hx509_abort("choice_OCSPResponderID unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(out, "producedAt: %s\n",
|
|
||||||
printable_time(ocsp.ocsp.tbsResponseData.producedAt));
|
|
||||||
|
|
||||||
fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
|
|
||||||
|
|
||||||
for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
|
|
||||||
const char *status;
|
|
||||||
switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
|
|
||||||
case choice_OCSPCertStatus_good:
|
|
||||||
status = "good";
|
|
||||||
break;
|
|
||||||
case choice_OCSPCertStatus_revoked:
|
|
||||||
status = "revoked";
|
|
||||||
break;
|
|
||||||
case choice_OCSPCertStatus_unknown:
|
|
||||||
status = "unknown";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = "element unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(out, "\t%zu. status: %s\n", i, status);
|
|
||||||
|
|
||||||
fprintf(out, "\tthisUpdate: %s\n",
|
|
||||||
printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
|
|
||||||
if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
|
|
||||||
fprintf(out, "\tproducedAt: %s\n",
|
|
||||||
printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(out, "appended certs:\n");
|
|
||||||
if (ocsp.certs)
|
|
||||||
ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
|
|
||||||
|
|
||||||
free_ocsp(&ocsp);
|
free_ocsp(&ocsp);
|
||||||
return ret;
|
return ret;
|
||||||
|
Reference in New Issue
Block a user