upstream PR: https://github.com/OpenSC/pkcs11-helper/pull/4 commit 90590b02085edc3830bdfe0942a46c4e7bf3f1ab (HEAD -> master) Author: David Woodhouse Date: Thu Apr 30 14:58:24 2015 +0100 Serialize to RFC7512-compliant PKCS#11 URIs Signed-off-by: David Woodhouse commit 4d5280da8df591aab701dff4493d13a835a9b29c Author: David Woodhouse Date: Wed Dec 10 14:00:21 2014 +0000 Accept RFC7512-compliant PKCS#11 URIs as serialized token/certificate IDs The old format is still accepted for compatibility. Signed-off-by: David Woodhouse commit 14e09211c3d50eb06825090c9765e4382cf52f19 Author: David Woodhouse Date: Sun Dec 14 19:42:18 2014 +0000 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 diff --git a/lib/pkcs11h-serialization.c b/lib/pkcs11h-serialization.c index ad275f8..1d077e4 100644 --- a/lib/pkcs11h-serialization.c +++ b/lib/pkcs11h-serialization.c @@ -61,29 +61,127 @@ #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 }, +}; + +#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", @@ -92,67 +190,161 @@ 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; + rv = __generate_pkcs11_uri(sz, max, NULL, token_id); + + _PKCS11H_DEBUG ( + PKCS11H_LOG_DEBUG2, + "PKCS#11: pkcs11h_token_serializeTokenId return rv=%lu-'%s', *max="P_Z", sz='%s'", + rv, + pkcs11h_getMessage (rv), + *max, + sz + ); + + return rv; +} + +static +CK_RV +__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; } - n+=t; + tokstr++; + uri++; + toklen--; + urilen--; + tokstr[0] = 0; } - if (sz != NULL) { - if (*max < n) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto cleanup; + 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; + } - 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 - ) { + 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; } - n+=t; - sz[n-1] = '/'; + + goto matched; } - sz[n-1] = '\x0'; - } - *max = n; - rv = CKR_OK; + /* 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; + } - _PKCS11H_DEBUG ( - PKCS11H_LOG_DEBUG2, - "PKCS#11: pkcs11h_token_serializeTokenId return rv=%lu-'%s', *max="P_Z", sz='%s'", - rv, - pkcs11h_getMessage (rv), - *max, - sz - ); + /* 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_deserializeTokenId ( - OUT pkcs11h_token_id_t *p_token_id, +__pkcs11h_token_legacy_deserializeTokenId ( + OUT pkcs11h_token_id_t token_id, IN const char * const sz ) { #define __PKCS11H_TARGETS_NUMBER 4 @@ -161,24 +353,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, @@ -190,10 +369,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; @@ -252,6 +427,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, @@ -264,11 +484,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); } @@ -281,7 +496,6 @@ cleanup: ); return rv; -#undef __PKCS11H_TARGETS_NUMBER } #endif /* ENABLE_PKCS11H_TOKEN || ENABLE_PKCS11H_CERTIFICATE */ @@ -295,9 +509,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); @@ -311,42 +522,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, @@ -360,27 +536,16 @@ cleanup: 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; - - _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 - ); + size_t id_hex_len; if ( (rv = _pkcs11h_mem_strdup ( @@ -393,10 +558,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; @@ -414,7 +575,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 ( @@ -430,21 +596,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'", diff --git a/lib/pkcs11h-util.c b/lib/pkcs11h-util.c index 0743fd1..f90e443 100644 --- a/lib/pkcs11h-util.c +++ b/lib/pkcs11h-util.c @@ -110,12 +110,7 @@ _pkcs11h_util_hexToBinary ( p++; } - if (*p != '\x0') { - return CKR_ATTRIBUTE_VALUE_INVALID; - } - else { - return CKR_OK; - } + return CKR_OK; } CK_RV