Esempio n. 1
0
static int
_dsa_params_generate(struct ck_function_list *module, ck_session_handle_t session,
		     unsigned long bits, struct dsa_params *params,
		     struct ck_attribute *a, int *a_val)
{
	struct ck_mechanism mech = { CKM_DSA_PARAMETER_GEN };
	struct ck_attribute attr = { CKA_PRIME_BITS, &bits, sizeof(bits) };
	ck_object_handle_t key;
	ck_rv_t rv;

	/* Generate DSA parameters from prime length. */

	rv = pkcs11_generate_key(module, session, &mech, &attr, 1, &key);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		return pkcs11_rv_to_err(rv);
	}

	/* Retrieve generated parameters to be used with the new key pair. */

	a[*a_val + 0].type = CKA_PRIME;
	a[*a_val + 0].value = params->prime;
	a[*a_val + 0].value_len = sizeof(params->prime);

	a[*a_val + 1].type = CKA_SUBPRIME;
	a[*a_val + 1].value = params->subprime;
	a[*a_val + 1].value_len = sizeof(params->subprime);

	a[*a_val + 2].type = CKA_BASE;
	a[*a_val + 2].value = params->generator;
	a[*a_val + 2].value_len = sizeof(params->generator);

	rv = pkcs11_get_attribute_value(module, session, key, &a[*a_val], 3);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		return pkcs11_rv_to_err(rv);
	}

	*a_val += 3;

	return 0;
}
/*
 * Class:     org_opensc_pkcs11_wrap_PKCS11Session
 * Method:    logoutNative
 * Signature: (JJJ)V
 */
JNIEXPORT void JNICALL JNIX_FUNC_NAME(Java_org_opensc_pkcs11_wrap_PKCS11Session_logoutNative)
  (JNIEnv *env, jobject jsession, jlong mh, jlong shandle, jlong hsession)
{
  int rv;
  pkcs11_slot_t *slot;
  pkcs11_module_t *mod =  pkcs11_module_from_jhandle(env,mh);
  if (!mod) return;

  slot = pkcs11_slot_from_jhandle(env,shandle);
  if (!slot) return;

  rv = mod->method->C_Logout(hsession);
  if (rv != CKR_OK)
    {
      fprintf(stderr,"PKCS11Session.logoutNative: C_Logout for PKCS11 slot %d(" PKCS11_MOD_NAME_FMT ") failed (%s).",
              (int)slot->id,mod->name,pkcs11_strerror(rv));
    }
}
Esempio n. 3
0
/**
 * gnutls_pkcs11_privkey_generate3:
 * @url: a token URL
 * @pk: the public key algorithm
 * @bits: the security bits
 * @label: a label
 * @cid: The CKA_ID to use for the new object
 * @fmt: the format of output params. PEM or DER
 * @pubkey: will hold the public key (may be %NULL)
 * @key_usage: One of GNUTLS_KEY_*
 * @flags: zero or an OR'ed sequence of %GNUTLS_PKCS11_OBJ_FLAGs
 *
 * This function will generate a private key in the specified
 * by the @url token. The private key will be generate within
 * the token and will not be exportable. This function will
 * store the DER-encoded public key in the SubjectPublicKeyInfo format 
 * in @pubkey. The @pubkey should be deinitialized using gnutls_free().
 *
 * Note that when generating an elliptic curve key, the curve
 * can be substituted in the place of the bits parameter using the
 * GNUTLS_CURVE_TO_BITS() macro.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.4.0
 **/
int
gnutls_pkcs11_privkey_generate3(const char *url, gnutls_pk_algorithm_t pk,
				unsigned int bits, const char *label,
				const gnutls_datum_t *cid,
				gnutls_x509_crt_fmt_t fmt,
				gnutls_datum_t * pubkey,
				unsigned int key_usage,
				unsigned int flags)
{
	int ret;
	const ck_bool_t tval = 1;
	const ck_bool_t fval = 0;
	struct pkcs11_session_info sinfo;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	struct ck_attribute a[22], p[22];
	ck_object_handle_t pub_ctx, priv_ctx;
	unsigned long _bits = bits;
	int a_val, p_val;
	struct ck_mechanism mech;
	gnutls_pubkey_t pkey = NULL;
	gnutls_pkcs11_obj_t obj = NULL;
	gnutls_datum_t der = {NULL, 0};
	ck_key_type_t key_type;
	uint8_t id[20];
	struct dsa_params dsa_params;

	PKCS11_CHECK_INIT;
	FIX_KEY_USAGE(pk, key_usage);

	memset(&sinfo, 0, sizeof(sinfo));

	ret = pkcs11_url_to_info(url, &info, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	/* a holds the public key template
	 * and p the private key */
	a_val = p_val = 0;
	mech.parameter = NULL;
	mech.parameter_len = 0;
	mech.mechanism = pk_to_genmech(pk, &key_type);

	if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_NO_STORE_PUBKEY)) {
		a[a_val].type = CKA_TOKEN;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_PRIVATE;
		a[a_val].value = (void *) &fval;
		a[a_val].value_len = sizeof(fval);
		a_val++;
	}

	a[a_val].type = CKA_ID;
	if (cid == NULL || cid->size == 0) {
		ret = gnutls_rnd(GNUTLS_RND_NONCE, id, sizeof(id));
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		a[a_val].value = (void *) id;
		a[a_val].value_len = sizeof(id);
	} else {
		a[a_val].value = (void *) cid->data;
		a[a_val].value_len = cid->size;
	}

	p[p_val].type = CKA_ID;
	p[p_val].value = a[a_val].value;
	p[p_val].value_len = a[a_val].value_len;
	a_val++;
	p_val++;

	switch (pk) {
	case GNUTLS_PK_RSA:
		p[p_val].type = CKA_DECRYPT;
		if (key_usage & (GNUTLS_KEY_DECIPHER_ONLY|GNUTLS_KEY_ENCIPHER_ONLY)) {
			p[p_val].value = (void *) &tval;
			p[p_val].value_len = sizeof(tval);
		} else {
			p[p_val].value = (void *) &fval;
			p[p_val].value_len = sizeof(fval);
		}
		p_val++;

		p[p_val].type = CKA_SIGN;
		if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) {
			p[p_val].value = (void *) &tval;
			p[p_val].value_len = sizeof(tval);
		} else {
			p[p_val].value = (void *) &fval;
			p[p_val].value_len = sizeof(fval);
		}
		p_val++;

		a[a_val].type = CKA_ENCRYPT;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_MODULUS_BITS;
		a[a_val].value = &_bits;
		a[a_val].value_len = sizeof(_bits);
		a_val++;

		a[a_val].type = CKA_PUBLIC_EXPONENT;
		a[a_val].value = (char*)def_rsa_pub_exp;
		a[a_val].value_len = sizeof(def_rsa_pub_exp);
		a_val++;

		break;
	case GNUTLS_PK_DSA:
		p[p_val].type = CKA_SIGN;
		if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) {
			p[p_val].value = (void *) &tval;
			p[p_val].value_len = sizeof(tval);
		} else {
			p[p_val].value = (void *) &fval;
			p[p_val].value_len = sizeof(fval);
		}
		p_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		ret = _dsa_params_generate(sinfo.module, sinfo.pks, _bits,
					   &dsa_params, a, &a_val);
		if (ret < 0) {
			goto cleanup;
		}

		break;
	case GNUTLS_PK_EC:
		p[p_val].type = CKA_SIGN;
		if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) {
			p[p_val].value = (void *) &tval;
			p[p_val].value_len = sizeof(tval);
		} else {
			p[p_val].value = (void *) &fval;
			p[p_val].value_len = sizeof(fval);
		}
		p_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		if (GNUTLS_BITS_ARE_CURVE(bits)) {
			bits = GNUTLS_BITS_TO_CURVE(bits);
		} else {
			bits = _gnutls_ecc_bits_to_curve(bits);
		}

		ret = _gnutls_x509_write_ecc_params(bits, &der);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		a[a_val].type = CKA_EC_PARAMS;
		a[a_val].value = der.data;
		a[a_val].value_len = der.size;
		a_val++;
		break;
	default:
		ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
		goto cleanup;
	}

	/*
	 * on request, add the CKA_WRAP/CKA_UNWRAP key attribute
	 */
	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_KEY_WRAP) {
		p[p_val].type = CKA_UNWRAP;
		p[p_val].value = (void*)&tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;
		a[a_val].type = CKA_WRAP;
		a[a_val].value = (void*)&tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;
	}

	/* a private key is set always as private unless
	 * requested otherwise
	 */
	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) {
		p[p_val].type = CKA_PRIVATE;
		p[p_val].value = (void *) &fval;
		p[p_val].value_len = sizeof(fval);
		p_val++;
	} else {
		p[p_val].type = CKA_PRIVATE;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;
	}

	p[p_val].type = CKA_TOKEN;
	p[p_val].value = (void *) &tval;
	p[p_val].value_len = sizeof(tval);
	p_val++;

	if (label) {
		p[p_val].type = CKA_LABEL;
		p[p_val].value = (void *) label;
		p[p_val].value_len = strlen(label);
		p_val++;

		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) {
		p[p_val].type = CKA_SENSITIVE;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;
	} else {
		p[p_val].type = CKA_SENSITIVE;
		p[p_val].value = (void *) &fval;
		p[p_val].value_len = sizeof(fval);
		p_val++;
	}

	rv = pkcs11_generate_key_pair(sinfo.module, sinfo.pks, &mech, a,
				      a_val, p, p_val, &pub_ctx, &priv_ctx);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* extract the public key */
	if (pubkey) {

		ret = gnutls_pubkey_init(&pkey);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pkcs11_obj_init(&obj);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		obj->pk_algorithm = pk;
		obj->type = GNUTLS_PKCS11_OBJ_PUBKEY;
		ret =
		    pkcs11_read_pubkey(sinfo.module, sinfo.pks, pub_ctx,
				       key_type, obj);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pubkey_import_pkcs11(pkey, obj, 0);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pubkey_export2(pkey, fmt, pubkey);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}

      cleanup:
	if (obj != NULL)
		gnutls_pkcs11_obj_deinit(obj);
	if (pkey != NULL)
		gnutls_pubkey_deinit(pkey);

	if (sinfo.pks != 0)
		pkcs11_close_session(&sinfo);
	gnutls_free(der.data);

	return ret;
}
Esempio n. 4
0
/**
 * gnutls_pkcs11_copy_x509_privkey2:
 * @token_url: A PKCS #11 URL specifying a token
 * @key: A private key
 * @label: A name to be used for the stored data
 * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key
 * @key_usage: One of GNUTLS_KEY_*
 * @flags: One of GNUTLS_PKCS11_OBJ_* flags
 *
 * This function will copy a private key into a PKCS #11 token specified by
 * a URL. It is highly recommended flags to contain %GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE
 * unless there is a strong reason not to.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.4.0
 **/
int
gnutls_pkcs11_copy_x509_privkey2(const char *token_url,
				gnutls_x509_privkey_t key,
				const char *label,
				const gnutls_datum_t *cid,
				unsigned int key_usage, unsigned int flags)
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	size_t id_size;
	uint8_t id[20];
	struct ck_attribute a[32];
	ck_object_class_t class = CKO_PRIVATE_KEY;
	ck_object_handle_t ctx;
	ck_key_type_t type;
	int a_val;
	gnutls_pk_algorithm_t pk;
	gnutls_datum_t p, q, g, y, x;
	gnutls_datum_t m, e, d, u, exp1, exp2;
	struct pkcs11_session_info sinfo;

	PKCS11_CHECK_INIT;

	memset(&p, 0, sizeof(p));
	memset(&q, 0, sizeof(q));
	memset(&g, 0, sizeof(g));
	memset(&y, 0, sizeof(y));
	memset(&x, 0, sizeof(x));
	memset(&m, 0, sizeof(m));
	memset(&e, 0, sizeof(e));
	memset(&d, 0, sizeof(d));
	memset(&u, 0, sizeof(u));
	memset(&exp1, 0, sizeof(exp1));
	memset(&exp2, 0, sizeof(exp2));

	ret = pkcs11_url_to_info(token_url, &info, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	pk = gnutls_x509_privkey_get_pk_algorithm(key);
	FIX_KEY_USAGE(pk, key_usage);

	/* FIXME: copy key usage flags */
	a_val = 0;
	a[a_val].type = CKA_CLASS;
	a[a_val].value = &class;
	a[a_val].value_len = sizeof(class);
	a_val++;

	a[a_val].type = CKA_ID;
	if (cid == NULL || cid->size == 0) {
		id_size = sizeof(id);
		ret = gnutls_x509_privkey_get_key_id(key, 0, id, &id_size);
		if (ret < 0) {
			p11_kit_uri_free(info);
			gnutls_assert();
			return ret;
		}

		a[a_val].value = id;
		a[a_val].value_len = id_size;
	} else {
		a[a_val].value = cid->data;
		a[a_val].value_len = cid->size;
	}
	a_val++;

	a[a_val].type = CKA_SIGN;
	if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) {
		a[a_val].value = (void*)&tval;
		a[a_val].value_len = sizeof(tval);
	} else {
		a[a_val].value = (void*)&fval;
		a[a_val].value_len = sizeof(fval);
	}
	a_val++;

	if (pk == GNUTLS_PK_RSA) {
		a[a_val].type = CKA_DECRYPT;
		if (key_usage & (GNUTLS_KEY_ENCIPHER_ONLY|GNUTLS_KEY_DECIPHER_ONLY)) {
			a[a_val].value = (void*)&tval;
			a[a_val].value_len = sizeof(tval);
		} else {
			a[a_val].value = (void*)&fval;
			a[a_val].value_len = sizeof(fval);
		}
		a_val++;
	}

	a[a_val].type = CKA_TOKEN;
	a[a_val].value = (void *) &tval;
	a[a_val].value_len = sizeof(tval);
	a_val++;

	/* a private key is set always as private unless
	 * requested otherwise
	 */
	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) {
		a[a_val].type = CKA_PRIVATE;
		a[a_val].value = (void *) &fval;
		a[a_val].value_len = sizeof(fval);
		a_val++;
	} else {
		a[a_val].type = CKA_PRIVATE;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH) {
		a[a_val].type = CKA_ALWAYS_AUTHENTICATE;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_EXTRACTABLE) {
		a[a_val].type = CKA_EXTRACTABLE;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		(a_val)++;
	} else {
		a[a_val].type = CKA_EXTRACTABLE;
		a[a_val].value = (void *) &fval;
		a[a_val].value_len = sizeof(fval);
		(a_val)++;
	}

	if (label) {
		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) {
		a[a_val].type = CKA_SENSITIVE;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;
	} else {
		a[a_val].type = CKA_SENSITIVE;
		a[a_val].value = (void *) &fval;
		a[a_val].value_len = sizeof(fval);
		a_val++;
	}

	switch (pk) {
	case GNUTLS_PK_RSA:
		{

			ret =
			    gnutls_x509_privkey_export_rsa_raw2(key, &m,
								&e, &d, &p,
								&q, &u,
								&exp1,
								&exp2);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			type = CKK_RSA;

			skip_leading_zeros(&m);
			skip_leading_zeros(&e);
			skip_leading_zeros(&d);
			skip_leading_zeros(&p);
			skip_leading_zeros(&q);
			skip_leading_zeros(&u);
			skip_leading_zeros(&exp1);
			skip_leading_zeros(&exp2);

			a[a_val].type = CKA_MODULUS;
			a[a_val].value = m.data;
			a[a_val].value_len = m.size;
			a_val++;

			a[a_val].type = CKA_PUBLIC_EXPONENT;
			a[a_val].value = e.data;
			a[a_val].value_len = e.size;
			a_val++;

			a[a_val].type = CKA_PRIVATE_EXPONENT;
			a[a_val].value = d.data;
			a[a_val].value_len = d.size;
			a_val++;

			a[a_val].type = CKA_PRIME_1;
			a[a_val].value = p.data;
			a[a_val].value_len = p.size;
			a_val++;

			a[a_val].type = CKA_PRIME_2;
			a[a_val].value = q.data;
			a[a_val].value_len = q.size;
			a_val++;

			a[a_val].type = CKA_COEFFICIENT;
			a[a_val].value = u.data;
			a[a_val].value_len = u.size;
			a_val++;

			a[a_val].type = CKA_EXPONENT_1;
			a[a_val].value = exp1.data;
			a[a_val].value_len = exp1.size;
			a_val++;

			a[a_val].type = CKA_EXPONENT_2;
			a[a_val].value = exp2.data;
			a[a_val].value_len = exp2.size;
			a_val++;

			break;
		}
	case GNUTLS_PK_DSA:
		{
			ret =
			    gnutls_x509_privkey_export_dsa_raw(key, &p, &q,
							       &g, &y, &x);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			type = CKK_DSA;

			skip_leading_zeros(&p);
			skip_leading_zeros(&q);
			skip_leading_zeros(&g);
			skip_leading_zeros(&y);
			skip_leading_zeros(&x);

			a[a_val].type = CKA_PRIME;
			a[a_val].value = p.data;
			a[a_val].value_len = p.size;
			a_val++;

			a[a_val].type = CKA_SUBPRIME;
			a[a_val].value = q.data;
			a[a_val].value_len = q.size;
			a_val++;

			a[a_val].type = CKA_BASE;
			a[a_val].value = g.data;
			a[a_val].value_len = g.size;
			a_val++;

			a[a_val].type = CKA_VALUE;
			a[a_val].value = x.data;
			a[a_val].value_len = x.size;
			a_val++;

			break;
		}
	case GNUTLS_PK_EC:
		{
			ret =
			    _gnutls_x509_write_ecc_params(key->params.flags,
							  &p);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			ret =
			    _gnutls_mpi_dprint(key->params.
						params[ECC_K], &x);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			type = CKK_ECDSA;

			a[a_val].type = CKA_EC_PARAMS;
			a[a_val].value = p.data;
			a[a_val].value_len = p.size;
			a_val++;

			a[a_val].type = CKA_VALUE;
			a[a_val].value = x.data;
			a[a_val].value_len = x.size;
			a_val++;

			break;
		}
	default:
		gnutls_assert();
		ret = GNUTLS_E_INVALID_REQUEST;
		goto cleanup;
	}

	a[a_val].type = CKA_KEY_TYPE;
	a[a_val].value = &type;
	a[a_val].value_len = sizeof(type);
	a_val++;

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &ctx);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	ret = 0;

      cleanup:
	switch (pk) {
	case GNUTLS_PK_RSA:
		{
			gnutls_free(m.data);
			gnutls_free(e.data);
			gnutls_free(d.data);
			gnutls_free(p.data);
			gnutls_free(q.data);
			gnutls_free(u.data);
			gnutls_free(exp1.data);
			gnutls_free(exp2.data);
			break;
		}
	case GNUTLS_PK_DSA:
		{
			gnutls_free(p.data);
			gnutls_free(q.data);
			gnutls_free(g.data);
			gnutls_free(y.data);
			gnutls_free(x.data);
			break;
		}
	case GNUTLS_PK_EC:
		{
			gnutls_free(p.data);
			gnutls_free(x.data);
			break;
		}
	default:
		gnutls_assert();
		ret = GNUTLS_E_INVALID_REQUEST;
		break;
	}

	if (sinfo.pks != 0)
		pkcs11_close_session(&sinfo);

	return ret;

}
Esempio n. 5
0
/**
 * gnutls_pkcs11_copy_attached_extension:
 * @token_url: A PKCS #11 URL specifying a token
 * @crt: An X.509 certificate object
 * @data: the attached extension
 * @label: A name to be used for the attached extension (may be %NULL)
 * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_*
 *
 * This function will copy an the attached extension in @data for
 * the certificate provided in @crt in the PKCS #11 token specified
 * by the URL (typically a trust module). The extension must be in
 * RFC5280 Extension format.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.3.8
 **/
int
gnutls_pkcs11_copy_attached_extension(const char *token_url,
		       gnutls_x509_crt_t crt,
		       gnutls_datum_t *data,
		       const char *label,
		       unsigned int flags)
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	struct ck_attribute a[MAX_ASIZE];
	ck_object_handle_t ctx;
	unsigned a_vals;
	struct pkcs11_session_info sinfo;
	ck_object_class_t class;
	gnutls_datum_t spki = {NULL, 0};
	
	PKCS11_CHECK_INIT;

	ret = pkcs11_url_to_info(token_url, &info, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret = x509_crt_to_raw_pubkey(crt, &spki);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	class = CKO_X_CERTIFICATE_EXTENSION;
	a_vals = 0;
	a[a_vals].type = CKA_CLASS;
	a[a_vals].value = &class;
	a[a_vals++].value_len = sizeof(class);

	a[a_vals].type = CKA_PUBLIC_KEY_INFO;
	a[a_vals].value = spki.data;
	a[a_vals++].value_len = spki.size;

	a[a_vals].type = CKA_VALUE;
	a[a_vals].value = data->data;
	a[a_vals++].value_len = data->size;

	a[a_vals].type = CKA_TOKEN;
	a[a_vals].value = (void *) &tval;
	a[a_vals++].value_len = sizeof(tval);

	if (label) {
		a[a_vals].type = CKA_LABEL;
		a[a_vals].value = (void *) label;
		a[a_vals++].value_len = strlen(label);
	}

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_vals, &ctx);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	ret = 0;

      cleanup:
	pkcs11_close_session(&sinfo);
	gnutls_free(spki.data);
	return ret;

}
Esempio n. 6
0
/**
 * gnutls_pkcs11_copy_pubkey:
 * @token_url: A PKCS #11 URL specifying a token
 * @pubkey: The public key to copy
 * @label: The name to be used for the stored data
 * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key
 * @key_usage: One of GNUTLS_KEY_*
 * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_*
 *
 * This function will copy a public key object into a PKCS #11 token specified by
 * a URL. Valid flags to mark the key: %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
 * %GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE, %GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE,
 * %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, %GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.4.6
 **/
int
gnutls_pkcs11_copy_pubkey(const char *token_url,
			  gnutls_pubkey_t pubkey, const char *label,
			  const gnutls_datum_t *cid,
			  unsigned int key_usage, unsigned int flags)
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	size_t id_size;
	uint8_t id[20];
	struct ck_attribute a[MAX_ASIZE];
	gnutls_pk_algorithm_t pk;
	ck_object_class_t class = CKO_PUBLIC_KEY;
	ck_object_handle_t ctx;
	unsigned a_val;
	ck_key_type_t type;
	struct pkcs11_session_info sinfo;
	
	PKCS11_CHECK_INIT;

	ret = pkcs11_url_to_info(token_url, &info, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	a[0].type = CKA_CLASS;
	a[0].value = &class;
	a[0].value_len = sizeof(class);

	a[1].type = CKA_TOKEN;
	a[1].value = (void *) &tval;
	a[1].value_len = sizeof(tval);

	a_val = 2;

	ret = add_pubkey(pubkey, a, &a_val);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	if (label) {
		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	pk = gnutls_pubkey_get_pk_algorithm(pubkey, NULL);
	type = pk_to_key_type(pk);
	FIX_KEY_USAGE(pk, key_usage);

	a[a_val].type = CKA_KEY_TYPE;
	a[a_val].value = &type;
	a[a_val].value_len = sizeof(type);
	a_val++;

	a[a_val].type = CKA_ID;
	if (cid == NULL || cid->size == 0) {
		id_size = sizeof(id);
		ret = gnutls_pubkey_get_key_id(pubkey, 0, id, &id_size);
		if (ret < 0) {
			  gnutls_assert();
			  goto cleanup;
		}

		a[a_val].value = id;
		a[a_val].value_len = id_size;
	} else {
		a[a_val].value = cid->data;
		a[a_val].value_len = cid->size;
	}
	a_val++;

	mark_flags(flags, a, &a_val, sinfo.trusted);

	a[a_val].type = CKA_VERIFY;
	if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) {
		a[a_val].value = (void*)&tval;
		a[a_val].value_len = sizeof(tval);
	} else {
		a[a_val].value = (void*)&fval;
		a[a_val].value_len = sizeof(fval);
	}
	a_val++;

	if (pk == GNUTLS_PK_RSA) {
		a[a_val].type = CKA_ENCRYPT;
		if (key_usage & (GNUTLS_KEY_ENCIPHER_ONLY|GNUTLS_KEY_DECIPHER_ONLY)) {
			a[a_val].value = (void*)&tval;
			a[a_val].value_len = sizeof(tval);
		} else {
			a[a_val].value = (void*)&fval;
			a[a_val].value_len = sizeof(fval);
		}
		a_val++;
	}

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &ctx);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* generated! 
	 */

	ret = 0;

      cleanup:
	clean_pubkey(a, a_val);
	pkcs11_close_session(&sinfo);
	return ret;

}
Esempio n. 7
0
/**
 * gnutls_pkcs11_copy_x509_crt2:
 * @token_url: A PKCS #11 URL specifying a token
 * @crt: The certificate to copy
 * @label: The name to be used for the stored data
 * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key
 * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_*
 *
 * This function will copy a certificate into a PKCS #11 token specified by
 * a URL. Valid flags to mark the certificate: %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
 * %GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE, %GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE,
 * %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, %GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.4.0
 **/
int
gnutls_pkcs11_copy_x509_crt2(const char *token_url,
			    gnutls_x509_crt_t crt, const char *label,
			    const gnutls_datum_t *cid,
			    unsigned int flags)
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	size_t der_size, id_size, serial_size;
	gnutls_datum_t serial_der = {NULL, 0};
	uint8_t *der = NULL;
	uint8_t serial[128];
	uint8_t id[20];
	struct ck_attribute a[MAX_ASIZE];
	ck_object_class_t class = CKO_CERTIFICATE;
	ck_certificate_type_t type = CKC_X_509;
	ck_object_handle_t ctx;
	unsigned a_val;
	struct pkcs11_session_info sinfo;
	
	PKCS11_CHECK_INIT;

	ret = pkcs11_url_to_info(token_url, &info, 0);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	der_size = 0;
	ret =
	    gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_DER, NULL,
				   &der_size);
	if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
		gnutls_assert();
		goto cleanup;
	}

	der = gnutls_malloc(der_size);
	if (der == NULL) {
		gnutls_assert();
		ret = GNUTLS_E_MEMORY_ERROR;
		goto cleanup;
	}

	ret =
	    gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_DER, der,
				   &der_size);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	a[0].type = CKA_CLASS;
	a[0].value = &class;
	a[0].value_len = sizeof(class);

	a[1].type = CKA_ID;
	if (cid == NULL || cid->size == 0) {
		id_size = sizeof(id);
		ret = gnutls_x509_crt_get_subject_key_id(crt, id, &id_size, NULL);
		if (ret < 0) {
			id_size = sizeof(id);
			ret = gnutls_x509_crt_get_key_id(crt, 0, id, &id_size);
			if (ret < 0) {
			  gnutls_assert();
			  goto cleanup;
			}
		}

		a[1].value = id;
		a[1].value_len = id_size;
	} else {
		a[1].value = cid->data;
		a[1].value_len = cid->size;
	}

	a[2].type = CKA_VALUE;
	a[2].value = der;
	a[2].value_len = der_size;
	a[3].type = CKA_TOKEN;
	a[3].value = (void *) &tval;
	a[3].value_len = sizeof(tval);
	a[4].type = CKA_CERTIFICATE_TYPE;
	a[4].value = &type;
	a[4].value_len = sizeof(type);
	/* FIXME: copy key usage flags */

	a_val = 5;

	a[a_val].type = CKA_SUBJECT;
	a[a_val].value = crt->raw_dn.data;
	a[a_val].value_len = crt->raw_dn.size;
	a_val++;

	a[a_val].type = CKA_ISSUER;
	a[a_val].value = crt->raw_issuer_dn.data;
	a[a_val].value_len = crt->raw_issuer_dn.size;
	a_val++;

	serial_size = sizeof(serial);
	if (gnutls_x509_crt_get_serial(crt, serial, &serial_size) >= 0) {
		ret = _gnutls_x509_ext_gen_number(serial, serial_size, &serial_der);
		if (ret >= 0) {
			a[a_val].type = CKA_SERIAL_NUMBER;
			a[a_val].value = (void *) serial_der.data;
			a[a_val].value_len = serial_der.size;
			a_val++;
		}
	}

	if (label) {
		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	mark_flags(flags, a, &a_val, sinfo.trusted);

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &ctx);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* generated! 
	 */

	ret = 0;

      cleanup:
	gnutls_free(der);
	gnutls_free(serial_der.data);
	pkcs11_close_session(&sinfo);
	return ret;

}
Esempio n. 8
0
/**
 * gnutls_pkcs11_privkey_generate2:
 * @url: a token URL
 * @pk: the public key algorithm
 * @bits: the security bits
 * @label: a label
 * @fmt: the format of output params. PEM or DER.
 * @pubkey: will hold the public key (may be %NULL)
 * @flags: should be zero
 *
 * This function will generate a private key in the specified
 * by the @url token. The private key will be generate within
 * the token and will not be exportable. This function will
 * store the DER-encoded public key in the SubjectPublicKeyInfo format 
 * in @pubkey. The @pubkey should be deinitialized using gnutls_free().
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.1.5
 **/
int
gnutls_pkcs11_privkey_generate2(const char *url, gnutls_pk_algorithm_t pk,
				unsigned int bits, const char *label,
				gnutls_x509_crt_fmt_t fmt,
				gnutls_datum_t * pubkey,
				unsigned int flags)
{
	int ret;
	const ck_bool_t tval = 1;
	const ck_bool_t fval = 0;
	struct pkcs11_session_info sinfo;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	struct ck_attribute a[10], p[10];
	ck_object_handle_t pub, priv;
	unsigned long _bits = bits;
	int a_val, p_val;
	struct ck_mechanism mech;
	gnutls_pubkey_t pkey = NULL;
	gnutls_pkcs11_obj_t obj = NULL;

	PKCS11_CHECK_INIT;

	memset(&sinfo, 0, sizeof(sinfo));

	ret = pkcs11_url_to_info(url, &info);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	/* a holds the public key template
	 * and p the private key */
	a_val = p_val = 0;
	mech.parameter = NULL;
	mech.parameter_len = 0;
	mech.mechanism = pk_to_genmech(pk);

	switch (pk) {
	case GNUTLS_PK_RSA:
		p[p_val].type = CKA_DECRYPT;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;

		p[p_val].type = CKA_SIGN;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;

		a[a_val].type = CKA_ENCRYPT;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_MODULUS_BITS;
		a[a_val].value = &_bits;
		a[a_val].value_len = sizeof(_bits);
		a_val++;
		break;
	case GNUTLS_PK_DSA:
		p[p_val].type = CKA_SIGN;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_MODULUS_BITS;
		a[a_val].value = &_bits;
		a[a_val].value_len = sizeof(_bits);
		a_val++;
		break;
	case GNUTLS_PK_EC:
		p[p_val].type = CKA_SIGN;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;

		a[a_val].type = CKA_VERIFY;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_MODULUS_BITS;
		a[a_val].value = &_bits;
		a[a_val].value_len = sizeof(_bits);
		a_val++;
		break;
	default:
		ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
		goto cleanup;
	}

	/* a private key is set always as private unless
	 * requested otherwise
	 */
	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) {
		p[p_val].type = CKA_PRIVATE;
		p[p_val].value = (void *) &fval;
		p[p_val].value_len = sizeof(fval);
		p_val++;
	} else {
		p[p_val].type = CKA_PRIVATE;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;
	}

	p[p_val].type = CKA_TOKEN;
	p[p_val].value = (void *) &tval;
	p[p_val].value_len = sizeof(tval);
	p_val++;

	if (label) {
		p[p_val].type = CKA_LABEL;
		p[p_val].value = (void *) label;
		p[p_val].value_len = strlen(label);
		p_val++;

		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) {
		p[p_val].type = CKA_SENSITIVE;
		p[p_val].value = (void *) &tval;
		p[p_val].value_len = sizeof(tval);
		p_val++;
	} else {
		p[p_val].type = CKA_SENSITIVE;
		p[p_val].value = (void *) &fval;
		p[p_val].value_len = sizeof(fval);
		p_val++;
	}

	rv = pkcs11_generate_key_pair(sinfo.module, sinfo.pks, &mech, a,
				      a_val, p, p_val, &pub, &priv);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* extract the public key */
	if (pubkey) {
		ret = gnutls_pubkey_init(&pkey);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pkcs11_obj_init(&obj);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		obj->pk_algorithm = pk;
		obj->type = GNUTLS_PKCS11_OBJ_PUBKEY;
		ret =
		    pkcs11_read_pubkey(sinfo.module, sinfo.pks, pub,
				       mech.mechanism, obj->pubkey);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pubkey_import_pkcs11(pkey, obj, 0);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		ret = gnutls_pubkey_export2(pkey, fmt, pubkey);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}
	}


      cleanup:
	if (obj != NULL)
		gnutls_pkcs11_obj_deinit(obj);
	if (pkey != NULL)
		gnutls_pubkey_deinit(pkey);
	if (sinfo.pks != 0)
		pkcs11_close_session(&sinfo);

	return ret;
}
Esempio n. 9
0
/**
 * gnutls_pkcs11_copy_secret_key:
 * @token_url: A PKCS #11 URL specifying a token
 * @key: The raw key
 * @label: A name to be used for the stored data
 * @key_usage: One of GNUTLS_KEY_*
 * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_*
 *
 * This function will copy a raw secret (symmetric) key into a PKCS #11 
 * token specified by a URL. The key can be marked as sensitive or not.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 2.12.0
 **/
int
gnutls_pkcs11_copy_secret_key(const char *token_url, gnutls_datum_t * key,
			      const char *label,
			      unsigned int key_usage, unsigned int flags
			      /* GNUTLS_PKCS11_OBJ_FLAG_* */ )
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	struct ck_attribute a[12];
	ck_object_class_t class = CKO_SECRET_KEY;
	ck_object_handle_t obj;
	ck_key_type_t keytype = CKK_GENERIC_SECRET;
	ck_bool_t tval = 1;
	int a_val;
	uint8_t id[16];
	struct pkcs11_session_info sinfo;
	
	PKCS11_CHECK_INIT;

	memset(&sinfo, 0, sizeof(sinfo));

	ret = pkcs11_url_to_info(token_url, &info);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* generate a unique ID */
	ret = _gnutls_rnd(GNUTLS_RND_NONCE, id, sizeof(id));
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	/* FIXME: copy key usage flags */

	a[0].type = CKA_CLASS;
	a[0].value = &class;
	a[0].value_len = sizeof(class);
	a[1].type = CKA_VALUE;
	a[1].value = key->data;
	a[1].value_len = key->size;
	a[2].type = CKA_TOKEN;
	a[2].value = &tval;
	a[2].value_len = sizeof(tval);
	a[3].type = CKA_PRIVATE;
	a[3].value = &tval;
	a[3].value_len = sizeof(tval);
	a[4].type = CKA_KEY_TYPE;
	a[4].value = &keytype;
	a[4].value_len = sizeof(keytype);
	a[5].type = CKA_ID;
	a[5].value = id;
	a[5].value_len = sizeof(id);

	a_val = 6;

	if (label) {
		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE)
		tval = 1;
	else
		tval = 0;

	a[a_val].type = CKA_SENSITIVE;
	a[a_val].value = &tval;
	a[a_val].value_len = sizeof(tval);
	a_val++;

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &obj);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* generated! 
	 */

	ret = 0;

      cleanup:
	pkcs11_close_session(&sinfo);

	return ret;

}
Esempio n. 10
0
/*
 * Set up metaslot for the framework using either user configuration
 * or system wide configuration options
 *
 * Also sets up the global "slottable" to have the first slot be metaslot.
 */
static CK_RV
setup_metaslot(uentry_t *metaslot_entry) {
	CK_RV rv;
	CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL;
	pkcs11_slot_t *cur_slot;

	/* process policies for mechanisms */
	if ((metaslot_entry) && (metaslot_entry->count > 0)) {
		rv = pkcs11_mech_parse(metaslot_entry->policylist,
		    &prov_pol_mechs, metaslot_entry->count);

		if (rv == CKR_HOST_MEMORY) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Could not parse configuration,"
			    "out of memory. Cannot continue parsing "
			    "%s.\n", _PATH_PKCS11_CONF);
			return (rv);
		} else if (rv == CKR_MECHANISM_INVALID) {
			/*
			 * Configuration file is corrupted for metaslot
			 */
			cryptoerror(LOG_ERR,
			    "libpkcs11: Policy invalid or corrupted "
			    "for metaslot. Use cryptoadm(1M) to fix "
			    "this. Disabling metaslot functionality.\n");
			metaslot_enabled = B_FALSE;
			return (rv);
		}
	}

	/*
	 * Check for metaslot policy.  If all mechanisms are
	 * disabled, disable metaslot since there is nothing
	 * interesting for it to do
	 */
	if ((metaslot_entry) && (metaslot_entry->flag_enabledlist) &&
	    (prov_pol_mechs == NULL)) {
		metaslot_enabled = B_FALSE;
		return (rv);
	}

	/*
	 * save system wide value for metaslot's keystore.
	 * If either slot description or token label is specified by
	 * the user, the system wide value for both is ignored.
	 */
	if ((metaslot_entry) &&
	    (!metaslot_config.keystore_token_specified) &&
	    (!metaslot_config.keystore_slot_specified)) {
		/*
		 * blank_str is used for comparing with token label,
		 * and slot description, make sure it is better than
		 * the larger of both
		 */
		char blank_str[TOKEN_LABEL_SIZE + SLOT_DESCRIPTION_SIZE];

		bzero(blank_str, sizeof (blank_str));

		if (memcmp(metaslot_entry->metaslot_ks_token,
		    blank_str, TOKEN_LABEL_SIZE) != 0) {
			metaslot_config.keystore_token_specified = B_TRUE;
			(void) strlcpy(
			    (char *)metaslot_config.keystore_token,
			    (const char *)metaslot_entry->metaslot_ks_token,
			    TOKEN_LABEL_SIZE);
		}

		if (memcmp(metaslot_entry->metaslot_ks_slot,
		    blank_str, SLOT_DESCRIPTION_SIZE) != 0) {
			metaslot_config.keystore_slot_specified = B_TRUE;
			(void) strlcpy(
			    (char *)metaslot_config.keystore_slot,
			    (const char *)metaslot_entry->metaslot_ks_slot,
			    SLOT_DESCRIPTION_SIZE);
		}
	}

	/* check system-wide value for auto_key_migrate */
	if (metaslot_config.auto_key_migrate_specified) {
		/* take user's specified value */
		metaslot_auto_key_migrate = metaslot_config.auto_key_migrate;
	} else {
		if (metaslot_entry) {
			/* use system-wide default */
			metaslot_auto_key_migrate =
			    metaslot_entry->flag_metaslot_auto_key_migrate;
		} else {
			/*
			 * there's no system wide metaslot entry,
			 * default auto_key_migrate to true
			 */
			metaslot_auto_key_migrate = B_TRUE;
		}
	}


	/* Make first slotID be 0, for metaslot. */
	slottable->st_first = 0;

	/* Set up the slottable entry for metaslot */
	slottable->st_slots[0] = NULL;
	cur_slot = calloc(1, sizeof (pkcs11_slot_t));
	if (cur_slot == NULL) {
		rv = CKR_HOST_MEMORY;
		return (rv);
	}
	cur_slot->sl_wfse_state = WFSE_CLEAR;
	cur_slot->sl_enabledpol = B_FALSE;
	cur_slot->sl_no_wfse = B_FALSE;
	(void) pthread_mutex_init(&cur_slot->sl_mutex, NULL);

	/*
	 * The metaslot entry was prealloc'd by
	 * pkcs11_slottable_increase()
	 */
	(void) pthread_mutex_lock(&slottable->st_mutex);
	slottable->st_slots[0] = cur_slot;
	(void) pthread_mutex_unlock(&slottable->st_mutex);

	(void) pthread_mutex_lock(&cur_slot->sl_mutex);
	cur_slot->sl_id = METASLOT_SLOTID;
	cur_slot->sl_func_list = &metaslot_functionList;
	if (metaslot_entry) {
		cur_slot->sl_enabledpol = metaslot_entry->flag_enabledlist;
		cur_slot->sl_pol_count = metaslot_entry->count;
	} else {
		/* if no metaslot entry, assume all mechs are enabled */
		cur_slot->sl_enabledpol = B_FALSE;
		cur_slot->sl_pol_count = 0;
	}
	cur_slot->sl_pol_mechs = prov_pol_mechs;
	cur_slot->sl_dldesc = NULL; /* not applicable */
	cur_slot->sl_prov_id = 0;
	(void) pthread_mutex_unlock(&cur_slot->sl_mutex);

	/* Call the meta_Initialize() to initialize metaslot */
	rv = meta_Initialize(NULL);
	if (rv != CKR_OK) {
		cryptoerror(LOG_ERR,
		    "libpkcs11: Can't initialize metaslot (%s)",
		    pkcs11_strerror(rv));
		goto cleanup;
	}

	return (CKR_OK);

cleanup:
	metaslot_enabled = B_FALSE;
	slottable->st_slots[0] = NULL;

	if (cur_slot) {
		(void) pthread_mutex_destroy(&cur_slot->sl_mutex);
		free(cur_slot);
	}
	return (rv);
}
Esempio n. 11
0
/*
 * For each provider found in pkcs11.conf: expand $ISA if necessary,
 * verify the module is signed, load the provider, find all of its
 * slots, and store the function list and disabled policy.
 *
 * This function requires that the uentrylist_t and pkcs11_slottable_t
 * already have memory allocated, and that the uentrylist_t is already
 * populated with provider and policy information.
 *
 * pInitArgs can be set to NULL, but is normally the same value
 * the framework's C_Initialize() was called with.
 *
 * Unless metaslot is explicitly disabled, it is setup when all other
 * providers are loaded.
 */
CK_RV
pkcs11_slot_mapping(uentrylist_t *pplist, CK_VOID_PTR pInitArgs)
{
	CK_RV rv = CKR_OK;
	CK_RV prov_rv;			/* Provider's return code */
	CK_INFO prov_info;
	CK_RV (*Tmp_C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR);
	CK_FUNCTION_LIST_PTR prov_funcs = NULL; /* Provider's function list */
	CK_ULONG prov_slot_count; 		/* Number of slots */
	CK_SLOT_ID slot_id; 		/* slotID assigned for framework */
	CK_SLOT_ID_PTR prov_slots = NULL; 	/* Provider's slot list */
					/* Enabled or Disabled policy */
	CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL;

	void *dldesc = NULL;
	char *isa, *fullpath = NULL, *dl_error;
	uentrylist_t *phead;
	uint_t prov_count = 0;
	pkcs11_slot_t *cur_slot;
	CK_ULONG i;
	size_t len;
	uentry_t *metaslot_entry = NULL;
	/* number of slots in the framework, not including metaslot */
	uint_t slot_count = 0;

	ELFsign_status_t estatus = ELFSIGN_UNKNOWN;
	char *estatus_str = NULL;
	int kcfdfd = -1;
	door_arg_t	darg;
	kcf_door_arg_t *kda = NULL;
	kcf_door_arg_t *rkda = NULL;
	int r;

	phead = pplist;

	/* Loop through all of the provider listed in pkcs11.conf */
	while (phead != NULL) {
		if (!strcasecmp(phead->puent->name, "metaslot")) {
			/*
			 * Skip standard processing for metaslot
			 * entry since it is not an actual library
			 * that can be dlopened.
			 * It will be initialized later.
			 */
			if (metaslot_entry != NULL) {
				cryptoerror(LOG_ERR,
				    "libpkcs11: multiple entries for metaslot "
				    "detected.  All but the first entry will "
				    "be ignored");
			} else {
				metaslot_entry = phead->puent;
			}
			goto contparse;
		}

		/* Check for Instruction Set Architecture indicator */
		if ((isa = strstr(phead->puent->name, PKCS11_ISA)) != NULL) {
			/* Substitute the architecture dependent path */
			len = strlen(phead->puent->name) -
			    strlen(PKCS11_ISA) +
			    strlen(PKCS11_ISA_DIR) + 1;
			if ((fullpath = (char *)malloc(len)) == NULL) {
				cryptoerror(LOG_ERR,
				    "libpksc11: parsing %s, out of memory. "
				    "Cannot continue parsing.",
				    _PATH_PKCS11_CONF);
				rv = CKR_HOST_MEMORY;
				goto conferror;
			}
			*isa = '\000';
			isa += strlen(PKCS11_ISA);
			(void) snprintf(fullpath, len, "%s%s%s",
			    phead->puent->name, PKCS11_ISA_DIR, isa);
		} else if ((fullpath = strdup(phead->puent->name)) == 0) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: parsing %s, out of memory. "
			    "Cannot continue parsing.",
			    _PATH_PKCS11_CONF);
			rv = CKR_HOST_MEMORY;
			goto conferror;
		}

		/*
		 * Open the provider. Use RTLD_NOW to make sure we
		 * will not encounter symbol referencing errors later.
		 * Use RTLD_GROUP to limit the provider to it's own
		 * symbols, which prevents it from mistakenly accessing
		 * the framework's C_* functions.
		 */
		dldesc = dlopen(fullpath, RTLD_NOW|RTLD_GROUP);

		/*
		 * If we failed to load it, we will just skip this
		 * provider and move on to the next one.
		 */
		if (dldesc == NULL) {
			dl_error = dlerror();
			cryptoerror(LOG_ERR,
			    "libpkcs11: Cannot load PKCS#11 library %s.  "
			    "dlerror: %s. %s",
			    fullpath, dl_error != NULL ? dl_error : "Unknown",
			    conf_err);
			goto contparse;
		}

		/* Get the pointer to provider's C_GetFunctionList() */
		Tmp_C_GetFunctionList =
		    (CK_RV(*)())dlsym(dldesc, "C_GetFunctionList");

		/*
		 * If we failed to get the pointer to C_GetFunctionList(),
		 * skip this provider and continue to the next one.
		 */
		if (Tmp_C_GetFunctionList == NULL) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Could not dlsym() C_GetFunctionList() "
			    "for %s. May not be a PKCS#11 library. %s",
			    fullpath, conf_err);
			(void) dlclose(dldesc);
			goto contparse;
		}


		/* Get the provider's function list */
		prov_rv = Tmp_C_GetFunctionList(&prov_funcs);

		/*
		 * If we failed to get the provider's function list,
		 * skip this provider and continue to the next one.
		 */
		if (prov_rv != CKR_OK) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Could not get function list for %s. "
			    "%s Error: %s.",
			    fullpath, conf_err, pkcs11_strerror(prov_rv));
			(void) dlclose(dldesc);
			goto contparse;
		}

		/* Initialize this provider */
		prov_rv = prov_funcs->C_Initialize(pInitArgs);

		/*
		 * If we failed to initialize this provider,
		 * skip this provider and continue to the next one.
		 */
		if ((prov_rv != CKR_OK) &&
		    (prov_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Could not initialize %s. "
			    "%s Error: %s.",
			    fullpath, conf_err, pkcs11_strerror(prov_rv));
			(void) dlclose(dldesc);
			goto contparse;
		}

		/*
		 * Make sure this provider is implementing the same
		 * major version, and at least the same minor version
		 * that we are.
		 */
		prov_rv = prov_funcs->C_GetInfo(&prov_info);

		/*
		 * If we can't verify that we are implementing the
		 * same major version, or if it is definitely not the same
		 * version, we need to skip this provider.
		 */
		if ((prov_rv != CKR_OK) ||
		    (prov_info.cryptokiVersion.major !=
			CRYPTOKI_VERSION_MAJOR))  {
			if (prov_rv != CKR_OK) {
				cryptoerror(LOG_ERR,
				    "libpkcs11: Could not verify version of "
				    "%s. %s Error: %s.", fullpath,
				    conf_err, pkcs11_strerror(prov_rv));
			} else {
				cryptoerror(LOG_ERR,
				    "libpkcs11: Only CRYPTOKI major version "
				    "%d is supported.  %s is major "
				    "version %d. %s",
				    CRYPTOKI_VERSION_MAJOR, fullpath,
				    prov_info.cryptokiVersion.major, conf_err);
			}
			(void) prov_funcs->C_Finalize(NULL);
			(void) dlclose(dldesc);
			goto contparse;
		}

		/*
		 * Warn the administrator (at debug) that a provider with
		 * a significantly older or newer version of
		 * CRYPTOKI is being used.  It should not cause
		 * problems, but logging a warning makes it easier
		 * to debug later.
		 */
		if ((prov_info.cryptokiVersion.minor <
			CRYPTOKI_VERSION_WARN_MINOR) ||
		    (prov_info.cryptokiVersion.minor >
			CRYPTOKI_VERSION_MINOR)) {
			cryptoerror(LOG_DEBUG,
			    "libpkcs11: %s CRYPTOKI minor version, %d, may "
			    "not be compatible with minor version %d.",
			    fullpath, prov_info.cryptokiVersion.minor,
			    CRYPTOKI_VERSION_MINOR);
		}

		/*
		 * Find out how many slots this provider has,
		 * call with tokenPresent set to FALSE so all
		 * potential slots are returned.
		 */
		prov_rv = prov_funcs->C_GetSlotList(FALSE,
		    NULL, &prov_slot_count);

		/*
		 * If the call failed, or if no slots are returned,
		 * then skip this provider and continue to next one.
		 */
		if (prov_rv != CKR_OK) {
			cryptoerror(LOG_ERR,
			    "libpksc11: Could not get slot list from %s. "
			    "%s Error: %s.",
			    fullpath, conf_err, pkcs11_strerror(prov_rv));
			(void) prov_funcs->C_Finalize(NULL);
			(void) dlclose(dldesc);
			goto contparse;
		}

		if (prov_slot_count == 0) {
			cryptodebug("libpkcs11: No slots presented from %s. "
			    "Skipping this plug-in at this time.\n",
			    fullpath);
			(void) prov_funcs->C_Finalize(NULL);
			(void) dlclose(dldesc);
			goto contparse;
		}

		/*
		 * Verify that the module is signed correctly.
		 *
		 * NOTE: there is a potential race condition here,
		 * since the module is verified well after we have
		 * opened the provider via dlopen().  This could be
		 * resolved by a variant of dlopen() that would take a
		 * file descriptor as an argument and by changing the
		 * kcfd libelfsign door protocol to use and fd instead
		 * of a path - but that wouldn't work in the kernel case.
		 */
		while ((kcfdfd = open(_PATH_KCFD_DOOR, O_RDONLY)) == -1) {
			if (!(errno == EINTR || errno == EAGAIN))
				break;
		}
		if (kcfdfd == -1) {
			cryptoerror(LOG_ERR, "libpkcs11: open %s: %s",
				_PATH_KCFD_DOOR,
			    strerror(errno));
			goto verifycleanup;
		}

		/* Mark the door "close on exec" */
		(void) fcntl(kcfdfd, F_SETFD, FD_CLOEXEC);

		if ((kda = malloc(sizeof (kcf_door_arg_t))) == NULL) {
			cryptoerror(LOG_ERR, "libpkcs11: malloc of kda "
				"failed: %s", strerror(errno));
			goto verifycleanup;
		}
		kda->da_version = KCF_KCFD_VERSION1;
		kda->da_iskernel = B_FALSE;
		(void) strlcpy(kda->da_u.filename, fullpath,
		    strlen(fullpath) + 1);

		darg.data_ptr = (char *)kda;
		darg.data_size = sizeof (kcf_door_arg_t);
		darg.desc_ptr = NULL;
		darg.desc_num = 0;
		darg.rbuf = (char *)kda;
		darg.rsize = sizeof (kcf_door_arg_t);

		while ((r = door_call(kcfdfd, &darg)) != 0) {
			if (!(errno == EINTR || errno == EAGAIN))
				break;
		}

		if (r != 0) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Unable to contact kcfd: %s",
			    strerror(errno));
			goto verifycleanup;
		}

		/*LINTED*/
		rkda = (kcf_door_arg_t *)darg.rbuf;
		if (rkda->da_version != KCF_KCFD_VERSION1) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: kcfd and libelfsign versions "
			    "don't match: got %d expected %d",
			    rkda->da_version, KCF_KCFD_VERSION1);
			goto verifycleanup;
		}
		estatus = rkda->da_u.result.status;
verifycleanup:
		if (kcfdfd != -1) {
			(void) close(kcfdfd);
		}
		if (rkda != NULL && rkda != kda)
			(void) munmap((char *)rkda, darg.rsize);
		if (kda != NULL) {
			bzero(kda, sizeof (kda));
			free(kda);
			kda = NULL;
			rkda = NULL;	/* rkda is an alias of kda */
		}

		switch (estatus) {
		case ELFSIGN_SUCCESS:
		case ELFSIGN_RESTRICTED:
			break;
		case ELFSIGN_NOTSIGNED:
			estatus_str = strdup("not a signed provider.");
			break;
		case ELFSIGN_FAILED:
			estatus_str = strdup("signature verification failed.");
			break;
		default:
			estatus_str = strdup("unexpected failure in ELF "
			    "signature verification. "
			    "System may have been tampered with.");
		}
		if (estatus_str != NULL) {
			cryptoerror(LOG_ERR, "libpkcs11: %s %s %s",
			    fullpath, estatus_str ? estatus_str : "",
			    estatus == ELFSIGN_UNKNOWN ?
			    "Cannot continue parsing " _PATH_PKCS11_CONF:
			    conf_err);
			(void) prov_funcs->C_Finalize(NULL);
			(void) dlclose(dldesc);
			free(estatus_str);
			estatus_str = NULL;
			if (estatus == ELFSIGN_UNKNOWN) {
				prov_funcs = NULL;
				dldesc = NULL;
				rv = CKR_GENERAL_ERROR;
				goto conferror;
			}
			goto contparse;
		}

		/* Allocate memory for the slot list */
		prov_slots = calloc(prov_slot_count, sizeof (CK_SLOT_ID));

		if (prov_slots == NULL) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Could not allocate memory for "
			    "plug-in slots. Cannot continue parsing %s\n",
			    _PATH_PKCS11_CONF);
			rv = CKR_HOST_MEMORY;
			goto conferror;
		}

		/* Get slot list from provider */
		prov_rv = prov_funcs->C_GetSlotList(FALSE,
		    prov_slots, &prov_slot_count);

		/* if second call fails, drop this provider */
		if (prov_rv != CKR_OK) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: Second call to C_GetSlotList() for %s "
			    "failed. %s Error: %s.",
			    fullpath, conf_err, pkcs11_strerror(prov_rv));
			(void) prov_funcs->C_Finalize(NULL);
			(void) dlclose(dldesc);
			goto contparse;
		}

		/*
		 * Parse the list of disabled or enabled mechanisms, will
		 * apply to each of the provider's slots.
		 */
		if (phead->puent->count > 0) {
			rv = pkcs11_mech_parse(phead->puent->policylist,
			    &prov_pol_mechs, phead->puent->count);

			if (rv == CKR_HOST_MEMORY) {
				cryptoerror(LOG_ERR,
				    "libpkcs11: Could not parse configuration,"
				    "out of memory. Cannot continue parsing "
				    "%s.", _PATH_PKCS11_CONF);
				goto conferror;
			} else if (rv == CKR_MECHANISM_INVALID) {
				/*
				 * Configuration file is corrupted for this
				 * provider.
				 */
				cryptoerror(LOG_ERR,
				    "libpkcs11: Policy invalid or corrupted "
				    "for %s. Use cryptoadm(1M) to fix "
				    "this. Skipping this plug-in.",
				    fullpath);
				(void) prov_funcs->C_Finalize(NULL);
				(void) dlclose(dldesc);
				goto contparse;
			}
		}

		/* Allocate memory in our slottable for these slots */
		rv = pkcs11_slottable_increase(prov_slot_count);

		/*
		 * If any error is returned, it will be memory related,
		 * so we need to abort the attempt at filling the
		 * slottable.
		 */
		if (rv != CKR_OK) {
			cryptoerror(LOG_ERR,
			    "libpkcs11: slottable could not increase. "
			    "Cannot continue parsing %s.",
			    _PATH_PKCS11_CONF);
			goto conferror;
		}

		/* Configure information for each new slot */
		for (i = 0; i < prov_slot_count; i++) {
			/* allocate slot in framework */
			rv = pkcs11_slot_allocate(&slot_id);
			if (rv != CKR_OK) {
				cryptoerror(LOG_ERR,
				    "libpkcs11: Could not allocate "
				    "new slot.  Cannot continue parsing %s.",
				    _PATH_PKCS11_CONF);
				goto conferror;
			}
			slot_count++;
			cur_slot = slottable->st_slots[slot_id];
			(void) pthread_mutex_lock(&cur_slot->sl_mutex);
			cur_slot->sl_id = prov_slots[i];
			cur_slot->sl_func_list = prov_funcs;
			cur_slot->sl_enabledpol =
			    phead->puent->flag_enabledlist;
			cur_slot->sl_pol_mechs = prov_pol_mechs;
			cur_slot->sl_pol_count = phead->puent->count;
			cur_slot->sl_norandom = phead->puent->flag_norandom;
			cur_slot->sl_dldesc = dldesc;
			cur_slot->sl_prov_id = prov_count + 1;
			(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
		}


		/* Set and reset values to process next provider */
		prov_count++;
contparse:
		prov_slot_count = 0;
		Tmp_C_GetFunctionList = NULL;
		prov_funcs = NULL;
		dldesc = NULL;
		if (fullpath != NULL) {
			free(fullpath);
			fullpath = NULL;
		}
		if (prov_slots != NULL) {
			free(prov_slots);
			prov_slots = NULL;
		}
		phead = phead->next;
	}

	if (slot_count == 0) {
		/*
		 * there's no other slot in the framework,
		 * there is nothing to do
		 */
		goto config_complete;
	}

	/* determine if metaslot should be enabled */

	/*
	 * Check to see if any environment variable is defined
	 * by the user for configuring metaslot.  Users'
	 * setting always take precedence over the system wide
	 * setting.  So, we will first check for any user's
	 * defined env variables before looking at the system-wide
	 * configuration.
	 */
	get_user_metaslot_config();

	/* no metaslot entry in /etc/crypto/pkcs11.conf */
	if (!metaslot_entry) {
		/*
		 * If user env variable indicates metaslot should be enabled,
		 * but there's no entry in /etc/crypto/pkcs11.conf for
		 * metaslot at all, will respect the user's defined value
		 */
		if ((metaslot_config.enabled_specified) &&
		    (metaslot_config.enabled)) {
			metaslot_enabled = B_TRUE;
		}
	} else {
		if (!metaslot_config.enabled_specified) {
			/*
			 * take system wide value if
			 * it is not specified by user
			 */
			metaslot_enabled
			    = metaslot_entry->flag_metaslot_enabled;
		} else {
			metaslot_enabled = metaslot_config.enabled;
		}
	}

	/*
	 *
	 * As long as the user or system configuration file does not
	 * disable metaslot, it will be enabled regardless of the
	 * number of slots plugged into the framework.  Therefore,
	 * metaslot is enabled even when there's only one slot
	 * plugged into the framework.  This is necessary for
	 * presenting a consistent token label view to applications.
	 *
	 * However, for the case where there is only 1 slot plugged into
	 * the framework, we can use "fastpath".
	 *
	 * "fastpath" will pass all of the application's requests
	 * directly to the underlying provider.  Only when policy is in
	 * effect will we need to keep slotID around.
	 *
	 * When metaslot is enabled, and fastpath is enabled,
	 * all the metaslot processing will be skipped.
	 * When there is only 1 slot, there's
	 * really not much metaslot can do in terms of combining functionality
	 * of different slots, and object migration.
	 *
	 */

	/* check to see if fastpath can be used */
	if (slottable->st_last == slottable->st_first) {

		cur_slot = slottable->st_slots[slottable->st_first];

		(void) pthread_mutex_lock(&cur_slot->sl_mutex);

		if ((cur_slot->sl_pol_count == 0) &&
		    (!cur_slot->sl_enabledpol) && (!cur_slot->sl_norandom)) {
			/* No policy is in effect, don't need slotid */
			fast_funcs = cur_slot->sl_func_list;
			purefastpath = B_TRUE;
		} else {
			fast_funcs = cur_slot->sl_func_list;
			fast_slot = slottable->st_first;
			policyfastpath = B_TRUE;
		}

		(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
	}

	if ((purefastpath || policyfastpath) && (!metaslot_enabled)) {
		goto config_complete;
	}

	/*
	 * If we get here, there are more than 2 slots in the framework,
	 * we need to set up metaslot if it is enabled
	 */
	if (metaslot_enabled) {
		rv = setup_metaslot(metaslot_entry);
		if (rv != CKR_OK) {
			goto conferror;
		}
	}


config_complete:

	return (CKR_OK);

conferror:
	/*
	 * This cleanup code is only exercised when a major,
	 * unrecoverable error like "out of memory" occurs.
	 */
	if (prov_funcs != NULL) {
		(void) prov_funcs->C_Finalize(NULL);
	}
	if (dldesc != NULL) {
		(void) dlclose(dldesc);
	}
	if (fullpath != NULL) {
		free(fullpath);
		fullpath = NULL;
	}
	if (prov_slots != NULL) {
		free(prov_slots);
		prov_slots = NULL;
	}

	return (rv);
}
Esempio n. 12
0
static int
key_hdl_to_zc(libzfs_handle_t *hdl, zfs_handle_t *zhp, char *keysource,
    int crypt, zfs_cmd_t *zc, zfs_crypto_zckey_t cmd)
{
    //	CK_SESSION_HANDLE session;
	int ret = 0;
	key_format_t format;
	key_locator_t locator;
	char *uri;
	//pkcs11_uri_t p11uri;
	size_t keylen = zio_crypt_table[crypt].ci_keylen;
	char *keydata = NULL;
	size_t keydatalen = 0;
	char *tmpkeydata = NULL;
	size_t tmpkeydatalen = 0;
	uint64_t salt;
	//struct cb_arg_curl cb_curl = { 0 };

    fprintf(stderr, "in key_hdl_to_zc\r\n");

	zc->zc_crypto.zic_clone_newkey = hdl->libzfs_crypt.zc_clone_newkey;

	if (!keysource_prop_parser(keysource, &format, &locator, &uri)) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
		    "invalid keysource property."));
		return (-1);
	}

	/*
	 * First check if there was anything in the handle already
	 * if so we use that and we are done with locating the data.
	 * Note that we may be looking at other fields
	 * and zic_clone_newkey even if zc_key_data_len is empty.
	 *
	 * We allow this regardless of the locator so that things
	 * like a PAM module can provide the passphrase but the user
	 * can still have "passphrase,prompt" to use zfs(1M) interactively.
	 */
	if (hdl->libzfs_crypt.zc_key_data_len != 0) {
		keydata = zfs_alloc(hdl, hdl->libzfs_crypt.zc_key_data_len);
		bcopy(hdl->libzfs_crypt.zc_key_data, keydata,
		    hdl->libzfs_crypt.zc_key_data_len);
		keydatalen = hdl->libzfs_crypt.zc_key_data_len;
		goto format_key;
	}

	/*
	 * Get the key from the URI or prompt for it.
	 * If the format is raw then prompting is a simple read(2)
	 * otherwise we put up a prompt saying what we are asking for.
	 * We can't do this with the 'zfs mount -a' that is in
	 * sys:/system/filesystem/local:default but we shouldn't
	 * cause errors or warnings there either.
	 */
	switch (locator) {
	case KEY_LOCATOR_PROMPT:
		if (format == KEY_FORMAT_RAW) {
			keydata = zfs_alloc(hdl, keylen);
			errno = 0;
			keydatalen = read(STDIN_FILENO, keydata, keylen);
			if (keydatalen != keylen) {
				free(keydata);
				return (-1);
			}

		} else {
			int tries = 0;
			do {
				/* get_passphrase allocates keydata */
				ret = get_passphrase(hdl, &keydata,
				    &keydatalen, format, zc, cmd);
			} while (ret != 0 && ++tries < 3);
			if (ret)
				return (-1);
		}
		break;
	case KEY_LOCATOR_FILE_URI:
		/*
		 * Need to tell pkcs11_read_data() how big of a key
		 * we want in case the locator URI is a device (eg, /dev/random)
		 * to be read from and not a file.
		 *
		 * Note that pkcs11_read_data allocates memory with malloc
		 * that we need to free.
		 */
#if 0 // FIXME
		keydatalen = keylen;
		ret = pkcs11_read_data(&(uri[7]),
		    (void **)&keydata, &keydatalen);
		if (ret != 0) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "failed to read key file: %s"), strerror(ret));
			errno = ret;
			return (-1);
		}
#endif
		break;

	case KEY_LOCATOR_PKCS11_URI:
#if 0 // FIXME
		keydatalen = keylen;
		/*
		 * Parse out the PKCS#11 URI and
		 * get the value of the wrapping key.
		 */
		if (pkcs11_parse_uri(uri, &p11uri) != PK11_URI_OK) {
			errno = EINVAL;
			return (-1);
		}
		ret = get_pkcs11_key_value(hdl, zc, cmd, &p11uri,
		    &keydata, &keydatalen);
		pkcs11_free_uri(&p11uri);
		if (ret != 0) {
			return (-1);
		}
#endif
		break;
	case KEY_LOCATOR_HTTPS_URI: {
#if 0
		CURL *curl_hdl = curl_easy_init();
		CURLcode cerr;

		cerr = curl_easy_setopt(curl_hdl, CURLOPT_URL, uri);
		if (cerr != CURLE_OK)
			goto curl_fail;
		cerr = curl_easy_setopt(curl_hdl, CURLOPT_FAILONERROR, 1L);
		if (cerr != CURLE_OK)
			goto curl_fail;
		cerr = curl_easy_setopt(curl_hdl, CURLOPT_WRITEFUNCTION,
		    get_keydata_curl);
		if (cerr != CURLE_OK)
			goto curl_fail;
		cb_curl.cb_hdl = hdl;
		cerr = curl_easy_setopt(curl_hdl, CURLOPT_WRITEDATA,
		    &cb_curl);
		if (cerr != CURLE_OK)
			goto curl_fail;
		cerr = curl_easy_perform(curl_hdl);
curl_fail:
		/*
		 * Just deal with libcurl errors here, reading the wrong key
		 * size is dealt with generically in the format_key section.
		 */
		if (cerr != 0) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "failed to retreive key from '%s': '%s'"),
			    uri, curl_easy_strerror(cerr));
			return (-1);
		}

		keydata = cb_curl.cb_keydata;
		keydatalen = cb_curl.cb_keydatalen;

		curl_easy_cleanup(curl_hdl);
#endif
		break;

        case KEY_LOCATOR_NONE: // Avoid Warning
            break;
		}
	}

format_key:
	if (keydata == NULL || keydatalen == 0) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
		    "key can not be of zero size"));
		errno = ret;
		return (-1);
	}

	/*
	 * Now that we have the key do any transform that is necessary
	 * such as turning the hex format into raw or in the case of
	 * a passphrase running it through PKCS#5 to get the raw key.
	 *
	 * Note that zic_keydata is not malloc'd memory so that we
	 * don't have to worry about our caller freeing it.
	 */
	switch (format) {
	case KEY_FORMAT_RAW:
		bcopy(keydata, zc->zc_crypto.zic_keydata, keydatalen);
		zc->zc_crypto.zic_keydatalen = keydatalen;
		zc->zc_crypto.zic_salt = 0;
		break;
	case KEY_FORMAT_HEX:
		/*
		 * If the keylen is not on the byte boundary, in terms of hex
		 * format, and that extra char is a linefeed, we can trim it
		 */
		if (keydatalen == (keylen * 2) + 1 &&
		    keydata[keydatalen] == '\n') {
			keydatalen--;
		}

		/*
		 * hexstr_to_bytes allocates memory with malloc
		 * but we want the data in zic_keydata which isn't malloc'd
		 * so to avoid a memory leak we use a tmpkeydata buffer
		 * and bcopy it.
		 */
#if 0        // FIXME
		ret = hexstr_to_bytes(keydata, keydatalen,
		    (uchar_t **)&tmpkeydata, &tmpkeydatalen);
#endif

		if (ret) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "invalid hex format key."));
			errno = EACCES;
			ret = -1;
			goto out;
		}

		bcopy(tmpkeydata, zc->zc_crypto.zic_keydata, tmpkeydatalen);
		bzero(tmpkeydata, tmpkeydatalen);
		free(tmpkeydata);
		zc->zc_crypto.zic_keydatalen = tmpkeydatalen;
		zc->zc_crypto.zic_salt = 0;
		break;
	case KEY_FORMAT_PASSPHRASE:
		/* Remove any extra linefeed that may be on the end */
		if (keydata[keydatalen - 1] == '\n')
			keydatalen--;

		if (cmd == ZFS_CRYPTO_KEY_LOAD) {
			salt = zfs_prop_get_int(zhp, ZFS_PROP_SALT);
		} else {
#if 0 // FIXME
			ret = pkcs11_get_random(&salt, sizeof (uint64_t));
			if (ret) {
				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
				    "failed to obtain salt: %s."),
				    pkcs11_strerror(ret));
				errno = EINVAL;
				ret = -1;
				goto out;
			}
#endif
		}

        fprintf(stderr, "Key is '%s' and is len %u\r\n",
                keydata, keydatalen);

        // FIXME
        tmpkeydata = strdup(keydata);
        tmpkeydatalen = keydatalen;
        salt = 0x1234;

#if 0 // FIXME
		ret = SUNW_C_GetMechSession(CKM_PKCS5_PBKD2, &session);
		if (ret) {
			zfs_error_aux(hdl,
			    dgettext(TEXT_DOMAIN,
			    "failed to access CKM_PKCS5_PBKD2: %s."),
			    pkcs11_strerror(ret));
			errno = EINVAL;
			ret = -1;
			goto out;
		}

		/*
		 * pkcs11_PasswdToKey allocates memory with malloc
		 * but we want the data in zic_keydata which isn't malloc'd
		 * so to avoid a memory leak we use a tmpkeydata buffer
		 * and bcopy it.
		 */
		ret = pkcs11_PasswdToKey(session, keydata, keydatalen,
		    (void *)&salt, sizeof (uint64_t), CKK_AES,
		    keylen, (void **)&tmpkeydata, &tmpkeydatalen);

		(void) C_CloseSession(session);

		if (ret) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "failed to generate key: %s."),
			    pkcs11_strerror(ret));
			errno = EINVAL;
			ret = -1;
			goto out;
		}
#endif

		bcopy(tmpkeydata, zc->zc_crypto.zic_keydata, tmpkeydatalen);
		bzero(tmpkeydata, tmpkeydatalen);
		free(tmpkeydata);
		zc->zc_crypto.zic_keydatalen = tmpkeydatalen;
		zc->zc_crypto.zic_salt = salt;
		break;

	default:
		ASSERT(format);
	}

	if (zc->zc_crypto.zic_keydatalen != keylen) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
		    "key length invalid. expected %lu bytes have %lu"),
		    keylen, zc->zc_crypto.zic_keydatalen);
		errno = EIO;
		ret = -1;
	}

    if (tmpkeydatalen) // Only decrease if NOT zero.
        tmpkeydatalen--;
	while (zc->zc_crypto.zic_keydata[tmpkeydatalen] == 0 &&
	    tmpkeydatalen > 0)
		tmpkeydatalen--;

	if (tmpkeydatalen == 0) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "invalid all zeros key %lu"), tmpkeydatalen);
		errno = EIO;
		ret = -1;
	}
out:
	if (keydata) {
		bzero(keydata, keydatalen);
		free(keydata);
	}

	return (ret);
}
Esempio n. 13
0
/**
 * gnutls_pkcs11_copy_x509_crt:
 * @token_url: A PKCS #11 URL specifying a token
 * @crt: A certificate
 * @label: A name to be used for the stored data
 * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_*
 *
 * This function will copy a certificate into a PKCS #11 token specified by
 * a URL. The certificate can be marked as trusted or not.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 2.12.0
 **/
int
gnutls_pkcs11_copy_x509_crt(const char *token_url,
			    gnutls_x509_crt_t crt, const char *label,
			    unsigned int flags)
{
	int ret;
	struct p11_kit_uri *info = NULL;
	ck_rv_t rv;
	size_t der_size, id_size;
	uint8_t *der = NULL;
	uint8_t id[20];
	struct ck_attribute a[16];
	ck_object_class_t class = CKO_CERTIFICATE;
	ck_certificate_type_t type = CKC_X_509;
	ck_object_handle_t obj;
	int a_val;
	unsigned long category;
	struct pkcs11_session_info sinfo;
	
	PKCS11_CHECK_INIT;

	memset(&sinfo, 0, sizeof(sinfo));

	ret = pkcs11_url_to_info(token_url, &info);
	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	ret =
	    pkcs11_open_session(&sinfo, NULL, info,
				SESSION_WRITE |
				pkcs11_obj_flags_to_int(flags));
	p11_kit_uri_free(info);

	if (ret < 0) {
		gnutls_assert();
		return ret;
	}

	der_size = 0;
	ret =
	    gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_DER, NULL,
				   &der_size);
	if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
		gnutls_assert();
		goto cleanup;
	}

	der = gnutls_malloc(der_size);
	if (der == NULL) {
		gnutls_assert();
		ret = GNUTLS_E_MEMORY_ERROR;
		goto cleanup;
	}

	ret =
	    gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_DER, der,
				   &der_size);
	if (ret < 0) {
		gnutls_assert();
		goto cleanup;
	}

	id_size = sizeof(id);
	ret = gnutls_x509_crt_get_subject_key_id(crt, id, &id_size, NULL);
	if (ret < 0) {
	        id_size = sizeof(id);
		ret = gnutls_x509_crt_get_key_id(crt, 0, id, &id_size);
		if (ret < 0) {
		  gnutls_assert();
		  goto cleanup;
                }
	}

	/* FIXME: copy key usage flags */

	a[0].type = CKA_CLASS;
	a[0].value = &class;
	a[0].value_len = sizeof(class);
	a[1].type = CKA_ID;
	a[1].value = id;
	a[1].value_len = id_size;
	a[2].type = CKA_VALUE;
	a[2].value = der;
	a[2].value_len = der_size;
	a[3].type = CKA_TOKEN;
	a[3].value = (void *) &tval;
	a[3].value_len = sizeof(tval);
	a[4].type = CKA_CERTIFICATE_TYPE;
	a[4].value = &type;
	a[4].value_len = sizeof(type);

	a_val = 5;

	a[a_val].type = CKA_SUBJECT;
	a[a_val].value = crt->raw_dn.data;
	a[a_val].value_len = crt->raw_dn.size;
	a_val++;

	if (label) {
		a[a_val].type = CKA_LABEL;
		a[a_val].value = (void *) label;
		a[a_val].value_len = strlen(label);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_CA) {
		category = 2;
		a[a_val].type = CKA_CERTIFICATE_CATEGORY;
		a[a_val].value = (void *) &category;
		a[a_val].value_len = sizeof(category);
		a_val++;
	}

	if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED) {
		a[a_val].type = CKA_TRUSTED;
		a[a_val].value = (void *) &tval;
		a[a_val].value_len = sizeof(tval);
		a_val++;

		a[a_val].type = CKA_PRIVATE;
		a[a_val].value = (void *) &fval;
		a[a_val].value_len = sizeof(fval);
		a_val++;
	} else {
		if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE) {
			a[a_val].type = CKA_PRIVATE;
			a[a_val].value = (void *) &tval;
			a[a_val].value_len = sizeof(tval);
			a_val++;
		} else if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) {
			a[a_val].type = CKA_PRIVATE;
			a[a_val].value = (void *) &fval;
			a[a_val].value_len = sizeof(fval);
			a_val++;
		}
	}

	rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &obj);
	if (rv != CKR_OK) {
		gnutls_assert();
		_gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv));
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* generated! 
	 */

	ret = 0;

      cleanup:
	gnutls_free(der);
	pkcs11_close_session(&sinfo);
	return ret;

}
/**
 * gnutls_pkcs11_privkey_generate:
 * @url: a token URL
 * @pk: the public key algorithm
 * @bits: the security bits
 * @label: a label
 * @flags: should be zero
 *
 * This function will generate a private key in the specified
 * by the @url token. The pivate key will be generate within
 * the token and will not be exportable.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 *
 * Since: 3.0
 **/
int
gnutls_pkcs11_privkey_generate (const char* url, 
  gnutls_pk_algorithm_t pk, unsigned int bits, 
  const char* label, unsigned int flags)
{
  int ret;
  const ck_bool_t tval = 1;
  const ck_bool_t fval = 0;
  struct ck_function_list *module;
  ck_session_handle_t pks = 0;
  struct p11_kit_uri *info = NULL;
  ck_rv_t rv;
  struct ck_attribute a[10], p[10];
  ck_object_handle_t pub, priv;
  unsigned long _bits = bits;
  int a_val, p_val;
  struct ck_mechanism mech;

  ret = pkcs11_url_to_info (url, &info);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  ret =
    pkcs11_open_session (&module, &pks, info,
                         SESSION_WRITE | pkcs11_obj_flags_to_int (flags));
  p11_kit_uri_free (info);

  if (ret < 0)
    {
      gnutls_assert ();
      goto cleanup;
    }

  /* a holds the public key template
   * and p the private key */
  a_val = p_val = 0;
  mech.parameter = NULL;
  mech.parameter_len = 0;
  mech.mechanism = pk_to_genmech(pk);

  switch(pk)
    {
      case GNUTLS_PK_RSA:
        p[p_val].type = CKA_DECRYPT;
        p[p_val].value = (void*)&tval;
        p[p_val].value_len = sizeof (tval);
        p_val++;

        p[p_val].type = CKA_SIGN;
        p[p_val].value = (void*)&tval;
        p[p_val].value_len = sizeof (tval);
        p_val++;

        a[a_val].type = CKA_ENCRYPT;
        a[a_val].value = (void*)&tval;
        a[a_val].value_len = sizeof (tval);
        a_val++;

        a[a_val].type = CKA_VERIFY;
        a[a_val].value = (void*)&tval;
        a[a_val].value_len = sizeof (tval);
        a_val++;

        a[a_val].type = CKA_MODULUS_BITS;
        a[a_val].value = &_bits;
        a[a_val].value_len = sizeof (_bits);
        a_val++;
        break;
      case GNUTLS_PK_DSA:
        p[p_val].type = CKA_SIGN;
        p[p_val].value = (void*)&tval;
        p[p_val].value_len = sizeof (tval);
        p_val++;

        a[a_val].type = CKA_VERIFY;
        a[a_val].value = (void*)&tval;
        a[a_val].value_len = sizeof (tval);
        a_val++;

        a[a_val].type = CKA_MODULUS_BITS;
        a[a_val].value = &_bits;
        a[a_val].value_len = sizeof (_bits);
        a_val++;
        break;
      case GNUTLS_PK_EC:
        p[p_val].type = CKA_SIGN;
        p[p_val].value = (void*)&tval;
        p[p_val].value_len = sizeof (tval);
        p_val++;

        a[a_val].type = CKA_VERIFY;
        a[a_val].value = (void*)&tval;
        a[a_val].value_len = sizeof (tval);
        a_val++;

        a[a_val].type = CKA_MODULUS_BITS;
        a[a_val].value = &_bits;
        a[a_val].value_len = sizeof (_bits);
        a_val++;
        break;
      default:
        ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
        goto cleanup;
    }

  /* a private key is set always as private unless
   * requested otherwise
   */
  if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE)
    {
      p[p_val].type = CKA_PRIVATE;
      p[p_val].value = (void*)&fval;
      p[p_val].value_len = sizeof(fval);
      p_val++;
    }
  else
    {
      p[p_val].type = CKA_PRIVATE;
      p[p_val].value = (void*)&tval;
      p[p_val].value_len = sizeof (tval);
      p_val++;
    }

  p[p_val].type = CKA_TOKEN;
  p[p_val].value = (void *)&tval;
  p[p_val].value_len = sizeof (tval);
  p_val++;

  if (label)
    {
      p[p_val].type = CKA_LABEL;
      p[p_val].value = (void*)label;
      p[p_val].value_len = strlen (label);
      p_val++;

      a[a_val].type = CKA_LABEL;
      a[a_val].value = (void*)label;
      a[a_val].value_len = strlen (label);
      a_val++;
    }

  if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE)
    {
      p[p_val].type = CKA_SENSITIVE;
      p[p_val].value = (void*)&tval;
      p[p_val].value_len = sizeof (tval);
      p_val++;
    }
  else
    {
      p[p_val].type = CKA_SENSITIVE;
      p[p_val].value = (void*)&fval;
      p[p_val].value_len = sizeof (fval);
      p_val++;
    }

  rv = pkcs11_generate_key_pair( module, pks, &mech, a, a_val, p, p_val, &pub, &priv);
  if (rv != CKR_OK)
    {
      gnutls_assert ();
      _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv));
      ret = pkcs11_rv_to_err (rv);
      goto cleanup;
    }
    

cleanup:
  if (pks != 0)
    pkcs11_close_session (module, pks);

  return ret;
}
Esempio n. 15
0
/*
 * Lists all slots with tokens in them.
 */
int
pk_tokens(int argc, char *argv[])
{
	CK_SLOT_ID_PTR	slots = NULL;
	CK_ULONG	slot_count = 0;
	CK_TOKEN_INFO	token_info;
	const char	*fmt = NULL;
	CK_RV		rv = CKR_OK;
	int		i;

	cryptodebug("inside pk_tokens");

	/* Get rid of subcommand word "tokens". */
	argc--;
	argv++;

	/* No additional args allowed. */
	if (argc != 0)
		return (PK_ERR_USAGE);
	/* Done parsing command line options. */

	/* Get the list of slots with tokens in them. */
	if ((rv = get_token_slots(&slots, &slot_count)) != CKR_OK) {
		cryptoerror(LOG_STDERR,
		    gettext("Unable to get token slot list (%s)."),
		    pkcs11_strerror(rv));
		return (PK_ERR_PK11);
	}

	/* Make sure we have something to display. */
	if (slot_count == 0) {
		cryptoerror(LOG_STDERR, gettext("No slots with tokens found."));
		return (0);
	}

	/* Display the list. */
	fmt = "%-30.30s  %-15.15s  %-15.15s  %-10.10s\n"; /* No I18N/L10N. */
	(void) fprintf(stdout, fmt, gettext("Token Label"), gettext("Manuf ID"),
	    gettext("Serial No"), gettext("PIN State"));
	for (i = 0; i < slot_count; i++) {
		cryptodebug("calling C_GetTokenInfo");
		if ((rv = C_GetTokenInfo(slots[i], &token_info)) != CKR_OK) {
			cryptoerror(LOG_STDERR,
			    gettext("Unable to get slot %d token info (%s)."),
			    i, pkcs11_strerror(rv));
			cryptodebug("token info error, slot %d (%s)", i,
				pkcs11_strerror(rv));
			continue;
		}

		(void) fprintf(stdout, fmt, token_info.label,
		    token_info.manufacturerID, token_info.serialNumber,
		    (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ?
		    gettext("default") : gettext("user set"));
	}

	/* Clean up. */
	free(slots);
	quick_finish(NULL);
	return (0);
}