summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Pechanec <Jan.Pechanec@Sun.COM>2010-06-07 10:04:00 -0700
committerJan Pechanec <Jan.Pechanec@Sun.COM>2010-06-07 10:04:00 -0700
commitccd81fdda071e031209c777983199d191c35b0a2 (patch)
tree5bc1ef966a963ecff237dcc989b379e66e00070d
parentf7327bbd956f5bf6b97f9b90e0c81e8344d8835f (diff)
downloadillumos-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.com6
-rw-r--r--usr/src/lib/libcryptoutil/README29
-rw-r--r--usr/src/lib/libcryptoutil/common/cryptoutil.h70
-rw-r--r--usr/src/lib/libcryptoutil/common/mapfile-vers6
-rw-r--r--usr/src/lib/libcryptoutil/common/pkcs11_uri.c297
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);
+}