diff --git a/gnutls.c b/gnutls.c index 76ef5b9c..be6dc15c 100644 --- a/gnutls.c +++ b/gnutls.c @@ -1648,43 +1648,51 @@ static int get_cert_fingerprint(struct openconnect_info *vpninfo, } int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, char *buf) + void *cert, char *buf) { return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_MD5, buf); } -int openconnect_get_cert_sha1(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, char *buf) +const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo) { - return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_SHA1, buf); + if (!vpninfo->peer_cert_hash) { + char buf[41]; + + if (get_cert_fingerprint(vpninfo, vpninfo->peer_cert, + GNUTLS_DIG_SHA1, buf)) + return NULL; + + vpninfo->peer_cert_hash = strdup(buf); + } + return vpninfo->peer_cert_hash; } -char *openconnect_get_cert_details(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert) +char *openconnect_get_peer_cert_details(struct openconnect_info *vpninfo) { gnutls_datum_t buf; - if (gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &buf)) + if (gnutls_x509_crt_print(vpninfo->peer_cert, GNUTLS_CRT_PRINT_FULL, &buf)) return NULL; return (char *)buf.data; } -int openconnect_get_cert_DER(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, unsigned char **buf) +int openconnect_get_peer_cert_DER(struct openconnect_info *vpninfo, + unsigned char **buf) { size_t l = 0; unsigned char *ret = NULL; - if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l) != - GNUTLS_E_SHORT_MEMORY_BUFFER) + if (gnutls_x509_crt_export(vpninfo->peer_cert, GNUTLS_X509_FMT_DER, + ret, &l) != GNUTLS_E_SHORT_MEMORY_BUFFER) return -EIO; ret = gnutls_malloc(l); if (!ret) return -ENOMEM; - if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l)) { + if (gnutls_x509_crt_export(vpninfo->peer_cert, GNUTLS_X509_FMT_DER, + ret, &l)) { gnutls_free(ret); return -EIO; } @@ -1705,7 +1713,7 @@ static int verify_peer(gnutls_session_t session) gnutls_x509_crt_t cert; unsigned int status, cert_list_size; const char *reason = NULL; - int err; + int err = 0; if (vpninfo->peer_cert) { gnutls_x509_crt_deinit(vpninfo->peer_cert); @@ -1718,6 +1726,19 @@ static int verify_peer(gnutls_session_t session) return GNUTLS_E_CERTIFICATE_ERROR; } + err = gnutls_x509_crt_init(&cert); + if (err) { + vpn_progress(vpninfo, PRG_ERR, _("Error initialising X509 cert structure\n")); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); + if (err) { + vpn_progress(vpninfo, PRG_ERR, _("Error importing server's cert\n")); + gnutls_x509_crt_deinit(cert); + return GNUTLS_E_CERTIFICATE_ERROR; + } + if (vpninfo->servercert) { unsigned char sha1bin[SHA1_SIZE]; char fingerprint[(SHA1_SIZE * 2) + 1]; @@ -1737,7 +1758,7 @@ static int verify_peer(gnutls_session_t session) _("Server SSL certificate didn't match: %s\n"), fingerprint); return GNUTLS_E_CERTIFICATE_ERROR; } - return 0; + goto done; } err = gnutls_certificate_verify_peers2(session, &status); @@ -1764,19 +1785,6 @@ static int verify_peer(gnutls_session_t session) why we don't just set a bit for that too. */ reason = _("signature verification failed"); - err = gnutls_x509_crt_init(&cert); - if (err) { - vpn_progress(vpninfo, PRG_ERR, _("Error initialising X509 cert structure\n")); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); - if (err) { - vpn_progress(vpninfo, PRG_ERR, _("Error importing server's cert\n")); - gnutls_x509_crt_deinit(cert); - return GNUTLS_E_CERTIFICATE_ERROR; - } - if (reason) goto done; @@ -1826,20 +1834,21 @@ static int verify_peer(gnutls_session_t session) reason = _("certificate does not match hostname"); } done: + vpninfo->peer_cert = cert; + free(vpninfo->peer_cert_hash); + vpninfo->peer_cert_hash = 0; + if (reason) { vpn_progress(vpninfo, PRG_INFO, _("Server certificate verify failed: %s\n"), reason); if (vpninfo->validate_peer_cert) err = vpninfo->validate_peer_cert(vpninfo->cbdata, - cert, reason) ? GNUTLS_E_CERTIFICATE_ERROR : 0; else err = GNUTLS_E_CERTIFICATE_ERROR; } - vpninfo->peer_cert = cert; - return err; } @@ -2086,6 +2095,9 @@ void openconnect_close_https(struct openconnect_info *vpninfo, int final) gnutls_x509_crt_deinit(vpninfo->peer_cert); vpninfo->peer_cert = NULL; } + free(vpninfo->peer_cert_hash); + vpninfo->peer_cert_hash = NULL; + if (vpninfo->https_sess) { gnutls_deinit(vpninfo->https_sess); vpninfo->https_sess = NULL; diff --git a/jni.c b/jni.c index 0698f854..c315d896 100644 --- a/jni.c +++ b/jni.c @@ -32,7 +32,6 @@ struct libctx { jobject jobj; jobject async_lock; struct openconnect_info *vpninfo; - OPENCONNECT_X509 *cert; int cmd_fd; int loglevel; }; @@ -195,7 +194,7 @@ static int add_string_pair(struct libctx *ctx, jclass jcls, jobject jobj, return 0; } -static int validate_peer_cert_cb(void *privdata, OPENCONNECT_X509 *cert, const char *reason) +static int validate_peer_cert_cb(void *privdata, const char *reason) { struct libctx *ctx = privdata; jstring jreason; @@ -209,7 +208,6 @@ static int validate_peer_cert_cb(void *privdata, OPENCONNECT_X509 *cert, const c if (!jreason) goto out; - ctx->cert = cert; mid = get_obj_mid(ctx, ctx->jobj, "onValidatePeerCert", "(Ljava/lang/String;)I"); if (mid) ret = (*ctx->jenv)->CallIntMethod(ctx->jenv, ctx->jobj, mid, jreason); @@ -685,15 +683,10 @@ JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_obtainCo JNIEnv *jenv, jobject jobj) { struct libctx *ctx = getctx(jenv, jobj); - int ret; if (!ctx) return 0; - ctx->cert = NULL; - ret = openconnect_obtain_cookie(ctx->vpninfo); - if (ret == 0) - ctx->cert = openconnect_get_peer_cert(ctx->vpninfo); - return ret; + return openconnect_obtain_cookie(ctx->vpninfo); } /* special handling: caller-allocated buffer */ @@ -701,14 +694,15 @@ JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getCe JNIEnv *jenv, jobject jobj) { struct libctx *ctx = getctx(jenv, jobj); - char buf[41]; + const char *hash; jstring jresult = NULL; - if (!ctx || !ctx->cert) + if (!ctx) return NULL; - if (openconnect_get_cert_sha1(ctx->vpninfo, ctx->cert, buf)) + hash = openconnect_get_peer_cert_hash(ctx->vpninfo); + if (!hash) return NULL; - jresult = dup_to_jstring(ctx->jenv, buf); + jresult = dup_to_jstring(ctx->jenv, hash); if (!jresult) OOM(ctx->jenv); return jresult; @@ -722,9 +716,9 @@ JNIEXPORT jstring JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getCe char *buf = NULL; jstring jresult = NULL; - if (!ctx || !ctx->cert) + if (!ctx) return NULL; - buf = openconnect_get_cert_details(ctx->vpninfo, ctx->cert); + buf = openconnect_get_peer_cert_details(ctx->vpninfo); if (!buf) return NULL; @@ -745,9 +739,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_ge int ret; jbyteArray jresult = NULL; - if (!ctx || !ctx->cert) + if (!ctx) return NULL; - ret = openconnect_get_cert_DER(ctx->vpninfo, ctx->cert, &buf); + ret = openconnect_get_peer_cert_DER(ctx->vpninfo, &buf); if (ret < 0) return NULL; diff --git a/libopenconnect.map.in b/libopenconnect.map.in index b241fd8b..c6b703e4 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -1,12 +1,11 @@ -OPENCONNECT_4.0 { +OPENCONNECT_5.0 { global: openconnect_free_cert_info; openconnect_set_option_value; openconnect_clear_cookie; - openconnect_get_cert_sha1; + openconnect_get_peer_cert_hash; openconnect_get_cookie; openconnect_get_hostname; - openconnect_get_peer_cert; openconnect_get_port; openconnect_get_urlpath; openconnect_get_version; @@ -25,8 +24,8 @@ OPENCONNECT_4.0 { openconnect_vpninfo_free; openconnect_set_cert_expiry_warning; openconnect_set_cancel_fd; - openconnect_get_cert_details; - openconnect_get_cert_DER; + openconnect_get_peer_cert_details; + openconnect_get_peer_cert_DER; openconnect_init_ssl; openconnect_has_tss_blob_support; openconnect_has_pkcs11_support; @@ -54,15 +53,11 @@ OPENCONNECT_4.0 { openconnect_set_dpd; openconnect_set_proxy_auth; openconnect_set_token_callbacks; -}; - -OPENCONNECT_4.1 { - global: openconnect_set_system_trust; openconnect_get_dtls_cipher; openconnect_get_cstp_cipher; openconnect_set_csd_environ; -} OPENCONNECT_4.0; +}; OPENCONNECT_PRIVATE { diff --git a/library.c b/library.c index 24f513f3..65d0944d 100644 --- a/library.c +++ b/library.c @@ -236,6 +236,7 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo) #endif vpninfo->peer_cert = NULL; } + free(vpninfo->peer_cert_hash); free(vpninfo->localname); free(vpninfo->useragent); free(vpninfo->authgroup); @@ -394,11 +395,6 @@ int openconnect_set_client_cert(struct openconnect_info *vpninfo, return 0; } -OPENCONNECT_X509 *openconnect_get_peer_cert(struct openconnect_info *vpninfo) -{ - return vpninfo->peer_cert; -} - int openconnect_get_port(struct openconnect_info *vpninfo) { return vpninfo->port; diff --git a/main.c b/main.c index 3ba0fa16..c488c1c0 100644 --- a/main.c +++ b/main.c @@ -66,9 +66,7 @@ static int write_new_config(void *_vpninfo, const char *buf, int buflen); static void __attribute__ ((format(printf, 3, 4))) write_progress(void *_vpninfo, int level, const char *fmt, ...); -static int validate_peer_cert(void *_vpninfo, - OPENCONNECT_X509 *peer_cert, - const char *reason); +static int validate_peer_cert(void *_vpninfo, const char *reason); static int process_auth_form_cb(void *_vpninfo, struct oc_auth_form *form); static void init_token(struct openconnect_info *vpninfo, @@ -1333,11 +1331,8 @@ int main(int argc, char **argv) /* --authenticate */ printf("COOKIE='%s'\n", vpninfo->cookie); printf("HOST='%s'\n", openconnect_get_hostname(vpninfo)); - if (vpninfo->peer_cert) { - char buf[41] = {0, }; - openconnect_get_cert_sha1(vpninfo, vpninfo->peer_cert, buf); - printf("FINGERPRINT='%s'\n", buf); - } + printf("FINGERPRINT='%s'\n", + openconnect_get_peer_cert_hash(vpninfo)); openconnect_vpninfo_free(vpninfo); exit(0); } else if (cookieonly) { @@ -1531,20 +1526,16 @@ struct accepted_cert { char host[0]; } *accepted_certs; -static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert, - const char *reason) +static int validate_peer_cert(void *_vpninfo, const char *reason) { struct openconnect_info *vpninfo = _vpninfo; - char fingerprint[SHA1_SIZE * 2 + 1]; + const char *fingerprint; struct accepted_cert *this; - int ret; if (nocertcheck) return 0; - ret = openconnect_get_cert_sha1(vpninfo, peer_cert, fingerprint); - if (ret) - return ret; + fingerprint = openconnect_get_peer_cert_hash(vpninfo); for (this = accepted_certs; this; this = this->next) { if (!strcasecmp(this->host, vpninfo->hostname) && @@ -1587,9 +1578,9 @@ static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert, } free(response); - details = openconnect_get_cert_details(vpninfo, peer_cert); + details = openconnect_get_peer_cert_details(vpninfo); fputs(details, stderr); - free(details); + openconnect_free_cert_info(vpninfo, details); fprintf(stderr, _("SHA1 fingerprint: %s\n"), fingerprint); } } diff --git a/openconnect-internal.h b/openconnect-internal.h index 9bc86dc5..352b9989 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -282,7 +282,8 @@ struct openconnect_info { openconnect_unlock_token_vfn unlock_token; void *tok_cbdata; - OPENCONNECT_X509 *peer_cert; + void *peer_cert; + char *peer_cert_hash; char *cookie; /* Pointer to within cookies list */ struct oc_vpn_option *cookies; @@ -621,7 +622,7 @@ void openconnect_clear_cookies(struct openconnect_info *vpninfo); int openconnect_open_https(struct openconnect_info *vpninfo); void openconnect_close_https(struct openconnect_info *vpninfo, int final); int cstp_handshake(struct openconnect_info *vpninfo, unsigned init); -int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, OPENCONNECT_X509 *cert, +int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, void *cert, char *buf); int openconnect_sha1(unsigned char *result, void *data, int len); int openconnect_md5(unsigned char *result, void *data, int len); diff --git a/openconnect.h b/openconnect.h index df407186..c416f887 100644 --- a/openconnect.h +++ b/openconnect.h @@ -28,10 +28,15 @@ #define uid_t unsigned #endif -#define OPENCONNECT_API_VERSION_MAJOR 4 -#define OPENCONNECT_API_VERSION_MINOR 1 +#define OPENCONNECT_API_VERSION_MAJOR 5 +#define OPENCONNECT_API_VERSION_MINOR 0 /* + * API version 5.0: + * - Remove OPENCONNECT_X509 and openconnect_get_peer_cert(). + * - Change openconnect_get_cert_der() to openconnect_get_peer_cert_DER() etc. + * - Add openconnect_check_peer_cert_hash(). + * * API version 4.1: * - Add openconnect_get_cstp_cipher(), openconnect_get_dtls_cipher(), * openconnect_set_system_trust(), openconnect_set_csd_environ(). @@ -263,8 +268,6 @@ struct oc_stats { struct openconnect_info; -#define OPENCONNECT_X509 void - typedef enum { OC_TOKEN_MODE_NONE, OC_TOKEN_MODE_STOKEN, @@ -293,19 +296,20 @@ typedef enum { int openconnect_set_csd_environ(struct openconnect_info *vpninfo, const char *name, const char *value); -/* The buffer 'buf' must be at least 41 bytes. It will receive a hex string - with trailing NUL, representing the SHA1 fingerprint of the certificate. */ -int openconnect_get_cert_sha1(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, char *buf); +/* This string is static, valid only while the connection lasts. If you + * are going to cache this to remember which certs the user has accepted, + * make sure you also store the host/port for which it was accepted and + * don't just accept this cert from *anywhere*. */ +const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo); /* The buffers returned by these two functions must be freed with openconnect_free_cert_info(), especially on Windows. */ -char *openconnect_get_cert_details(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert); +char *openconnect_get_peer_cert_details(struct openconnect_info *vpninfo); + /* Returns the length of the created DER output, in a newly-allocated buffer that will need to be freed by openconnect_free_cert_info(). */ -int openconnect_get_cert_DER(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, unsigned char **buf); +int openconnect_get_peer_cert_DER(struct openconnect_info *vpninfo, + unsigned char **buf); void openconnect_free_cert_info(struct openconnect_info *vpninfo, void *buf); /* Contains a comma-separated list of authentication methods to enabled. @@ -399,12 +403,6 @@ int openconnect_get_ip_info(struct openconnect_info *, const struct oc_vpn_option **cstp_options, const struct oc_vpn_option **dtls_options); -/* This is *not* yours and must not be destroyed with X509_free(). It - will be valid when a cookie has been obtained successfully, and will - be valid until the connection is destroyed or another attempt it made - to use it. */ -OPENCONNECT_X509 *openconnect_get_peer_cert(struct openconnect_info *); - int openconnect_get_port(struct openconnect_info *); const char *openconnect_get_cookie(struct openconnect_info *); void openconnect_clear_cookie(struct openconnect_info *); @@ -480,7 +478,6 @@ int openconnect_mainloop(struct openconnect_info *vpninfo, if the certificate is (or has in the past been) explicitly accepted by the user, and non-zero to abort the connection. */ typedef int (*openconnect_validate_peer_cert_vfn) (void *privdata, - OPENCONNECT_X509 *cert, const char *reason); /* On a successful connection, the server may provide us with a new XML configuration file. This contains the list of servers that can be diff --git a/openssl.c b/openssl.c index 6a16116d..a5a792ed 100644 --- a/openssl.c +++ b/openssl.c @@ -57,14 +57,14 @@ int openconnect_md5(unsigned char *result, void *data, int len) return 0; } -int openconnect_get_cert_DER(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, unsigned char **buf) +int openconnect_get_peer_cert_DER(struct openconnect_info *vpninfo, + unsigned char **buf) { BIO *bp = BIO_new(BIO_s_mem()); BUF_MEM *certinfo; size_t l; - if (!i2d_X509_bio(bp, cert)) { + if (!i2d_X509_bio(bp, vpninfo->peer_cert)) { BIO_free(bp); return -EIO; } @@ -870,7 +870,7 @@ static int load_certificate(struct openconnect_info *vpninfo) } static int get_cert_fingerprint(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, const EVP_MD *type, + X509 *cert, const EVP_MD *type, char *buf) { unsigned char md[EVP_MAX_MD_SIZE]; @@ -886,25 +886,30 @@ static int get_cert_fingerprint(struct openconnect_info *vpninfo, } int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, char *buf) + void *cert, char *buf) { return get_cert_fingerprint(vpninfo, cert, EVP_md5(), buf); } -int openconnect_get_cert_sha1(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert, char *buf) +const char *openconnect_get_peer_cert_hash(struct openconnect_info *vpninfo) { - return get_cert_fingerprint(vpninfo, cert, EVP_sha1(), buf); + if (!vpninfo->peer_cert_hash) { + char buf[41]; + + if (get_cert_fingerprint(vpninfo, vpninfo->peer_cert, + EVP_sha1(), buf)) + return NULL; + + vpninfo->peer_cert_hash = strdup(buf); + } + return vpninfo->peer_cert_hash; } static int check_server_cert(struct openconnect_info *vpninfo, X509 *cert) { - char fingerprint[EVP_MAX_MD_SIZE * 2 + 1]; - int ret; + const char *fingerprint; - ret = openconnect_get_cert_sha1(vpninfo, cert, fingerprint); - if (ret) - return ret; + fingerprint = openconnect_get_peer_cert_hash(vpninfo); if (strcasecmp(vpninfo->servercert, fingerprint)) { vpn_progress(vpninfo, PRG_ERR, @@ -1190,22 +1195,19 @@ static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl) { - X509 *peer_cert; int ret; - peer_cert = SSL_get_peer_certificate(https_ssl); - if (vpninfo->servercert) { /* If given a cert fingerprint on the command line, that's all we look for */ - ret = check_server_cert(vpninfo, peer_cert); + ret = check_server_cert(vpninfo, vpninfo->peer_cert); } else { int vfy = SSL_get_verify_result(https_ssl); const char *err_string = NULL; if (vfy != X509_V_OK) err_string = X509_verify_cert_error_string(vfy); - else if (match_cert_hostname(vpninfo, peer_cert)) + else if (match_cert_hostname(vpninfo, vpninfo->peer_cert)) err_string = _("certificate does not match hostname"); if (err_string) { @@ -1215,7 +1217,6 @@ static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl) if (vpninfo->validate_peer_cert) ret = vpninfo->validate_peer_cert(vpninfo->cbdata, - peer_cert, err_string); else ret = -EINVAL; @@ -1223,7 +1224,6 @@ static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl) ret = 0; } } - X509_free(peer_cert); return ret; } @@ -1334,6 +1334,8 @@ int openconnect_open_https(struct openconnect_info *vpninfo) X509_free(vpninfo->peer_cert); vpninfo->peer_cert = NULL; } + free (vpninfo->peer_cert_hash); + vpninfo->peer_cert_hash = NULL; ssl_sock = connect_https_socket(vpninfo); if (ssl_sock < 0) @@ -1491,6 +1493,8 @@ int openconnect_open_https(struct openconnect_info *vpninfo) } } + vpninfo->peer_cert = SSL_get_peer_certificate(https_ssl); + if (verify_peer(vpninfo, https_ssl)) { SSL_free(https_ssl); closesocket(ssl_sock); @@ -1504,9 +1508,6 @@ int openconnect_open_https(struct openconnect_info *vpninfo) vpninfo->ssl_write = openconnect_openssl_write; vpninfo->ssl_gets = openconnect_openssl_gets; - /* Stash this now, because it might not be available later if the - server has disconnected. */ - vpninfo->peer_cert = SSL_get_peer_certificate(vpninfo->https_ssl); vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s\n"), vpninfo->hostname); @@ -1525,6 +1526,9 @@ void openconnect_close_https(struct openconnect_info *vpninfo, int final) X509_free(vpninfo->peer_cert); vpninfo->peer_cert = NULL; } + free (vpninfo->peer_cert_hash); + vpninfo->peer_cert_hash = NULL; + if (vpninfo->https_ssl) { SSL_free(vpninfo->https_ssl); vpninfo->https_ssl = NULL; @@ -1562,15 +1566,14 @@ int openconnect_init_ssl(void) return 0; } -char *openconnect_get_cert_details(struct openconnect_info *vpninfo, - OPENCONNECT_X509 *cert) +char *openconnect_get_peer_cert_details(struct openconnect_info *vpninfo) { BIO *bp = BIO_new(BIO_s_mem()); BUF_MEM *certinfo; char zero = 0; char *ret; - X509_print_ex(bp, cert, 0, 0); + X509_print_ex(bp, vpninfo->peer_cert, 0, 0); BIO_write(bp, &zero, 1); BIO_get_mem_ptr(bp, &certinfo);