static int pem_pw_cb(char *buf, int len, int w, void *v) { struct openconnect_info *vpninfo = v; char *pass = NULL; int plen; if (vpninfo->cert_password) { pass = vpninfo->cert_password; vpninfo->cert_password = NULL; } else if (request_passphrase(vpninfo, "openconnect_pem", &pass, _("Enter PEM pass phrase:"))) return -1; plen = strlen(pass); if (len <= plen) { vpn_progress(vpninfo, PRG_ERR, _("PEM password too long (%d >= %d)\n"), plen, len); free(pass); return -1; } memcpy(buf, pass, plen+1); free(pass); return plen; }
static int load_pkcs12_certificate(struct openconnect_info *vpninfo, PKCS12 *p12) { EVP_PKEY *pkey = NULL; X509 *cert = NULL; STACK_OF(X509) *ca; int ret = 0; char *pass; pass = vpninfo->cert_password; vpninfo->cert_password = NULL; retrypass: /* We do this every time round the loop, to work around a bug in OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed when PKCS12_parse() returns an error, but *ca is left pointing to the freed memory. */ ca = NULL; if (!pass && request_passphrase(vpninfo, "openconnect_pkcs12", &pass, _("Enter PKCS#12 pass phrase:")) < 0) { PKCS12_free(p12); return -EINVAL; } if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) { unsigned long err = ERR_peek_error(); openconnect_report_ssl_errors(vpninfo); if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 && ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE && ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) { vpn_progress(vpninfo, PRG_ERR, _("Parse PKCS#12 failed (wrong passphrase?)\n")); free(pass); pass = NULL; goto retrypass; } vpn_progress(vpninfo, PRG_ERR, _("Parse PKCS#12 failed (see above errors)\n")); PKCS12_free(p12); free(pass); return -EINVAL; } free(pass); if (cert) { char buf[200]; vpninfo->cert_x509 = cert; SSL_CTX_use_certificate(vpninfo->https_ctx, cert); X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); vpn_progress(vpninfo, PRG_INFO, _("Using client certificate '%s'\n"), buf); } else { vpn_progress(vpninfo, PRG_ERR, _("PKCS#12 contained no certificate!")); ret = -EINVAL; } if (pkey) { SSL_CTX_use_PrivateKey(vpninfo->https_ctx, pkey); EVP_PKEY_free(pkey); } else { vpn_progress(vpninfo, PRG_ERR, _("PKCS#12 contained no private key!")); ret = -EINVAL; } /* Only include supporting certificates which are actually necessary */ if (ca) { int i; next: for (i = 0; i < sk_X509_num(ca); i++) { X509 *cert2 = sk_X509_value(ca, i); if (X509_check_issued(cert2, cert) == X509_V_OK) { char buf[200]; if (cert2 == cert) break; if (X509_check_issued(cert2, cert2) == X509_V_OK) break; X509_NAME_oneline(X509_get_subject_name(cert2), buf, sizeof(buf)); vpn_progress(vpninfo, PRG_DEBUG, _("Extra cert from PKCS#12: '%s'\n"), buf); CRYPTO_add(&cert2->references, 1, CRYPTO_LOCK_X509); SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2); cert = cert2; goto next; } } sk_X509_pop_free(ca, X509_free); } PKCS12_free(p12); return ret; }
int load_tpm_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig) { static const TSS_UUID SRK_UUID = TSS_UUID_SRK; gnutls_datum_t asn1; unsigned int tss_len; char *pass; int ofs, err; err = gnutls_pem_base64_decode_alloc("TSS KEY BLOB", fdata, &asn1); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Error decoding TSS key blob: %s\n"), gnutls_strerror(err)); return -EINVAL; } /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */ if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */) { vpn_progress(vpninfo, PRG_ERR, _("Error in TSS key blob\n")); goto out_blob; } tss_len = asn1.data[1]; ofs = 2; if (tss_len & 0x80) { int lenlen = tss_len & 0x7f; if (asn1.size < 2 + lenlen || lenlen > 3) { vpn_progress(vpninfo, PRG_ERR, _("Error in TSS key blob\n")); goto out_blob; } tss_len = 0; while (lenlen) { tss_len <<= 8; tss_len |= asn1.data[ofs++]; lenlen--; } } if (tss_len + ofs != asn1.size) { vpn_progress(vpninfo, PRG_ERR, _("Error in TSS key blob\n")); goto out_blob; } err = Tspi_Context_Create(&vpninfo->tpm_context); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to create TPM context: %s\n"), Trspi_Error_String(err)); goto out_blob; } err = Tspi_Context_Connect(vpninfo->tpm_context, NULL); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to connect TPM context: %s\n"), Trspi_Error_String(err)); goto out_context; } err = Tspi_Context_LoadKeyByUUID(vpninfo->tpm_context, TSS_PS_TYPE_SYSTEM, SRK_UUID, &vpninfo->srk); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to load TPM SRK key: %s\n"), Trspi_Error_String(err)); goto out_context; } err = Tspi_GetPolicyObject(vpninfo->srk, TSS_POLICY_USAGE, &vpninfo->srk_policy); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to load TPM SRK policy object: %s\n"), Trspi_Error_String(err)); goto out_srk; } pass = vpninfo->cert_password; vpninfo->cert_password = NULL; while (1) { static const char nullpass[20]; /* We don't seem to get the error here... */ if (pass) err = Tspi_Policy_SetSecret(vpninfo->srk_policy, TSS_SECRET_MODE_PLAIN, strlen(pass), (BYTE *)pass); else /* Well-known NULL key */ err = Tspi_Policy_SetSecret(vpninfo->srk_policy, TSS_SECRET_MODE_SHA1, sizeof(nullpass), (BYTE *)nullpass); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to set TPM PIN: %s\n"), Trspi_Error_String(err)); goto out_srkpol; } free(pass); /* ... we get it here instead. */ err = Tspi_Context_LoadKeyByBlob(vpninfo->tpm_context, vpninfo->srk, tss_len, asn1.data + ofs, &vpninfo->tpm_key); if (!err) break; if (pass) vpn_progress(vpninfo, PRG_ERR, _("Failed to load TPM key blob: %s\n"), Trspi_Error_String(err)); if (err != TPM_E_AUTHFAIL) goto out_srkpol; err = request_passphrase(vpninfo, "openconnect_tpm_srk", &pass, _("Enter TPM SRK PIN:")); if (err) goto out_srkpol; } #ifdef HAVE_GNUTLS_CERTIFICATE_SET_KEY gnutls_privkey_init(pkey); /* This would be nicer if there was a destructor callback. I could allocate a data structure with the TPM handles and the vpninfo pointer, and destroy that properly when the key is destroyed. */ gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, vpninfo, tpm_sign_fn, NULL, 0); #else *pkey = OPENCONNECT_TPM_PKEY; #endif retry_sign: err = sign_dummy_data(vpninfo, *pkey, fdata, pkey_sig); if (err == GNUTLS_E_INSUFFICIENT_CREDENTIALS) { if (!vpninfo->tpm_key_policy) { err = Tspi_Context_CreateObject(vpninfo->tpm_context, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &vpninfo->tpm_key_policy); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to create key policy object: %s\n"), Trspi_Error_String(err)); goto out_key; } err = Tspi_Policy_AssignToObject(vpninfo->tpm_key_policy, vpninfo->tpm_key); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to assign policy to key: %s\n"), Trspi_Error_String(err)); goto out_key_policy; } } err = request_passphrase(vpninfo, "openconnect_tpm_key", &pass, _("Enter TPM key PIN:")); if (err) goto out_key_policy; err = Tspi_Policy_SetSecret(vpninfo->tpm_key_policy, TSS_SECRET_MODE_PLAIN, strlen(pass), (void *)pass); free (pass); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Failed to set key PIN: %s\n"), Trspi_Error_String(err)); goto out_key_policy; } goto retry_sign; } free (asn1.data); return 0; out_key_policy: Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key_policy); vpninfo->tpm_key_policy = 0; out_key: Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key); vpninfo->tpm_key = 0; out_srkpol: Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk_policy); vpninfo->srk_policy = 0; out_srk: Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk); vpninfo->srk = 0; out_context: Tspi_Context_Close(vpninfo->tpm_context); vpninfo->tpm_context = 0; out_blob: free (asn1.data); return -EIO; }