return cert;
}
+/**
+ * Copy a certificate object, but drop any private key assignment.
+ *
+ * @param context A hx509 context.
+ * @param src Certificate object
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_copy_no_private_key(hx509_context context,
+ hx509_cert src,
+ heim_error_t *error)
+{
+ return hx509_cert_init(context, src->data, error);
+}
+
/**
* Allocate and init an hx509 certificate object containing only a private key
* (but no Certificate).
/* flags to hx509_certs_init */
#define HX509_CERTS_CREATE 0x01
#define HX509_CERTS_UNPROTECT_ALL 0x02
+#define HX509_CERTS_NO_PRIVATE_KEYS 0x04
+
+/* flags to hx509_certs_store */
+#define HX509_CERTS_STORE_NO_PRIVATE_KEYS 0x04
+
/* flags to hx509_set_error_string */
#define HX509_ERROR_APPEND 0x01
argument = "datetime"
help = "check that the certificate's notBefore is after the given time"
}
+ option = {
+ long = "has-private-key"
+ type = "flag"
+ help = "check that the certificate has a private key"
+ }
+ option = {
+ long = "lacks-private-key"
+ type = "flag"
+ help = "check that the certificate does not have a private key"
+ }
name = "acert"
min_args = "1"
max_args = "1"
time_t not_after_eq = 0;
time_t not_after_lt = 0;
time_t not_after_gt = 0;
+ int ret = 0;
if (opt->valid_now_flag) {
time_t now = time(NULL);
if (hx509_cert_get_notBefore(cert) > now) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate not valid yet\n");
- return -1;
+ ret = -1;
}
if (hx509_cert_get_notAfter(cert) < now) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate currently expired\n");
- return -1;
+ ret = -1;
}
}
if (opt->valid_at_string) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate not valid yet at %s\n",
opt->valid_at_string);
- return -1;
+ ret = -1;
}
if (hx509_cert_get_notAfter(cert) < at) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate expired before %s\n",
opt->valid_at_string);
- return -1;
+ ret = -1;
}
}
(not_before_gt && hx509_cert_get_notBefore(cert) <= not_before_gt)) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate notBefore not as requested\n");
- return -1;
+ ret = -1;
}
if ((not_after_eq && hx509_cert_get_notAfter(cert) != not_after_eq) ||
(not_after_lt && hx509_cert_get_notAfter(cert) >= not_after_lt) ||
(not_after_gt && hx509_cert_get_notAfter(cert) <= not_after_gt)) {
if (opt->verbose_flag)
fprintf(stderr, "Certificate notAfter not as requested\n");
- return -1;
+ ret = -1;
}
- return 0;
+ if (opt->has_private_key_flag && !hx509_cert_have_private_key(cert)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate does not have a private key\n");
+ ret = -1;
+ }
+
+ if (opt->lacks_private_key_flag && hx509_cert_have_private_key(cert)) {
+ if (opt->verbose_flag)
+ fprintf(stderr, "Certificate does not have a private key\n");
+ ret = -1;
+ }
+
+ return ret;
}
static int
if (e == NULL) {
if (wanted)
return -1;
- return acert1_validity(opt, cert);;
+ return acert1_validity(opt, cert);
}
for (i = 0; i < e->len; i++) {
unsigned int ref;
struct hx509_keyset_ops *ops;
void *ops_data;
+ int flags;
};
static struct hx509_keyset_ops *
* @param flags list of flags:
* - HX509_CERTS_CREATE create a new keystore of the specific TYPE.
* - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.
+ * - HX509_CERTS_NO_PRIVATE_KEYS does not load or permit adding private keys
* @param lock a lock that unlocks the certificates store, use NULL to
* select no password/certifictes/prompt lock (see @ref page_lock).
* @param certs return pointer, free with hx509_certs_free().
hx509_clear_error_string(context);
return ENOMEM;
}
+ c->flags = flags;
c->ops = ops;
c->ref = 1;
/**
* Write the certificate store to stable storage.
*
+ * Use the HX509_CERTS_STORE_NO_PRIVATE_KEYS flag to ensure that no private
+ * keys are stored, even if added.
+ *
* @param context A hx509 context.
* @param certs a certificate store to store.
- * @param flags currently unused, use 0.
+ * @param flags currently one flag is defined: HX509_CERTS_STORE_NO_PRIVATE_KEYS
* @param lock a lock that unlocks the certificates store, use NULL to
* select no password/certifictes/prompt lock (see @ref page_lock).
*
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
{
+ hx509_cert copy = NULL;
+ int ret;
+
if (certs->ops->add == NULL) {
hx509_set_error_string(context, 0, ENOENT,
"Keyset type %s doesn't support add operation",
return ENOENT;
}
- return (*certs->ops->add)(context, certs, certs->ops_data, cert);
+ if ((certs->flags & HX509_CERTS_NO_PRIVATE_KEYS) &&
+ hx509_cert_have_private_key(cert)) {
+ if ((copy = hx509_cert_copy_no_private_key(context, cert,
+ NULL)) == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Could not add certificate to store");
+ return ENOMEM;
+ }
+ cert = copy;
+ }
+
+ ret = (*certs->ops->add)(context, certs, certs->ops_data, cert);
+ hx509_cert_free(copy);
+ return ret;
}
/**
}
/**
- * Merge a certificate store into another. The from store is keep
- * intact.
+ * Merge one certificate store into another. The from store is kept intact.
*
* @param context a hx509 context.
* @param to the store to merge into.
*/
static int
-parse_certificate(hx509_context context, const char *fn,
+parse_certificate(hx509_context context, const char *fn, int flags,
struct hx509_collector *c,
const hx509_pem_header *headers,
const void *data, size_t len,
static int
try_decrypt(hx509_context context,
struct hx509_collector *collector,
+ int flags,
const AlgorithmIdentifier *alg,
const EVP_CIPHER *c,
const void *ivdata,
EVP_CIPHER_CTX_cleanup(&ctx);
}
- ret = _hx509_collector_private_key_add(context,
- collector,
- alg,
- NULL,
- &clear,
- NULL);
+ if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS))
+ ret = _hx509_collector_private_key_add(context, collector, alg, NULL,
+ &clear, NULL);
memset_s(clear.data, clear.length, 0, clear.length);
free(clear.data);
}
static int
-parse_pkcs8_private_key(hx509_context context, const char *fn,
+parse_pkcs8_private_key(hx509_context context, const char *fn, int flags,
struct hx509_collector *c,
const hx509_pem_header *headers,
const void *data, size_t length,
{
PKCS8PrivateKeyInfo ki;
heim_octet_string keydata;
-
int ret;
ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
if (ret)
return ret;
- keydata.data = rk_UNCONST(data);
- keydata.length = length;
-
- ret = _hx509_collector_private_key_add(context,
- c,
- &ki.privateKeyAlgorithm,
- NULL,
- &ki.privateKey,
- &keydata);
+ if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
+ keydata.data = rk_UNCONST(data);
+ keydata.length = length;
+ ret = _hx509_collector_private_key_add(context,
+ c,
+ &ki.privateKeyAlgorithm,
+ NULL,
+ &ki.privateKey,
+ &keydata);
+ }
free_PKCS8PrivateKeyInfo(&ki);
return ret;
}
static int
-parse_pem_private_key(hx509_context context, const char *fn,
+parse_pem_private_key(hx509_context context, const char *fn, int flags,
struct hx509_collector *c,
const hx509_pem_header *headers,
const void *data, size_t len,
password = pw->val[i];
passwordlen = strlen(password);
- ret = try_decrypt(context, c, ai, cipher, ivdata,
+ ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
password, passwordlen, data, len);
if (ret == 0) {
decrypted = 1;
ret = hx509_lock_prompt(lock, &prompt);
if (ret == 0)
- ret = try_decrypt(context, c, ai, cipher, ivdata, password,
- strlen(password), data, len);
+ ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
+ password, strlen(password), data, len);
/* XXX add password to lock password collection ? */
memset_s(password, sizeof(password), 0, sizeof(password));
}
free(ivdata);
- } else {
+ } else if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
heim_octet_string keydata;
keydata.data = rk_UNCONST(data);
keydata.length = len;
- ret = _hx509_collector_private_key_add(context, c, ai, NULL,
- &keydata, NULL);
+ ret = _hx509_collector_private_key_add(context, c, ai, NULL,
+ &keydata, NULL);
}
return ret;
struct pem_formats {
const char *name;
- int (*func)(hx509_context, const char *, struct hx509_collector *,
+ int (*func)(hx509_context, const char *, int, struct hx509_collector *,
const hx509_pem_header *, const void *, size_t,
const AlgorithmIdentifier *);
const AlgorithmIdentifier *(*ai)(void);
const char *q = formats[j].name;
if (strcasecmp(type, q) == 0) {
const AlgorithmIdentifier *ai = NULL;
+
if (formats[j].ai != NULL)
ai = (*formats[j].ai)();
- ret = (*formats[j].func)(context, NULL, pem_ctx->c,
- header, data, len, ai);
+ ret = (*formats[j].func)(context, NULL, pem_ctx->flags, pem_ctx->c,
+ header, data, len, ai);
if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
"Failed parseing PEM format %s", type);
for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
const AlgorithmIdentifier *ai = NULL;
+
if (formats[i].ai != NULL)
ai = (*formats[i].ai)();
- ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai);
+ ret = (*formats[i].func)(context, p, pem_ctx.flags, pem_ctx.c,
+ NULL, ptr, length, ai);
if (ret == 0)
break;
}
struct store_ctx {
FILE *f;
outformat format;
+ int store_flags;
};
static int HX509_LIB_CALL
if (data.data) {
fwrite(data.data, data.length, 1, sc->f);
free(data.data);
- } else if (_hx509_cert_private_key_exportable(c)) {
+ } else if (_hx509_cert_private_key_exportable(c) &&
+ !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
hx509_private_key key = _hx509_cert_private_key(c);
ret = _hx509_private_key_export(context, key,
}
break;
case USE_PEM:
- if (_hx509_cert_private_key_exportable(c)) {
+ if (_hx509_cert_private_key_exportable(c) &&
+ !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
heim_octet_string priv_key;
hx509_private_key key = _hx509_cert_private_key(c);
return ret;
}
rk_cloexec_file(sc.f);
+ sc.store_flags = flags;
sc.format = ksf->format;
ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
{
struct ks_keychain *ctx;
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+ hx509_set_error_string(context, 0, ENOTSUP,
+ "KEYCHAIN store does not support not reading "
+ "private keys");
+ return ENOTSUP;
+ }
+
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
hx509_clear_error_string(context);
*data = NULL;
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+ hx509_set_error_string(context, 0, ENOTSUP,
+ "PKCS#11 store does not support "
+ "HX509_CERTS_NO_PRIVATE_KEYS flag");
+ return ENOTSUP;
+ }
+
if (residue == NULL || residue[0] == '\0') {
hx509_set_error_string(context, 0, EINVAL,
"PKCS#11 store not specified");
struct ks_pkcs12 {
hx509_certs certs;
char *fn;
+ unsigned int store_no_priv_keys;
};
typedef int (*collector_func)(hx509_context,
struct hx509_collector *,
+ int,
const void *, size_t,
const PKCS12_Attributes *);
};
static void
-parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *,
- const void *, size_t, const PKCS12_Attributes *);
+parse_pkcs12_type(hx509_context, struct hx509_collector *, int,
+ const heim_oid *, const void *, size_t,
+ const PKCS12_Attributes *);
static const PKCS12_Attribute *
static int
keyBag_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
const heim_octet_string *os = NULL;
int ret;
+ if (flags & HX509_CERTS_NO_PRIVATE_KEYS)
+ return 0;
+
attr = find_attribute(attrs, &asn1_oid_id_pkcs_9_at_localKeyId);
if (attr)
os = &attr->attrValues;
static int
ShroudedKeyBag_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
if (ret)
return ret;
- ret = keyBag_parser(context, c, content.data, content.length, attrs);
+ ret = keyBag_parser(context, c, flags, content.data, content.length,
+ attrs);
der_free_octet_string(&content);
return ret;
}
static int
certBag_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
static int
parse_safe_content(hx509_context context,
struct hx509_collector *c,
+ int flags,
const unsigned char *p, size_t len)
{
PKCS12_SafeContents sc;
for (i = 0; i < sc.len ; i++)
parse_pkcs12_type(context,
c,
+ flags,
&sc.val[i].bagId,
sc.val[i].bagValue.data,
sc.val[i].bagValue.length,
static int
safeContent_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
ret = decode_PKCS12_OctetString(data, length, &os, NULL);
if (ret)
return ret;
- ret = parse_safe_content(context, c, os.data, os.length);
+ ret = parse_safe_content(context, c, flags, os.data, os.length);
der_free_octet_string(&os);
return ret;
}
static int
encryptedData_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
return ret;
if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
- ret = parse_safe_content(context, c, content.data, content.length);
+ ret = parse_safe_content(context, c, flags,
+ content.data, content.length);
der_free_octet_string(&content);
der_free_oid(&contentType);
static int
envelopedData_parser(hx509_context context,
struct hx509_collector *c,
+ int flags,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
}
if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
- ret = parse_safe_content(context, c, content.data, content.length);
+ ret = parse_safe_content(context, c, flags,
+ content.data, content.length);
der_free_octet_string(&content);
der_free_oid(&contentType);
static void
parse_pkcs12_type(hx509_context context,
struct hx509_collector *c,
+ int flags,
const heim_oid *oid,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
if (der_heim_oid_cmp(bagtypes[i].oid, oid) == 0)
- (*bagtypes[i].func)(context, c, data, length, attrs);
+ (*bagtypes[i].func)(context, c, flags, data, length, attrs);
}
static int
for (i = 0; i < as.len; i++)
parse_pkcs12_type(context,
c,
+ flags,
&as.val[i].contentType,
as.val[i].content->data,
as.val[i].content->length,
return 0;
}
+struct store_func_ctx {
+ PKCS12_AuthenticatedSafe as;
+ int store_flags;
+};
+
static int HX509_LIB_CALL
-store_func(hx509_context context, void *ctx, hx509_cert c)
+store_func(hx509_context context, void *d, hx509_cert c)
{
- PKCS12_AuthenticatedSafe *as = ctx;
+ struct store_func_ctx *ctx = d;
PKCS12_OctetString os;
PKCS12_CertBag cb;
size_t size;
if (ret)
goto out;
- ret = addBag(context, as, &asn1_oid_id_pkcs12_certBag, os.data, os.length);
+ ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_certBag, os.data,
+ os.length);
- if (_hx509_cert_private_key_exportable(c)) {
+ if (_hx509_cert_private_key_exportable(c) &&
+ !(ctx->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
hx509_private_key key = _hx509_cert_private_key(c);
PKCS8PrivateKeyInfo pki;
if (ret)
return ret;
- ret = addBag(context, as, &asn1_oid_id_pkcs12_keyBag, os.data, os.length);
+ ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_keyBag, os.data,
+ os.length);
if (ret)
return ret;
}
{
struct ks_pkcs12 *p12 = data;
PKCS12_PFX pfx;
- PKCS12_AuthenticatedSafe as;
+ struct store_func_ctx ctx;
PKCS12_OctetString asdata;
size_t size;
int ret;
- memset(&as, 0, sizeof(as));
+ memset(&ctx, 0, sizeof(ctx));
memset(&pfx, 0, sizeof(pfx));
+ ctx.store_flags = flags;
- ret = hx509_certs_iter_f(context, p12->certs, store_func, &as);
+ ret = hx509_certs_iter_f(context, p12->certs, store_func, &ctx);
if (ret)
goto out;
ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
- &as, &size, ret);
- free_PKCS12_AuthenticatedSafe(&as);
+ &ctx.as, &size, ret);
+ free_PKCS12_AuthenticatedSafe(&ctx.as);
if (ret)
return ret;
free(asdata.data);
out:
- free_PKCS12_AuthenticatedSafe(&as);
+ free_PKCS12_AuthenticatedSafe(&ctx.as);
free_PKCS12_PFX(&pfx);
return ret;