/* * 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 #include #include #include #include #include #include #include #include #include #include #include /* 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); }