diff options
author | Jan Pechanec <Jan.Pechanec@Sun.COM> | 2010-06-07 10:04:00 -0700 |
---|---|---|
committer | Jan Pechanec <Jan.Pechanec@Sun.COM> | 2010-06-07 10:04:00 -0700 |
commit | ccd81fdda071e031209c777983199d191c35b0a2 (patch) | |
tree | 5bc1ef966a963ecff237dcc989b379e66e00070d | |
parent | f7327bbd956f5bf6b97f9b90e0c81e8344d8835f (diff) | |
download | illumos-gate-ccd81fdda071e031209c777983199d191c35b0a2.tar.gz |
PSARC/2010/188 PKCS#11 URI parser for libcryptoutil
6924687 teach libcryptoutil to parse a PKCS#11 URI
-rw-r--r-- | usr/src/lib/libcryptoutil/Makefile.com | 6 | ||||
-rw-r--r-- | usr/src/lib/libcryptoutil/README | 29 | ||||
-rw-r--r-- | usr/src/lib/libcryptoutil/common/cryptoutil.h | 70 | ||||
-rw-r--r-- | usr/src/lib/libcryptoutil/common/mapfile-vers | 6 | ||||
-rw-r--r-- | usr/src/lib/libcryptoutil/common/pkcs11_uri.c | 297 |
5 files changed, 391 insertions, 17 deletions
diff --git a/usr/src/lib/libcryptoutil/Makefile.com b/usr/src/lib/libcryptoutil/Makefile.com index 98de0e1db3..d4183effb9 100644 --- a/usr/src/lib/libcryptoutil/Makefile.com +++ b/usr/src/lib/libcryptoutil/Makefile.com @@ -18,8 +18,7 @@ # # CDDL HEADER END # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # LIBRARY= libcryptoutil.a @@ -36,7 +35,8 @@ OBJECTS= \ passutils.o \ random.o \ keyfile.o \ - util.o + util.o \ + pkcs11_uri.o include $(SRC)/lib/Makefile.lib include $(SRC)/lib/Makefile.rootfs diff --git a/usr/src/lib/libcryptoutil/README b/usr/src/lib/libcryptoutil/README index 271fc6407e..371374e42d 100644 --- a/usr/src/lib/libcryptoutil/README +++ b/usr/src/lib/libcryptoutil/README @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,14 +18,13 @@ # # CDDL HEADER END # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # -# ident "%Z%%M% %I% %E% SMI" This is an internal library for use only by: usr/src/cmd/cmd-crypto usr/src/lib/pkcs11 + usr/src/lib/libkmf The library and the header file are installed into the proto area but are not included in any pacakges. @@ -191,13 +189,30 @@ Consumers: pkcs11_strerror() -This function returnes a string representation of any given PKCS11 return +This function returns a string representation of any given PKCS11 return code. Consumer: encrypt(1) and decrypt(1) uses this function for reporting errors. +2.5 PKCS#11 URI parsing code + + pkcs11_parse_uri() + pkcs11_free_uri() + +This function parses a PKCS#11 URI and fills up a pkcs11_uri_t structure. It +also reads the PIN if the PKCS#11 URI specifies a passphrase dialog. The +pkcs11_uri_t is described in cryptoutil.h, explanation of the return codes for +the pkcs11_parse_uri() function is in the function's comment in pk11_uri.c. The +pkcs11_parse_uri() function allocates the URI's fields and the caller is +responsible for calling pkcs11_free_uri() after it's done with the URI +structure. + +Consumer: + + SunSSH will use the functions for parsing PKCS#11 URIs. + 3. Non-Contents Code for cryptographic algorithms does not belong in here. That diff --git a/usr/src/lib/libcryptoutil/common/cryptoutil.h b/usr/src/lib/libcryptoutil/common/cryptoutil.h index 21f0a07665..cca77d3f87 100644 --- a/usr/src/lib/libcryptoutil/common/cryptoutil.h +++ b/usr/src/lib/libcryptoutil/common/cryptoutil.h @@ -17,10 +17,8 @@ * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _CRYPTOUTIL_H @@ -114,6 +112,67 @@ typedef struct uentrylist { struct uentrylist *next; } uentrylist_t; +/* Return codes for pkcs11_parse_uri() */ +#define PK11_URI_OK 0 +#define PK11_URI_INVALID 1 +#define PK11_MALLOC_ERROR 2 +#define PK11_URI_VALUE_OVERFLOW 3 +#define PK11_NOT_PKCS11_URI 4 + +/* + * There is no limit for the attribute length in the spec. 256 bytes should be + * enough for the object name. + */ +#define PK11_MAX_OBJECT_LEN 256 +/* + * CKA_ID is of type "byte array" which can be of arbitrary length. 256 bytes + * should be sufficient though. + */ +#define PK11_MAX_ID_LEN 256 + +/* Structure for the PKCS#11 URI. */ +typedef struct pkcs11_uri_t { + /* CKA_LABEL attribute to the C_FindObjectsInit function. */ + CK_UTF8CHAR_PTR object; + /* + * CKA_CLASS attribute to the C_FindObjectsInit function. The + * "objecttype" URI attribute can have a value one of "private", + * "public", "cert", "secretkey", and "data". The "objecttype" field can + * have a value of CKO_PUBLIC_KEY, CKO_PRIVATE_KEY, CKO_CERTIFICATE, + * CKO_SECRET_KEY, and CKO_DATA. This attribute cannot be empty in the + * URI. + */ + CK_ULONG objecttype; + /* CKO_DATA is 0 so we need this flag. Not part of the URI itself. */ + boolean_t objecttype_present; + /* + * Token, manufufacturer, serial and model are of fixed size length in + * the specification. We allocate memory on the fly to distinguish + * between an attribute not present and an empty value. We check for + * overflows. We always terminate the string with '\0' even when that is + * not used in the PKCS#11's CK_TOKEN_INFO structure (fields are padded + * with spaces). + */ + /* Token label from CK_TOKEN_INFO. */ + CK_UTF8CHAR_PTR token; + /* ManufacturerID from CK_TOKEN_INFO. */ + CK_UTF8CHAR_PTR manuf; + /* SerialNumber from CK_TOKEN_INFO. */ + CK_CHAR_PTR serial; + /* Model from CK_TOKEN_INFO. */ + CK_UTF8CHAR_PTR model; + /* This is a byte array, we need a length parameter as well. */ + CK_BYTE_PTR id; + int id_len; + /* + * Location of the file with a token PIN. Application can overload this, + * eg. "/bin/askpass|" may mean to read the PIN from a command. However, + * the pkcs11_parse_uri() function does not interpret this field in any + * way. + */ + char *pinfile; +} pkcs11_uri_t; + extern void cryptodebug(const char *fmt, ...); extern void cryptoerror(int priority, const char *fmt, ...); extern void cryptodebug_init(const char *prefix); @@ -166,6 +225,9 @@ extern int update_conf(char *conf_file, char *entry); extern CK_RV get_fips_mode(int *); +extern int pkcs11_parse_uri(const char *str, pkcs11_uri_t *uri); +extern void pkcs11_free_uri(pkcs11_uri_t *uri); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libcryptoutil/common/mapfile-vers b/usr/src/lib/libcryptoutil/common/mapfile-vers index 7e2fe51536..502984d81e 100644 --- a/usr/src/lib/libcryptoutil/common/mapfile-vers +++ b/usr/src/lib/libcryptoutil/common/mapfile-vers @@ -18,9 +18,7 @@ # # CDDL HEADER END # -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # @@ -59,6 +57,7 @@ SUNWprivate { pkcs11_close_urandom; pkcs11_close_urandom_seed; pkcs11_default_token; + pkcs11_free_uri; pkcs11_get_nzero_urandom; pkcs11_get_pass; pkcs11_get_random; @@ -66,6 +65,7 @@ SUNWprivate { pkcs11_mech2keytype; pkcs11_mech2keygen; pkcs11_mech2str; + pkcs11_parse_uri; pkcs11_read_data; pkcs11_seed_random; pkcs11_seed_urandom; diff --git a/usr/src/lib/libcryptoutil/common/pkcs11_uri.c b/usr/src/lib/libcryptoutil/common/pkcs11_uri.c new file mode 100644 index 0000000000..f7053de26a --- /dev/null +++ b/usr/src/lib/libcryptoutil/common/pkcs11_uri.c @@ -0,0 +1,297 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <strings.h> +#include <libgen.h> +#include <pthread.h> + +#include <security/cryptoki.h> +#include <security/pkcs11.h> + +#include <cryptoutil.h> + +/* PKCS#11 URI prefix and attributes. */ +#define PK11_URI_PREFIX "pkcs11:" +#define PK11_TOKEN "token" +#define PK11_MANUF "manufacturer" +#define PK11_SERIAL "serial" +#define PK11_MODEL "model" +#define PK11_OBJECT "object" +#define PK11_OBJECTTYPE "objecttype" +#define PK11_ID "id" +#define PK11_PINFILE "pinfile" + +/* + * Gets a hexadecimal string of the xx:xx:xx-like format and fills the output + * buffer with bytes represeting each of the hexadecimal numbers. Returns 0 on + * error (missing ':', not a hexadecimal character (eg. 'z'), output buffer + * overflow, etc.), or the number of hexadecimal numbers processed. + * + * Returns: + * 0 + * on failure + * >0 + * number of bytes returned via the output parameter + */ +static int +read_id(char *str, unsigned char *output, int out_len) +{ + int i, len, n; + unsigned int x1, x2; + + len = strlen(str); + (void) memset(output, 0, out_len); + /* Counter of the processed bytes. */ + i = 0; + /* Counter for the used output bytes. */ + n = 0; + + while (i < len) { + /* We require at least one hexadecimal character. */ + if (sscanf(str + i, "%1x", &x1) != 1) + return (0); + ++i; + /* And we accept the 2nd one if it is there. */ + if (sscanf(str + i, "%1x", &x2) == 1) { + x1 = x1 * 16 + x2; + ++i; + } + + /* Output buffer overflow? */ + if ((n + 1) > out_len) + return (0); + output[n] = (unsigned char)x1; + /* Still some bytes to process? */ + if (i < len) { + /* ':' is the only acceptable delimiter. */ + if (str[i] != ':') + return (0); + /* Skip ':' */ + ++i; + } + ++n; + } + + return (n); +} + +/* + * Process the PKCS#11 URI. The function expects an allocated URI structure. The + * caller is later expected to call pkcs11_free_uri() when the parsed URI is no + * longer needed. + * + * Returns: + * PK11_URI_OK + * success + * PK11_URI_INVALID + * invalid PKCS#11 URI (one that has the "pkcs11:" prefix but is + * otherwise incorrectly specified) + * PK11_MALLOC_ERROR + * malloc(3C) failed when allocating one of the internal buffers + * PK11_URI_VALUE_OVERFLOW + * some attributes in the URI are of the fixed length accroding to + * the spec. If any of those attributes overflows we report an + * error + * PK11_NOT_PKCS11_URI + * the URI supplied is not the PKCS#11 URI at all (does not have + * the "pkcs11:" prefix) + */ +int +pkcs11_parse_uri(const char *str, pkcs11_uri_t *uri) +{ + char *str2, *l1, *l2, *tok, *name; + + /* Initialize the structure. */ + (void) memset(uri, 0, sizeof (pkcs11_uri_t)); + /* Be really safe. */ + uri->objecttype_present = B_FALSE; + + /* Check that we have the correct PKCS#11 URI prefix. */ + if (strncmp(str, PK11_URI_PREFIX, strlen(PK11_URI_PREFIX)) != 0) + return (PK11_NOT_PKCS11_URI); + /* Dup the string and skip over the prefix then. */ + if ((str2 = strdup(str + strlen(PK11_URI_PREFIX))) == NULL) + return (PK11_MALLOC_ERROR); + + /* + * Using strtok_r() would silently skip over multiple semicolons. We + * must check such situation before moving on. We must also avoid ';' as + * the first and the last character of the URI. + */ + if (strstr(str2, ";;") != NULL || str2[0] == ';' || + (strlen(str2) > 0 && str2[strlen(str2) - 1] == ';')) + goto bad_uri; + + /* Now parse the URI. */ + tok = strtok_r(str2, ";", &l1); + for (; tok != NULL; tok = strtok_r(NULL, ";", &l1)) { + /* "tok" is not empty so there will be something in "name". */ + name = strtok_r(tok, "=", &l2); + /* Check whether there is '=' at all. */ + if (l2 == NULL) + goto bad_uri; + + /* + * Fill out the URI structure. We do not accept duplicate + * attributes. + */ + if (strcmp(name, PK11_TOKEN) == 0) { + /* Check for duplicity. */ + if (uri->token != NULL) + goto bad_uri; + if (strlen(l2) > 32) + goto value_overflow; + if ((uri->token = (unsigned char *)strdup(l2)) == NULL) + goto malloc_failed; + } else if (strcmp(name, PK11_MANUF) == 0) { + /* Check for duplicity. */ + if (uri->manuf != NULL) + goto bad_uri; + if (strlen(l2) > 32) + goto value_overflow; + if ((uri->manuf = (unsigned char *)strdup(l2)) == NULL) + goto malloc_failed; + } else if (strcmp(name, PK11_SERIAL) == 0) { + /* Check for duplicity. */ + if (uri->serial != NULL) + goto bad_uri; + if (strlen(l2) > 16) + goto value_overflow; + if ((uri->serial = (unsigned char *)strdup(l2)) == NULL) + goto malloc_failed; + } else if (strcmp(name, PK11_MODEL) == 0) { + /* Check for duplicity. */ + if (uri->model != NULL) + goto bad_uri; + if (strlen(l2) > 16) + goto value_overflow; + if ((uri->model = (unsigned char *)strdup(l2)) == NULL) + goto malloc_failed; + } else if (strcmp(name, PK11_ID) == 0) { + /* Check for duplicity. */ + if (uri->id_len != 0) + goto bad_uri; + /* + * We can have maximum of PK11_MAX_ID_LEN 2-byte + * numbers separated by ':'s, like + * 01:02:0A:FF:... + */ + if (strlen(l2) > PK11_MAX_ID_LEN * 2 + + PK11_MAX_ID_LEN - 1) { + goto value_overflow; + } + if ((uri->id = malloc(PK11_MAX_ID_LEN)) == NULL) + goto malloc_failed; + uri->id_len = read_id(l2, uri->id, + PK11_MAX_ID_LEN); + if (uri->id_len == 0) + goto bad_uri; + } else if (strcmp(name, PK11_OBJECT) == 0) { + /* Check for duplicity. */ + if (uri->object != NULL) + goto bad_uri; + if (strlen(l2) > PK11_MAX_OBJECT_LEN) + goto value_overflow; + if ((uri->object = (unsigned char *)strdup(l2)) == NULL) + goto malloc_failed; + } else if (strcmp(name, PK11_OBJECTTYPE) == 0) { + /* + * Check for duplicity. objecttype can not be empty, it + * would not make sense. + */ + if (uri->objecttype_present == CK_TRUE) + goto bad_uri; + if (strcmp(l2, "public") == 0) + uri->objecttype = CKO_PUBLIC_KEY; + else if (strcmp(l2, "private") == 0) + uri->objecttype = CKO_PRIVATE_KEY; + else if (strcmp(l2, "cert") == 0) + uri->objecttype = CKO_CERTIFICATE; + else if (strcmp(l2, "secretkey") == 0) + uri->objecttype = CKO_SECRET_KEY; + else if (strcmp(l2, "data") == 0) + uri->objecttype = CKO_DATA; + else + goto bad_uri; + uri->objecttype_present = CK_TRUE; + } else if (strcmp(name, PK11_PINFILE) == 0) + /* Check for duplicity. */ + if (uri->pinfile == NULL) { + if (strlen(l2) > MAXPATHLEN) + goto value_overflow; + if ((uri->pinfile = strdup(l2)) == NULL) + goto malloc_failed; + /* Empty pinfile makes no sense. */ + if (uri->pinfile[0] == '\0') + goto bad_uri; + } else + goto bad_uri; + else + /* Unknown attribute name. */ + goto bad_uri; + } + + free(str2); + return (PK11_URI_OK); +malloc_failed: + free(str2); + pkcs11_free_uri(uri); + return (PK11_MALLOC_ERROR); +bad_uri: + free(str2); + pkcs11_free_uri(uri); + return (PK11_URI_INVALID); +value_overflow: + free(str2); + pkcs11_free_uri(uri); + return (PK11_URI_VALUE_OVERFLOW); +} + +/* + * Free the PKCS#11 URI structure attributes but do not free the structure + * itself. + */ +void +pkcs11_free_uri(pkcs11_uri_t *uri) +{ + if (uri->object != NULL) + free(uri->object); + if (uri->token != NULL) + free(uri->token); + if (uri->manuf != NULL) + free(uri->manuf); + if (uri->serial != NULL) + free(uri->serial); + if (uri->model != NULL) + free(uri->model); + if (uri->id != NULL) + free(uri->id); + if (uri->pinfile != NULL) + free(uri->pinfile); +} |