pkcs11: Change how unavailable attributes like CKA_TRUSTED are handled
If a PKCS#11 library/token doesn't provide one or more attributes via C_GetAttributeValue(), we get back CKR_ATTRIBUTE_TYPE_INVALID (similar for protected attributes where CKR_ATTRIBUTE_SENSITIVE is returned). This is not an error as the spec demands that all attributes have been processed with the unavailable attributes having set their length field to CK_UNAVAILABLE_INFORMATION. We use this to handle the CKA_TRUSTED attribute, which some tokens apparently don't support. We previously used a version check to remove the attribute from the call but even the latest spec doesn't make the attribute mandatory (it's just in a list of "common" attributes for CKO_CERTIFICATE objects, without a default value), so there are current tokens that don't support it and prevent us from enumerating certificates.
This commit is contained in:
parent
a90716cd4d
commit
6537be9c8d
|
@ -83,21 +83,21 @@ static void find_certificates(private_pkcs11_creds_t *this,
|
||||||
|
|
||||||
/* store result in a temporary list, avoid recursive operation */
|
/* store result in a temporary list, avoid recursive operation */
|
||||||
raw = linked_list_create();
|
raw = linked_list_create();
|
||||||
/* do not use trusted argument if not supported */
|
|
||||||
if (!(this->lib->get_features(this->lib) & PKCS11_TRUSTED_CERTS))
|
|
||||||
{
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
enumerator = this->lib->create_object_enumerator(this->lib,
|
enumerator = this->lib->create_object_enumerator(this->lib,
|
||||||
session, tmpl, countof(tmpl), attr, count);
|
session, tmpl, countof(tmpl), attr, count);
|
||||||
while (enumerator->enumerate(enumerator, &object))
|
while (enumerator->enumerate(enumerator, &object))
|
||||||
{
|
{
|
||||||
entry = malloc(sizeof(*entry));
|
if (attr[0].ulValueLen == CK_UNAVAILABLE_INFORMATION ||
|
||||||
entry->value = chunk_clone(
|
attr[1].ulValueLen == CK_UNAVAILABLE_INFORMATION)
|
||||||
chunk_create(attr[0].pValue, attr[0].ulValueLen));
|
{
|
||||||
entry->label = chunk_clone(
|
continue;
|
||||||
chunk_create(attr[1].pValue, attr[1].ulValueLen));
|
}
|
||||||
entry->trusted = trusted;
|
INIT(entry,
|
||||||
|
.value = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen)),
|
||||||
|
.label = chunk_clone(chunk_create(attr[1].pValue, attr[1].ulValueLen)),
|
||||||
|
/* assume trusted certificates if attribute is not available */
|
||||||
|
.trusted = attr[2].ulValueLen == CK_UNAVAILABLE_INFORMATION || trusted,
|
||||||
|
);
|
||||||
raw->insert_last(raw, entry);
|
raw->insert_last(raw, entry);
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
@ -339,7 +339,8 @@ certificate_t *pkcs11_creds_load(certificate_type_t type, va_list args)
|
||||||
}
|
}
|
||||||
certs = p11->create_object_enumerator(p11, session,
|
certs = p11->create_object_enumerator(p11, session,
|
||||||
tmpl, countof(tmpl), attr, countof(attr));
|
tmpl, countof(tmpl), attr, countof(attr));
|
||||||
if (certs->enumerate(certs, &object))
|
if (certs->enumerate(certs, &object) &&
|
||||||
|
attr[0].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
{
|
{
|
||||||
data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
|
data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
|
||||||
}
|
}
|
||||||
|
|
|
@ -624,6 +624,8 @@ typedef struct {
|
||||||
pkcs11_library_t *lib;
|
pkcs11_library_t *lib;
|
||||||
/* attributes to retrieve */
|
/* attributes to retrieve */
|
||||||
CK_ATTRIBUTE_PTR attr;
|
CK_ATTRIBUTE_PTR attr;
|
||||||
|
/* copy of the original attributes provided by the caller */
|
||||||
|
CK_ATTRIBUTE_PTR orig_attr;
|
||||||
/* number of attributes */
|
/* number of attributes */
|
||||||
CK_ULONG count;
|
CK_ULONG count;
|
||||||
/* object handle in case of a single object */
|
/* object handle in case of a single object */
|
||||||
|
@ -633,17 +635,36 @@ typedef struct {
|
||||||
} object_enumerator_t;
|
} object_enumerator_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free contents of attributes in a list
|
* Keep a copy of the original attribute values so we can restore them while
|
||||||
|
* enumerating e.g. if an attribute was unavailable for a particular object.
|
||||||
|
*/
|
||||||
|
static void init_attrs(object_enumerator_t *this)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
this->orig_attr = calloc(this->count, sizeof(CK_ATTRIBUTE));
|
||||||
|
for (i = 0; i < this->count; i++)
|
||||||
|
{
|
||||||
|
this->orig_attr[i] = this->attr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free contents of allocated attributes and reset them to their original
|
||||||
|
* values.
|
||||||
*/
|
*/
|
||||||
static void free_attrs(object_enumerator_t *this)
|
static void free_attrs(object_enumerator_t *this)
|
||||||
{
|
{
|
||||||
CK_ATTRIBUTE_PTR attr;
|
CK_ATTRIBUTE_PTR attr;
|
||||||
|
int i;
|
||||||
|
|
||||||
while (this->freelist->remove_last(this->freelist, (void**)&attr) == SUCCESS)
|
while (this->freelist->remove_last(this->freelist, (void**)&attr) == SUCCESS)
|
||||||
{
|
{
|
||||||
free(attr->pValue);
|
free(attr->pValue);
|
||||||
attr->pValue = NULL;
|
}
|
||||||
attr->ulValueLen = 0;
|
for (i = 0; i < this->count; i++)
|
||||||
|
{
|
||||||
|
this->attr[i] = this->orig_attr[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +708,9 @@ static bool get_attributes(object_enumerator_t *this, CK_OBJECT_HANDLE object)
|
||||||
/* get length of objects first */
|
/* get length of objects first */
|
||||||
rv = this->lib->f->C_GetAttributeValue(this->session, object,
|
rv = this->lib->f->C_GetAttributeValue(this->session, object,
|
||||||
this->attr, this->count);
|
this->attr, this->count);
|
||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK &&
|
||||||
|
rv != CKR_ATTRIBUTE_SENSITIVE &&
|
||||||
|
rv != CKR_ATTRIBUTE_TYPE_INVALID)
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "C_GetAttributeValue(NULL) error: %N", ck_rv_names, rv);
|
DBG1(DBG_CFG, "C_GetAttributeValue(NULL) error: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -695,8 +718,12 @@ static bool get_attributes(object_enumerator_t *this, CK_OBJECT_HANDLE object)
|
||||||
/* allocate required chunks */
|
/* allocate required chunks */
|
||||||
for (i = 0; i < this->count; i++)
|
for (i = 0; i < this->count; i++)
|
||||||
{
|
{
|
||||||
if (this->attr[i].pValue == NULL &&
|
if (this->attr[i].ulValueLen == CK_UNAVAILABLE_INFORMATION)
|
||||||
this->attr[i].ulValueLen != 0 && this->attr[i].ulValueLen != -1)
|
{ /* reset this unavailable attribute before the next call */
|
||||||
|
this->attr[i] = this->orig_attr[i];
|
||||||
|
}
|
||||||
|
else if (this->attr[i].pValue == NULL &&
|
||||||
|
this->attr[i].ulValueLen != 0)
|
||||||
{
|
{
|
||||||
this->attr[i].pValue = malloc(this->attr[i].ulValueLen);
|
this->attr[i].pValue = malloc(this->attr[i].ulValueLen);
|
||||||
this->freelist->insert_last(this->freelist, &this->attr[i]);
|
this->freelist->insert_last(this->freelist, &this->attr[i]);
|
||||||
|
@ -705,9 +732,10 @@ static bool get_attributes(object_enumerator_t *this, CK_OBJECT_HANDLE object)
|
||||||
/* get the data */
|
/* get the data */
|
||||||
rv = this->lib->f->C_GetAttributeValue(this->session, object,
|
rv = this->lib->f->C_GetAttributeValue(this->session, object,
|
||||||
this->attr, this->count);
|
this->attr, this->count);
|
||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK &&
|
||||||
|
rv != CKR_ATTRIBUTE_SENSITIVE &&
|
||||||
|
rv != CKR_ATTRIBUTE_TYPE_INVALID)
|
||||||
{
|
{
|
||||||
free_attrs(this);
|
|
||||||
DBG1(DBG_CFG, "C_GetAttributeValue() error: %N", ck_rv_names, rv);
|
DBG1(DBG_CFG, "C_GetAttributeValue() error: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -774,6 +802,7 @@ METHOD(enumerator_t, object_destroy, void,
|
||||||
}
|
}
|
||||||
free_attrs(this);
|
free_attrs(this);
|
||||||
this->freelist->destroy(this->freelist);
|
this->freelist->destroy(this->freelist);
|
||||||
|
free(this->orig_attr);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,6 +833,7 @@ METHOD(pkcs11_library_t, create_object_enumerator, enumerator_t*,
|
||||||
.count = acount,
|
.count = acount,
|
||||||
.freelist = linked_list_create(),
|
.freelist = linked_list_create(),
|
||||||
);
|
);
|
||||||
|
init_attrs(enumerator);
|
||||||
return &enumerator->public;
|
return &enumerator->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,6 +856,7 @@ METHOD(pkcs11_library_t, create_object_attr_enumerator, enumerator_t*,
|
||||||
.object = object,
|
.object = object,
|
||||||
.freelist = linked_list_create(),
|
.freelist = linked_list_create(),
|
||||||
);
|
);
|
||||||
|
init_attrs(enumerator);
|
||||||
return &enumerator->public;
|
return &enumerator->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,7 +1064,6 @@ static void check_features(private_pkcs11_library_t *this, CK_INFO *info)
|
||||||
{
|
{
|
||||||
if (has_version(info, 2, 20))
|
if (has_version(info, 2, 20))
|
||||||
{
|
{
|
||||||
this->features |= PKCS11_TRUSTED_CERTS;
|
|
||||||
this->features |= PKCS11_ALWAYS_AUTH_KEYS;
|
this->features |= PKCS11_ALWAYS_AUTH_KEYS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,8 @@ typedef struct pkcs11_library_t pkcs11_library_t;
|
||||||
* Optional PKCS#11 features some libraries support, some not
|
* Optional PKCS#11 features some libraries support, some not
|
||||||
*/
|
*/
|
||||||
enum pkcs11_feature_t {
|
enum pkcs11_feature_t {
|
||||||
/** CKA_TRUSTED attribute supported for certificate objects */
|
|
||||||
PKCS11_TRUSTED_CERTS = (1<<0),
|
|
||||||
/** CKA_ALWAYS_AUTHENTICATE attribute supported for private keys */
|
/** CKA_ALWAYS_AUTHENTICATE attribute supported for private keys */
|
||||||
PKCS11_ALWAYS_AUTH_KEYS = (1<<1),
|
PKCS11_ALWAYS_AUTH_KEYS = (1<<0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +71,8 @@ struct pkcs11_library_t {
|
||||||
* An optional attribute array is automatically filled in with the
|
* An optional attribute array is automatically filled in with the
|
||||||
* objects associated attributes. If the value of an output attribute
|
* objects associated attributes. If the value of an output attribute
|
||||||
* is NULL, the value gets allocated/freed during enumeration.
|
* is NULL, the value gets allocated/freed during enumeration.
|
||||||
|
* For attributes that are unavailable, the length field will be set to
|
||||||
|
* CK_UNAVAILABLE_INFORMATION.
|
||||||
*
|
*
|
||||||
* @param session session to use
|
* @param session session to use
|
||||||
* @param tmpl search template
|
* @param tmpl search template
|
||||||
|
@ -92,6 +92,8 @@ struct pkcs11_library_t {
|
||||||
* The given attribute array is automatically filled in with the
|
* The given attribute array is automatically filled in with the
|
||||||
* associated attributes. If the value of an output attribute is NULL,
|
* associated attributes. If the value of an output attribute is NULL,
|
||||||
* the required memory gets allocated/freed during enumeration.
|
* the required memory gets allocated/freed during enumeration.
|
||||||
|
* For attributes that are unavailable, the length field will be set to
|
||||||
|
* CK_UNAVAILABLE_INFORMATION.
|
||||||
*
|
*
|
||||||
* @param session session to use
|
* @param session session to use
|
||||||
* @param object object handle
|
* @param object object handle
|
||||||
|
|
|
@ -389,4 +389,3 @@ pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
|
||||||
|
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -626,6 +626,9 @@ static pkcs11_library_t* find_lib_and_keyid_by_skid(chunk_t keyid_chunk,
|
||||||
certs = p11->create_object_enumerator(p11, session, tmpl, countof(tmpl),
|
certs = p11->create_object_enumerator(p11, session, tmpl, countof(tmpl),
|
||||||
attr, countof(attr));
|
attr, countof(attr));
|
||||||
while (certs->enumerate(certs, &object))
|
while (certs->enumerate(certs, &object))
|
||||||
|
{
|
||||||
|
if (attr[0].ulValueLen != CK_UNAVAILABLE_INFORMATION &&
|
||||||
|
attr[1].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
{
|
{
|
||||||
INIT(entry,
|
INIT(entry,
|
||||||
.value = chunk_clone(
|
.value = chunk_clone(
|
||||||
|
@ -635,6 +638,7 @@ static pkcs11_library_t* find_lib_and_keyid_by_skid(chunk_t keyid_chunk,
|
||||||
);
|
);
|
||||||
raw->insert_last(raw, entry);
|
raw->insert_last(raw, entry);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
certs->destroy(certs);
|
certs->destroy(certs);
|
||||||
|
|
||||||
while (raw->remove_first(raw, (void**)&entry) == SUCCESS)
|
while (raw->remove_first(raw, (void**)&entry) == SUCCESS)
|
||||||
|
@ -708,7 +712,8 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
|
||||||
}
|
}
|
||||||
enumerator = this->lib->create_object_enumerator(this->lib,
|
enumerator = this->lib->create_object_enumerator(this->lib,
|
||||||
this->session, tmpl, countof(tmpl), attr, count);
|
this->session, tmpl, countof(tmpl), attr, count);
|
||||||
if (enumerator->enumerate(enumerator, &object))
|
if (enumerator->enumerate(enumerator, &object) &&
|
||||||
|
attr[0].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
{
|
{
|
||||||
this->type = KEY_RSA;
|
this->type = KEY_RSA;
|
||||||
switch (type)
|
switch (type)
|
||||||
|
@ -717,7 +722,10 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
|
||||||
this->type = KEY_ECDSA;
|
this->type = KEY_ECDSA;
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case CKK_RSA:
|
case CKK_RSA:
|
||||||
|
if (attr[1].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
|
{
|
||||||
this->reauth = reauth;
|
this->reauth = reauth;
|
||||||
|
}
|
||||||
this->object = object;
|
this->object = object;
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
break;
|
break;
|
||||||
|
@ -803,7 +811,8 @@ static public_key_t* find_pubkey_in_certs(private_pkcs11_private_key_t *this,
|
||||||
|
|
||||||
enumerator = this->lib->create_object_enumerator(this->lib, this->session,
|
enumerator = this->lib->create_object_enumerator(this->lib, this->session,
|
||||||
tmpl, countof(tmpl), attr, countof(attr));
|
tmpl, countof(tmpl), attr, countof(attr));
|
||||||
if (enumerator->enumerate(enumerator, &object))
|
if (enumerator->enumerate(enumerator, &object) &&
|
||||||
|
attr[0].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
{
|
{
|
||||||
data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
|
data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
|
||||||
}
|
}
|
||||||
|
|
|
@ -888,7 +888,8 @@ static private_pkcs11_public_key_t *find_key_by_keyid(pkcs11_library_t *p11,
|
||||||
|
|
||||||
enumerator = p11->create_object_enumerator(p11, session, tmpl, count, attr,
|
enumerator = p11->create_object_enumerator(p11, session, tmpl, count, attr,
|
||||||
countof(attr));
|
countof(attr));
|
||||||
if (enumerator->enumerate(enumerator, &object))
|
if (enumerator->enumerate(enumerator, &object) &&
|
||||||
|
attr[0].ulValueLen != CK_UNAVAILABLE_INFORMATION)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue