diff --git a/kuser/heimtools-commands.in b/kuser/heimtools-commands.in index 97ef10923..ea8b073f6 100644 --- a/kuser/heimtools-commands.in +++ b/kuser/heimtools-commands.in @@ -270,8 +270,8 @@ command = { option = { long = "test" short = "t" - type = "flag" - help = "exit successfully if certificate and private key are in credential cache" + type = "integer" + help = "check for certificate with at least given time left" } option = { name = "private-key" diff --git a/kuser/kx509.1 b/kuser/kx509.1 index 76be02259..1fb3ce2af 100644 --- a/kuser/kx509.1 +++ b/kuser/kx509.1 @@ -56,8 +56,8 @@ .Fl Fl extract .Xc .Oc -.Oo Fl t \*(Ba Xo -.Fl Fl test +.Oo Fl t Ar time-left \*(Ba Xo +.Fl Fl test= Ns Ar time-left .Xc .Oc .Oo Fl C Ar PKCS10:filename \*(Ba Xo @@ -89,9 +89,14 @@ Options supported: .It Fl c Ar cache , Fl Fl cache= Ns Ar cache credential cache to use (if not given, then the default will be used). -.It Fl t , Fl Fl test +.It Fl t Ar time-left , Fl Fl test= Ns Ar time-left Test for there being an active and valid certificate in the -credential cache. +credential cache, with at least +.Ar time-left +seconds left of valid life. If given with the +.Fl o +then the certificates in the hx509 store are tested along with +those in the credentials cache (if any). .It Fl x , Fl Fl extract Extract, rather than acquire credentials. .It Fl s , Fl Fl save diff --git a/kuser/kx509.c b/kuser/kx509.c index 4ec2de2c5..831c5aece 100644 --- a/kuser/kx509.c +++ b/kuser/kx509.c @@ -39,14 +39,30 @@ #include "../lib/krb5/krb5_locl.h" #include "hx509-private.h" +struct validate_store { + size_t ncerts; + int grace; +}; + +static int +validate1(hx509_context hx509ctx, void *d, hx509_cert cert) +{ + struct validate_store *v = d; + + if (hx509_cert_get_notAfter(cert) < time(NULL) + v->grace) + return HX509_CERT_USED_AFTER_TIME; + v->ncerts++; + return 0; +} + static void validate(krb5_context context, + int grace, const char *hx509_store, krb5_data *der_cert, krb5_data *pkcs8_priv_key) { hx509_context hx509ctx = NULL; - hx509_private_key key = NULL; hx509_cert cert; krb5_error_code ret; @@ -54,19 +70,44 @@ validate(krb5_context context, if (ret) krb5_err(context, 1, ret, "hx509 context init"); - cert = hx509_cert_init_data(hx509ctx, der_cert->data, - der_cert->length, NULL); - if (cert == NULL) - krb5_err(context, 1, errno, "certificate could not be loaded"); - ret = hx509_parse_private_key(hx509ctx, NULL, pkcs8_priv_key->data, - pkcs8_priv_key->length, - HX509_KEY_FORMAT_PKCS8, &key); - if (ret) - krb5_err(context, 1, ret, "certificate could not be loaded"); - if (hx509_cert_get_notAfter(cert) < time(NULL)) - krb5_errx(context, 1, "certificate is expired"); - hx509_private_key_free(&key); - hx509_cert_free(cert); + if (der_cert->data && pkcs8_priv_key->data) { + hx509_private_key key = NULL; + + cert = hx509_cert_init_data(hx509ctx, der_cert->data, + der_cert->length, NULL); + if (cert == NULL) + krb5_err(context, 1, errno, "certificate could not be loaded"); + ret = hx509_parse_private_key(hx509ctx, NULL, pkcs8_priv_key->data, + pkcs8_priv_key->length, + HX509_KEY_FORMAT_PKCS8, &key); + if (ret) + krb5_err(context, 1, ret, "certificate could not be loaded"); + if (hx509_cert_get_notAfter(cert) < time(NULL) + grace) + krb5_errx(context, 1, "certificate is expired"); + hx509_private_key_free(&key); + hx509_cert_free(cert); + } + if (hx509_store) { + struct validate_store v; + hx509_certs certs; + + v.ncerts = 0; + v.grace = grace; + + ret = hx509_certs_init(hx509ctx, hx509_store, 0, NULL, &certs); + if (ret) + krb5_err(context, 1, ret, "could not read hx509 store %s", + hx509_store); + ret = hx509_certs_iter_f(hx509ctx, certs, validate1, &v); + if (ret) + krb5_err(context, 1, ret, "at least one certificate in %s expired", + hx509_store); + if (!v.ncerts) + krb5_errx(context, 1, "no certificates in %s", hx509_store); + + hx509_certs_free(&certs); + } + hx509_context_free(&hx509ctx); } @@ -124,7 +165,8 @@ store(krb5_context context, hx509_store = krb5_config_get_string(context, NULL, "libdefaults", "kx509_store", NULL); if (hx509_store) { - ret = _krb5_expand_path_tokens(context, hx509_store, 1, &store_exp); + ret = _krb5_expand_path_tokens(context, hx509_store, 1, + &store_exp); if (ret) krb5_err(context, 1, ret, "expanding tokens in default " "hx509 store"); @@ -202,28 +244,32 @@ kx509(struct kx509_options *opt, int argc, char **argv) if (opt->save_flag) ccout = cc; - if (opt->test_flag && + if (opt->test_integer && (opt->extract_flag || opt->csr_string || opt->private_key_string)) krb5_errx(context, 1, "--test is exclusive of --extract, --csr, and " "--private-key"); if (opt->extract_flag && (opt->csr_string || opt->private_key_string)) - krb5_errx(context, 1, "--extract is exclusive of --csr and --private-key"); + krb5_errx(context, 1, "--extract is exclusive of --csr and " + "--private-key"); - if (opt->test_flag || opt->extract_flag) { + if (opt->test_integer || opt->extract_flag) { krb5_data der_cert, pkcs8_key, chain; der_cert.data = pkcs8_key.data = chain.data = NULL; der_cert.length = pkcs8_key.length = chain.length = 0; ret = krb5_cc_get_config(context, cc, NULL, "kx509cert", &der_cert); if (ret == 0) - ret = krb5_cc_get_config(context, cc, NULL, "kx509key", &pkcs8_key); + ret = krb5_cc_get_config(context, cc, NULL, "kx509key", + &pkcs8_key); if (ret == 0) - ret = krb5_cc_get_config(context, cc, NULL, "kx509cert-chain", &chain); + ret = krb5_cc_get_config(context, cc, NULL, "kx509cert-chain", + &chain); if (ret) krb5_err(context, 1, ret, "no certificate in credential cache"); - if (opt->test_flag) - validate(context, opt->out_string, &der_cert, &pkcs8_key); + if (opt->test_integer) + validate(context, opt->test_integer, opt->out_string, &der_cert, + &pkcs8_key); else store(context, opt->out_string, &der_cert, &pkcs8_key, &chain); krb5_data_free(&pkcs8_key); @@ -240,13 +286,15 @@ kx509(struct kx509_options *opt, int argc, char **argv) if (ret == 0 && opt->csr_string) set_csr(context, req, opt->csr_string); if (ret == 0 && opt->private_key_string) - ret = krb5_kx509_ctx_set_key(context, req, opt->private_key_string); + ret = krb5_kx509_ctx_set_key(context, req, + opt->private_key_string); if (ret) krb5_err(context, 1, ret, "could not setup kx509 request options"); ret = krb5_kx509_ext(context, req, cc, opt->out_string, ccout); if (ret) - krb5_err(context, 1, ret, "could not acquire certificate with kx509"); + krb5_err(context, 1, ret, + "could not acquire certificate with kx509"); krb5_kx509_ctx_free(context, &req); }