From 14e09211c3d50eb06825090c9765e4382cf52f19 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 14 Dec 2014 19:42:18 +0000 Subject: [PATCH 1/3] Stop _pkcs11h_util_hexToBinary() checking for trailing NUL We are going to want to use this for parsing %XX hex escapes in RFC7512 PKCS#11 URIs, where we cannot expect a trailing NUL. Since there's only one existing caller at the moment, it's simple just to let the caller have responsibility for that check. Signed-off-by: David Woodhouse --- lib/pkcs11h-serialization.c | 8 +++++++- lib/pkcs11h-util.c | 7 +------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/pkcs11h-serialization.c b/lib/pkcs11h-serialization.c index 74b4ca7..a45a6c5 100644 --- a/lib/pkcs11h-serialization.c +++ b/lib/pkcs11h-serialization.c @@ -368,6 +368,7 @@ pkcs11h_certificate_deserializeCertificateId ( CK_RV rv = CKR_FUNCTION_FAILED; char *p = NULL; char *_sz = NULL; + size_t id_hex_len; _PKCS11H_ASSERT (p_certificate_id!=NULL); _PKCS11H_ASSERT (sz!=NULL); @@ -413,7 +414,12 @@ pkcs11h_certificate_deserializeCertificateId ( goto cleanup; } - certificate_id->attrCKA_ID_size = strlen (p)/2; + id_hex_len = strlen (p); + if (id_hex_len & 1) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto cleanup; + } + certificate_id->attrCKA_ID_size = id_hex_len/2; if ( (rv = _pkcs11h_mem_malloc ( diff --git a/lib/pkcs11h-util.c b/lib/pkcs11h-util.c index 7325db4..7dfe9a3 100644 --- a/lib/pkcs11h-util.c +++ b/lib/pkcs11h-util.c @@ -109,12 +109,7 @@ _pkcs11h_util_hexToBinary ( p++; } - if (*p != '\x0') { - return CKR_ATTRIBUTE_VALUE_INVALID; - } - else { - return CKR_OK; - } + return CKR_OK; } CK_RV From 4d5280da8df591aab701dff4493d13a835a9b29c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 10 Dec 2014 14:00:21 +0000 Subject: [PATCH 2/3] Accept RFC7512-compliant PKCS#11 URIs as serialized token/certificate IDs The old format is still accepted for compatibility. Signed-off-by: David Woodhouse --- lib/pkcs11h-serialization.c | 305 ++++++++++++++++++++++++++++++------ 1 file changed, 256 insertions(+), 49 deletions(-) diff --git a/lib/pkcs11h-serialization.c b/lib/pkcs11h-serialization.c index a45a6c5..390ac0e 100644 --- a/lib/pkcs11h-serialization.c +++ b/lib/pkcs11h-serialization.c @@ -60,6 +60,26 @@ #if defined(ENABLE_PKCS11H_TOKEN) || defined(ENABLE_PKCS11H_CERTIFICATE) +#define URI_SCHEME "pkcs11:" + +#define token_field_ofs(field) ((unsigned long)&(((struct pkcs11h_token_id_s *)0)->field)) +#define token_field_size(field) sizeof((((struct pkcs11h_token_id_s *)0)->field)) +#define token_field(name, field) { name "=", sizeof(name), \ + token_field_ofs(field), token_field_size(field) } + +static struct { + const char const *name; + size_t namelen; + unsigned long field_ofs; + size_t field_size; +} __token_fields[] = { + token_field ("model", model), + token_field ("token", label), + token_field ("manufacturer", manufacturerID ), + token_field ("serial", serialNumber ), + { NULL }, +}; + CK_RV pkcs11h_token_serializeTokenId ( OUT char * const sz, @@ -149,9 +169,147 @@ pkcs11h_token_serializeTokenId ( return rv; } +static CK_RV -pkcs11h_token_deserializeTokenId ( - OUT pkcs11h_token_id_t *p_token_id, +__parse_token_uri_attr ( + const char *uri, + size_t urilen, + char *tokstr, + size_t toklen, + size_t *parsed_len +) { + size_t orig_toklen = toklen; + CK_RV rv = CKR_OK; + + while (urilen && toklen > 1) { + if (*uri == '%') { + size_t size = 1; + + if (urilen < 3) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto done; + } + + rv = _pkcs11h_util_hexToBinary ((unsigned char *)tokstr, + uri + 1, &size); + if (rv != CKR_OK) { + goto done; + } + + uri += 2; + urilen -= 2; + } else { + *tokstr = *uri; + } + tokstr++; + uri++; + toklen--; + urilen--; + tokstr[0] = 0; + } + + if (urilen) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + } else if (parsed_len) { + *parsed_len = orig_toklen - toklen; + } + + done: + return rv; +} + +static +CK_RV +__parse_pkcs11_uri ( + OUT pkcs11h_token_id_t token_id, + OUT pkcs11h_certificate_id_t certificate_id, + IN const char * const sz +) { + const char *end, *p; + CK_RV rv = CKR_OK; + + _PKCS11H_ASSERT (token_id!=NULL); + _PKCS11H_ASSERT (sz!=NULL); + + if (strncmp (sz, URI_SCHEME, strlen (URI_SCHEME))) + return CKR_ATTRIBUTE_VALUE_INVALID; + + end = sz + strlen (URI_SCHEME) - 1; + while (rv == CKR_OK && end[0] && end[1]) { + int i; + + p = end + 1; + end = strchr (p, ';'); + if (!end) + end = p + strlen(p); + + for (i = 0; __token_fields[i].name; i++) { + /* Parse the token=, label=, manufacturer= and serial= fields */ + if (!strncmp(p, __token_fields[i].name, __token_fields[i].namelen)) { + char *field = ((char *)token_id) + __token_fields[i].field_ofs; + + p += __token_fields[i].namelen; + rv = __parse_token_uri_attr (p, end - p, field, + __token_fields[i].field_size, + NULL); + if (rv != CKR_OK) { + goto cleanup; + } + + goto matched; + } + } + if (certificate_id && !strncmp(p, "id=", 3)) { + p += 3; + + rv = _pkcs11h_mem_malloc ((void *)&certificate_id->attrCKA_ID, + end - p + 1); + if (rv != CKR_OK) { + goto cleanup; + } + + rv = __parse_token_uri_attr (p, end - p, + (char *)certificate_id->attrCKA_ID, + end - p + 1, + &certificate_id->attrCKA_ID_size); + if (rv != CKR_OK) { + goto cleanup; + } + + goto matched; + } + + /* We don't parse object= because the match code doesn't support + matching by label. */ + + /* Failed to parse PKCS#11 URI element. */ + return CKR_ATTRIBUTE_VALUE_INVALID; + + matched: + ; + } +cleanup: + /* The matching code doesn't support support partial matches; it needs + * *all* of manufacturer, model, serial and label attributes to be + * defined. So reject partial URIs early instead of letting it do the + * wrong thing. We can maybe improve this later. */ + if (!token_id->model[0] || !token_id->label[0] || + !token_id->manufacturerID[0] || !token_id->serialNumber[0]) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + /* For a certificate ID we need CKA_ID */ + if (certificate_id && !certificate_id->attrCKA_ID_size) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + return rv; +} + +static +CK_RV +__pkcs11h_token_legacy_deserializeTokenId ( + OUT pkcs11h_token_id_t token_id, IN const char * const sz ) { #define __PKCS11H_TARGETS_NUMBER 4 @@ -160,24 +318,11 @@ pkcs11h_token_deserializeTokenId ( size_t s; } targets[__PKCS11H_TARGETS_NUMBER]; - pkcs11h_token_id_t token_id = NULL; char *p1 = NULL; char *_sz = NULL; int e; CK_RV rv = CKR_FUNCTION_FAILED; - _PKCS11H_ASSERT (p_token_id!=NULL); - _PKCS11H_ASSERT (sz!=NULL); - - _PKCS11H_DEBUG ( - PKCS11H_LOG_DEBUG2, - "PKCS#11: pkcs11h_token_deserializeTokenId entry p_token_id=%p, sz='%s'", - (void *)p_token_id, - sz - ); - - *p_token_id = NULL; - if ( (rv = _pkcs11h_mem_strdup ( (void *)&_sz, @@ -189,10 +334,6 @@ pkcs11h_token_deserializeTokenId ( p1 = _sz; - if ((rv = _pkcs11h_token_newTokenId (&token_id)) != CKR_OK) { - goto cleanup; - } - targets[0].p = token_id->manufacturerID; targets[0].s = sizeof (token_id->manufacturerID); targets[1].p = token_id->model; @@ -251,6 +392,51 @@ pkcs11h_token_deserializeTokenId ( p1 = p2+1; } + rv = CKR_OK; + +cleanup: + + if (_sz != NULL) { + _pkcs11h_mem_free ((void *)&_sz); + } + + return rv; +#undef __PKCS11H_TARGETS_NUMBER +} + +CK_RV +pkcs11h_token_deserializeTokenId ( + OUT pkcs11h_token_id_t *p_token_id, + IN const char * const sz +) { + pkcs11h_token_id_t token_id = NULL; + CK_RV rv = CKR_FUNCTION_FAILED; + + _PKCS11H_ASSERT (p_token_id!=NULL); + _PKCS11H_ASSERT (sz!=NULL); + + _PKCS11H_DEBUG ( + PKCS11H_LOG_DEBUG2, + "PKCS#11: pkcs11h_token_deserializeTokenId entry p_token_id=%p, sz='%s'", + (void *)p_token_id, + sz + ); + + *p_token_id = NULL; + + if ((rv = _pkcs11h_token_newTokenId (&token_id)) != CKR_OK) { + goto cleanup; + } + + if (!strncmp (sz, URI_SCHEME, strlen (URI_SCHEME))) { + rv = __parse_pkcs11_uri(token_id, NULL, sz); + } else { + rv = __pkcs11h_token_legacy_deserializeTokenId(token_id, sz); + } + if (rv != CKR_OK) { + goto cleanup; + } + strncpy ( token_id->display, token_id->label, @@ -263,11 +449,6 @@ pkcs11h_token_deserializeTokenId ( rv = CKR_OK; cleanup: - - if (_sz != NULL) { - _pkcs11h_mem_free ((void *)&_sz); - } - if (token_id != NULL) { pkcs11h_token_freeTokenId (token_id); } @@ -280,7 +461,6 @@ pkcs11h_token_deserializeTokenId ( ); return rv; -#undef __PKCS11H_TARGETS_NUMBER } #endif /* ENABLE_PKCS11H_TOKEN || ENABLE_PKCS11H_CERTIFICATE */ @@ -359,29 +539,17 @@ pkcs11h_certificate_serializeCertificateId ( return rv; } +static CK_RV -pkcs11h_certificate_deserializeCertificateId ( - OUT pkcs11h_certificate_id_t * const p_certificate_id, +__pkcs11h_certificate_legacy_deserializeCertificateId ( + OUT pkcs11h_certificate_id_t certificate_id, IN const char * const sz ) { - pkcs11h_certificate_id_t certificate_id = NULL; CK_RV rv = CKR_FUNCTION_FAILED; char *p = NULL; char *_sz = NULL; size_t id_hex_len; - _PKCS11H_ASSERT (p_certificate_id!=NULL); - _PKCS11H_ASSERT (sz!=NULL); - - *p_certificate_id = NULL; - - _PKCS11H_DEBUG ( - PKCS11H_LOG_DEBUG2, - "PKCS#11: pkcs11h_certificate_deserializeCertificateId entry p_certificate_id=%p, sz='%s'", - (void *)p_certificate_id, - sz - ); - if ( (rv = _pkcs11h_mem_strdup ( (void *)&_sz, @@ -393,10 +561,6 @@ pkcs11h_certificate_deserializeCertificateId ( p = _sz; - if ((rv = _pkcs11h_certificate_newCertificateId (&certificate_id)) != CKR_OK) { - goto cleanup; - } - if ((p = strrchr (_sz, '/')) == NULL) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto cleanup; @@ -435,21 +599,64 @@ pkcs11h_certificate_deserializeCertificateId ( goto cleanup; } + rv = CKR_OK; + +cleanup: + + if (_sz != NULL) { + _pkcs11h_mem_free ((void *)&_sz); + } + + return rv; + +} + +CK_RV +pkcs11h_certificate_deserializeCertificateId ( + OUT pkcs11h_certificate_id_t * const p_certificate_id, + IN const char * const sz +) { + pkcs11h_certificate_id_t certificate_id = NULL; + CK_RV rv = CKR_FUNCTION_FAILED; + + _PKCS11H_ASSERT (p_certificate_id!=NULL); + _PKCS11H_ASSERT (sz!=NULL); + + *p_certificate_id = NULL; + + _PKCS11H_DEBUG ( + PKCS11H_LOG_DEBUG2, + "PKCS#11: pkcs11h_certificate_deserializeCertificateId entry p_certificate_id=%p, sz='%s'", + (void *)p_certificate_id, + sz + ); + + if ((rv = _pkcs11h_certificate_newCertificateId (&certificate_id)) != CKR_OK) { + goto cleanup; + } + if ((rv = _pkcs11h_token_newTokenId (&certificate_id->token_id)) != CKR_OK) { + goto cleanup; + } + + if (!strncmp(sz, URI_SCHEME, strlen (URI_SCHEME))) { + rv = __parse_pkcs11_uri (certificate_id->token_id, certificate_id, sz); + } else { + rv = __pkcs11h_certificate_legacy_deserializeCertificateId (certificate_id, sz); + } + if (rv != CKR_OK) { + goto cleanup; + } + *p_certificate_id = certificate_id; certificate_id = NULL; rv = CKR_OK; cleanup: - if (certificate_id != NULL) { pkcs11h_certificate_freeCertificateId (certificate_id); certificate_id = NULL; } - if (_sz != NULL) { - _pkcs11h_mem_free ((void *)&_sz); - } - _PKCS11H_DEBUG ( PKCS11H_LOG_DEBUG2, "PKCS#11: pkcs11h_certificate_deserializeCertificateId return rv=%lu-'%s'", From 90590b02085edc3830bdfe0942a46c4e7bf3f1ab Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 30 Apr 2015 14:58:24 +0100 Subject: [PATCH 3/3] Serialize to RFC7512-compliant PKCS#11 URIs Signed-off-by: David Woodhouse --- lib/pkcs11h-serialization.c | 186 ++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/lib/pkcs11h-serialization.c b/lib/pkcs11h-serialization.c index 390ac0e..0ea1861 100644 --- a/lib/pkcs11h-serialization.c +++ b/lib/pkcs11h-serialization.c @@ -80,29 +80,107 @@ static struct { { NULL }, }; +#define P11_URL_VERBATIM "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789_-." + +static +int +__token_attr_escape(char *uri, char *attr, size_t attrlen) +{ + int len = 0, i; + + for (i = 0; i < attrlen; i++) { + if ((attr[i] != '\x0') && strchr(P11_URL_VERBATIM, attr[i])) { + if (uri) { + *(uri++) = attr[i]; + } + len++; + } else { + if (uri) { + sprintf(uri, "%%%02x", (unsigned char)attr[i]); + uri += 3; + } + len += 3; + } + } + return len; +} + +static +CK_RV +__generate_pkcs11_uri ( + OUT char * const sz, + IN OUT size_t *max, + IN const pkcs11h_certificate_id_t certificate_id, + IN const pkcs11h_token_id_t token_id +) { + size_t _max; + char *p = sz; + int i; + + _PKCS11H_ASSERT (max!=NULL); + _PKCS11H_ASSERT (token_id!=NULL); + + _max = strlen(URI_SCHEME); + for (i = 0; __token_fields[i].name; i++) { + char *field = ((char *)token_id) + __token_fields[i].field_ofs; + + _max += __token_fields[i].namelen; + _max += __token_attr_escape (NULL, field, strlen(field)); + _max++; /* For a semicolon or trailing NUL */ + } + if (certificate_id) { + _max += strlen (";id="); + _max += __token_attr_escape (NULL, + (char *)certificate_id->attrCKA_ID, + certificate_id->attrCKA_ID_size); + } + + if (!sz) { + *max = _max; + return CKR_OK; + } + + if (sz && *max < _max) + return CKR_ATTRIBUTE_VALUE_INVALID; + + p += sprintf(p, URI_SCHEME); + for (i = 0; __token_fields[i].name; i++) { + char *field = ((char *)token_id) + __token_fields[i].field_ofs; + + p += sprintf (p, "%s", __token_fields[i].name); + p += __token_attr_escape (p, field, strlen(field)); + *(p++) = ';'; + } + if (certificate_id) { + p += sprintf (p, "id="); + p += __token_attr_escape (p, + (char *)certificate_id->attrCKA_ID, + certificate_id->attrCKA_ID_size); + } else { + /* Remove the unneeded trailing semicolon */ + p--; + } + *(p++) = 0; + + *max = _max; + + return CKR_OK; +} + CK_RV pkcs11h_token_serializeTokenId ( OUT char * const sz, IN OUT size_t *max, IN const pkcs11h_token_id_t token_id ) { - const char *sources[5]; CK_RV rv = CKR_FUNCTION_FAILED; - size_t n; - int e; /*_PKCS11H_ASSERT (sz!=NULL); Not required*/ _PKCS11H_ASSERT (max!=NULL); _PKCS11H_ASSERT (token_id!=NULL); - { /* Must be after assert */ - sources[0] = token_id->manufacturerID; - sources[1] = token_id->model; - sources[2] = token_id->serialNumber; - sources[3] = token_id->label; - sources[4] = NULL; - } - _PKCS11H_DEBUG ( PKCS11H_LOG_DEBUG2, "PKCS#11: pkcs11h_token_serializeTokenId entry sz=%p, *max="P_Z", token_id=%p", @@ -111,51 +189,7 @@ pkcs11h_token_serializeTokenId ( (void *)token_id ); - n = 0; - for (e=0;sources[e] != NULL;e++) { - size_t t; - if ( - (rv = _pkcs11h_util_escapeString ( - NULL, - sources[e], - &t, - __PKCS11H_SERIALIZE_INVALID_CHARS - )) != CKR_OK - ) { - goto cleanup; - } - n+=t; - } - - if (sz != NULL) { - if (*max < n) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto cleanup; - } - - n = 0; - for (e=0;sources[e] != NULL;e++) { - size_t t = *max-n; - if ( - (rv = _pkcs11h_util_escapeString ( - sz+n, - sources[e], - &t, - __PKCS11H_SERIALIZE_INVALID_CHARS - )) != CKR_OK - ) { - goto cleanup; - } - n+=t; - sz[n-1] = '/'; - } - sz[n-1] = '\x0'; - } - - *max = n; - rv = CKR_OK; - -cleanup: + rv = __generate_pkcs11_uri(sz, max, NULL, token_id); _PKCS11H_DEBUG ( PKCS11H_LOG_DEBUG2, @@ -474,9 +508,6 @@ pkcs11h_certificate_serializeCertificateId ( IN const pkcs11h_certificate_id_t certificate_id ) { CK_RV rv = CKR_FUNCTION_FAILED; - size_t saved_max = 0; - size_t n = 0; - size_t _max = 0; /*_PKCS11H_ASSERT (sz!=NULL); Not required */ _PKCS11H_ASSERT (max!=NULL); @@ -490,42 +521,7 @@ pkcs11h_certificate_serializeCertificateId ( (void *)certificate_id ); - if (sz != NULL) { - saved_max = n = *max; - } - *max = 0; - - if ( - (rv = pkcs11h_token_serializeTokenId ( - sz, - &n, - certificate_id->token_id - )) != CKR_OK - ) { - goto cleanup; - } - - _max = n + certificate_id->attrCKA_ID_size*2 + 1; - - if (sz != NULL) { - if (saved_max < _max) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto cleanup; - } - - sz[n-1] = '/'; - rv = _pkcs11h_util_binaryToHex ( - sz+n, - saved_max-n, - certificate_id->attrCKA_ID, - certificate_id->attrCKA_ID_size - ); - } - - *max = _max; - rv = CKR_OK; - -cleanup: + rv = __generate_pkcs11_uri(sz, max, certificate_id, certificate_id->token_id); _PKCS11H_DEBUG ( PKCS11H_LOG_DEBUG2,