mirror of
https://github.com/openssl/openssl.git
synced 2024-11-27 05:21:51 +08:00
Add certificate callback. If set this is called whenever a certificate
is required by client or server. An application can decide which certificate chain to present based on arbitrary criteria: for example supported signature algorithms. Add very simple example to s_server. This fixes many of the problems and restrictions of the existing client certificate callback: for example you can now clear existing certificates and specify the whole chain.
This commit is contained in:
parent
0f39bab0df
commit
18d7158809
9
CHANGES
9
CHANGES
@ -4,6 +4,15 @@
|
||||
|
||||
Changes between 1.0.1 and 1.1.0 [xx XXX xxxx]
|
||||
|
||||
*) Add certificate callback. If set this is called whenever a certificate
|
||||
is required by client or server. An application can decide which
|
||||
certificate chain to present based on arbitrary criteria: for example
|
||||
supported signature algorithms. Add very simple example to s_server.
|
||||
This fixes many of the problems and restrictions of the existing client
|
||||
certificate callback: for example you can now clear an existing
|
||||
certificate and specify the whole chain.
|
||||
[Steve Henson]
|
||||
|
||||
*) Add new "valid_flags" field to CERT_PKEY structure which determines what
|
||||
the certificate can be used for (if anything). Set valid_flags field
|
||||
in new tls1_check_chain function. Simplify ssl_set_cert_masks which used
|
||||
|
@ -181,3 +181,11 @@ void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
|
||||
|
||||
int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len);
|
||||
int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int cookie_len);
|
||||
|
||||
typedef struct ssl_excert_st SSL_EXCERT;
|
||||
|
||||
void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc);
|
||||
void ssl_excert_free(SSL_EXCERT *exc);
|
||||
int args_excert(char ***pargs, int *pargc,
|
||||
int *badarg, BIO *err, SSL_EXCERT **pexc);
|
||||
int load_excert(SSL_EXCERT **pexc, BIO *err);
|
||||
|
243
apps/s_cb.c
243
apps/s_cb.c
@ -1037,3 +1037,246 @@ int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Example of extended certificate handling. Where the standard support
|
||||
* of one certificate per algorithm is not sufficient an application
|
||||
* can decide which certificate(s) to use at runtime based on whatever
|
||||
* criteria it deems appropriate.
|
||||
*/
|
||||
|
||||
/* Linked list of certificates, keys and chains */
|
||||
struct ssl_excert_st
|
||||
{
|
||||
int certform;
|
||||
const char *certfile;
|
||||
int keyform;
|
||||
const char *keyfile;
|
||||
const char *chainfile;
|
||||
X509 *cert;
|
||||
EVP_PKEY *key;
|
||||
STACK_OF(X509) *chain;
|
||||
struct ssl_excert_st *next, *prev;
|
||||
};
|
||||
|
||||
/* Very basic selection callback: just use any certificate chain
|
||||
* reported as valid. More sophisticated could prioritise according
|
||||
* to local policy.
|
||||
*/
|
||||
static int set_cert_cb(SSL *ssl, void *arg)
|
||||
{
|
||||
SSL_EXCERT *exc = arg;
|
||||
SSL_certs_clear(ssl);
|
||||
|
||||
if (!exc)
|
||||
return 1;
|
||||
|
||||
/* Go to end of list and traverse backwards since we prepend
|
||||
* newer entries this retains the original order.
|
||||
*/
|
||||
while (exc->next)
|
||||
exc = exc->next;
|
||||
|
||||
while(exc)
|
||||
{
|
||||
if (SSL_check_chain(ssl, exc->cert, exc->key, exc->chain))
|
||||
{
|
||||
SSL_use_certificate(ssl, exc->cert);
|
||||
SSL_use_PrivateKey(ssl, exc->key);
|
||||
if (exc->chain)
|
||||
SSL_set1_chain(ssl, exc->chain);
|
||||
}
|
||||
exc = exc->prev;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc)
|
||||
{
|
||||
SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc);
|
||||
}
|
||||
|
||||
static int ssl_excert_prepend(SSL_EXCERT **pexc)
|
||||
{
|
||||
SSL_EXCERT *exc;
|
||||
exc = OPENSSL_malloc(sizeof(SSL_EXCERT));
|
||||
if (!exc)
|
||||
return 0;
|
||||
exc->certfile = NULL;
|
||||
exc->keyfile = NULL;
|
||||
exc->chainfile = NULL;
|
||||
exc->cert = NULL;
|
||||
exc->key = NULL;
|
||||
exc->chain = NULL;
|
||||
exc->prev = NULL;
|
||||
|
||||
exc->next = *pexc;
|
||||
*pexc = exc;
|
||||
|
||||
if (exc->next)
|
||||
{
|
||||
exc->certform = exc->next->certform;
|
||||
exc->keyform = exc->next->keyform;
|
||||
exc->next->prev = exc;
|
||||
}
|
||||
else
|
||||
{
|
||||
exc->certform = FORMAT_PEM;
|
||||
exc->keyform = FORMAT_PEM;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void ssl_excert_free(SSL_EXCERT *exc)
|
||||
{
|
||||
SSL_EXCERT *curr;
|
||||
while (exc)
|
||||
{
|
||||
if (exc->cert)
|
||||
X509_free(exc->cert);
|
||||
if (exc->key)
|
||||
EVP_PKEY_free(exc->key);
|
||||
if (exc->chain)
|
||||
sk_X509_pop_free(exc->chain, X509_free);
|
||||
curr = exc;
|
||||
exc = exc->next;
|
||||
OPENSSL_free(curr);
|
||||
}
|
||||
}
|
||||
|
||||
int load_excert(SSL_EXCERT **pexc, BIO *err)
|
||||
{
|
||||
SSL_EXCERT *exc = *pexc;
|
||||
if (!exc)
|
||||
return 1;
|
||||
/* If nothing in list, free and set to NULL */
|
||||
if (!exc->certfile && !exc->next)
|
||||
{
|
||||
ssl_excert_free(exc);
|
||||
*pexc = NULL;
|
||||
return 1;
|
||||
}
|
||||
for(; exc; exc=exc->next)
|
||||
{
|
||||
if (!exc->certfile)
|
||||
{
|
||||
BIO_printf(err, "Missing filename\n");
|
||||
return 0;
|
||||
}
|
||||
exc->cert = load_cert(err, exc->certfile, exc->certform,
|
||||
NULL, NULL, "Server Certificate");
|
||||
if (!exc->cert)
|
||||
return 0;
|
||||
if (exc->keyfile)
|
||||
exc->keyfile = exc->certfile;
|
||||
exc->key = load_key(err, exc->certfile, exc->certform, 0,
|
||||
NULL, NULL, "Server Certificate");
|
||||
if (!exc->key)
|
||||
return 0;
|
||||
if (exc->chainfile)
|
||||
{
|
||||
exc->chain = load_certs(err,
|
||||
exc->chainfile, FORMAT_PEM,
|
||||
NULL, NULL,
|
||||
"Server Chain");
|
||||
if (!exc->chainfile)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int args_excert(char ***pargs, int *pargc,
|
||||
int *badarg, BIO *err, SSL_EXCERT **pexc)
|
||||
{
|
||||
char *arg = **pargs, *argn = (*pargs)[1];
|
||||
SSL_EXCERT *exc = *pexc;
|
||||
if (!exc && !ssl_excert_prepend(&exc))
|
||||
{
|
||||
BIO_printf(err, "Error initialising xcert\n");
|
||||
*badarg = 1;
|
||||
goto err;
|
||||
}
|
||||
if (strcmp(arg, "-xcert") == 0)
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
*badarg = 1;
|
||||
return 1;
|
||||
}
|
||||
if (exc->certfile && !ssl_excert_prepend(&exc))
|
||||
{
|
||||
BIO_printf(err, "Error adding xcert\n");
|
||||
*badarg = 1;
|
||||
goto err;
|
||||
}
|
||||
exc->certfile = argn;
|
||||
}
|
||||
else if (strcmp(arg,"-xkey") == 0)
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
*badarg = 1;
|
||||
return 1;
|
||||
}
|
||||
if (exc->keyfile)
|
||||
{
|
||||
BIO_printf(err, "Key already specified\n");
|
||||
*badarg = 1;
|
||||
return 1;
|
||||
}
|
||||
exc->keyfile = argn;
|
||||
}
|
||||
else if (strcmp(arg,"-xchain") == 0)
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
*badarg = 1;
|
||||
return 1;
|
||||
}
|
||||
if (exc->chainfile)
|
||||
{
|
||||
BIO_printf(err, "Chain already specified\n");
|
||||
*badarg = 1;
|
||||
return 1;
|
||||
}
|
||||
exc->chainfile = argn;
|
||||
}
|
||||
else if (strcmp(arg,"-xcertform") == 0)
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
*badarg = 1;
|
||||
goto err;
|
||||
}
|
||||
exc->certform = str2fmt(argn);
|
||||
}
|
||||
else if (strcmp(arg,"-xkeyform") == 0)
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
*badarg = 1;
|
||||
goto err;
|
||||
}
|
||||
exc->keyform = str2fmt(argn);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
(*pargs) += 2;
|
||||
|
||||
if (pargc)
|
||||
*pargc -= 2;
|
||||
|
||||
*pexc = exc;
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
ERR_print_errors(err);
|
||||
ssl_excert_free(exc);
|
||||
*pexc = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -991,6 +991,7 @@ int MAIN(int argc, char *argv[])
|
||||
char *srpuserseed = NULL;
|
||||
char *srp_verifier_file = NULL;
|
||||
#endif
|
||||
SSL_EXCERT *exc = NULL;
|
||||
meth=SSLv23_server_method();
|
||||
|
||||
local_argc=argc;
|
||||
@ -1143,6 +1144,12 @@ int MAIN(int argc, char *argv[])
|
||||
goto bad;
|
||||
continue;
|
||||
}
|
||||
else if (args_excert(&argv, &argc, &badarg, bio_err, &exc))
|
||||
{
|
||||
if (badarg)
|
||||
goto bad;
|
||||
continue;
|
||||
}
|
||||
else if (strcmp(*argv,"-verify_return_error") == 0)
|
||||
verify_return_error = 1;
|
||||
else if (strcmp(*argv,"-serverpref") == 0)
|
||||
@ -1456,6 +1463,9 @@ bad:
|
||||
s_key_file2 = s_cert_file2;
|
||||
#endif
|
||||
|
||||
if (!load_excert(&exc, bio_err))
|
||||
goto end;
|
||||
|
||||
if (nocert == 0)
|
||||
{
|
||||
s_key = load_key(bio_err, s_key_file, s_key_format, 0, pass, e,
|
||||
@ -1618,6 +1628,7 @@ bad:
|
||||
if (hack) SSL_CTX_set_options(ctx,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
|
||||
SSL_CTX_set_options(ctx,off);
|
||||
if (cert_flags) SSL_CTX_set_cert_flags(ctx, cert_flags);
|
||||
if (exc) ssl_ctx_set_excert(ctx, exc);
|
||||
/* DTLS: partial reads end up discarding unread UDP bytes :-(
|
||||
* Setting read ahead solves this problem.
|
||||
*/
|
||||
@ -1692,6 +1703,7 @@ bad:
|
||||
if (hack) SSL_CTX_set_options(ctx2,SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
|
||||
SSL_CTX_set_options(ctx2,off);
|
||||
if (cert_flags) SSL_CTX_set_cert_flags(ctx2, cert_flags);
|
||||
if (exc) ssl_ctx_set_excert(ctx2, exc);
|
||||
/* DTLS: partial reads end up discarding unread UDP bytes :-(
|
||||
* Setting read ahead solves this problem.
|
||||
*/
|
||||
@ -2035,6 +2047,7 @@ end:
|
||||
if (authz_in != NULL)
|
||||
BIO_free(authz_in);
|
||||
#endif
|
||||
ssl_excert_free(exc);
|
||||
if (bio_s_out != NULL)
|
||||
{
|
||||
BIO_free(bio_s_out);
|
||||
|
29
demos/certs/apps/mkxcerts.sh
Normal file
29
demos/certs/apps/mkxcerts.sh
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
# Create certificates using various algorithms to test multi-certificate
|
||||
# functionality.
|
||||
|
||||
OPENSSL=../../../apps/openssl
|
||||
CN="OpenSSL Test RSA SHA-1 cert" $OPENSSL req \
|
||||
-config apps.cnf -extensions usr_cert -x509 -nodes \
|
||||
-keyout tsha1.pem -out tsha1.pem -new -days 3650 -sha1
|
||||
CN="OpenSSL Test RSA SHA-256 cert" $OPENSSL req \
|
||||
-config apps.cnf -extensions usr_cert -x509 -nodes \
|
||||
-keyout tsha256.pem -out tsha256.pem -new -days 3650 -sha256
|
||||
CN="OpenSSL Test RSA SHA-512 cert" $OPENSSL req \
|
||||
-config apps.cnf -extensions usr_cert -x509 -nodes \
|
||||
-keyout tsha512.pem -out tsha512.pem -new -days 3650 -sha512
|
||||
|
||||
# Create EC parameters
|
||||
|
||||
$OPENSSL ecparam -name P-256 -out ecp256.pem
|
||||
$OPENSSL ecparam -name P-384 -out ecp384.pem
|
||||
|
||||
CN="OpenSSL Test P-256 SHA-256 cert" $OPENSSL req \
|
||||
-config apps.cnf -extensions usr_cert -x509 -nodes \
|
||||
-nodes -keyout tecp256.pem -out tecp256.pem -newkey ec:ecp256.pem \
|
||||
-days 3650 -sha256
|
||||
|
||||
CN="OpenSSL Test P-384 SHA-384 cert" $OPENSSL req \
|
||||
-config apps.cnf -extensions usr_cert -x509 -nodes \
|
||||
-nodes -keyout tecp384.pem -out tecp384.pem -newkey ec:ecp384.pem \
|
||||
-days 3650 -sha384
|
@ -3180,6 +3180,13 @@ int ssl3_send_client_certificate(SSL *s)
|
||||
|
||||
if (s->state == SSL3_ST_CW_CERT_A)
|
||||
{
|
||||
/* Let cert callback update client certificates if required */
|
||||
if (s->cert->cert_cb
|
||||
&& s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0)
|
||||
{
|
||||
ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
if (ssl3_check_client_certificate(s))
|
||||
s->state=SSL3_ST_CW_CERT_C;
|
||||
else
|
||||
|
@ -1357,6 +1357,14 @@ int ssl3_get_client_hello(SSL *s)
|
||||
SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_CIPHERS_PASSED);
|
||||
goto f_err;
|
||||
}
|
||||
/* Let cert callback update server certificates if required */
|
||||
if (s->cert->cert_cb
|
||||
&& s->cert->cert_cb(s, s->cert->cert_cb_arg) <= 0)
|
||||
{
|
||||
al=SSL_AD_INTERNAL_ERROR;
|
||||
SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CERT_CB_ERROR);
|
||||
goto f_err;
|
||||
}
|
||||
ciphers=NULL;
|
||||
c=ssl3_choose_cipher(s,s->session->ciphers,
|
||||
SSL_get_ciphers(s));
|
||||
|
@ -1800,6 +1800,7 @@ int (*SSL_get_verify_callback(const SSL *s))(int,X509_STORE_CTX *);
|
||||
void SSL_set_verify(SSL *s, int mode,
|
||||
int (*callback)(int ok,X509_STORE_CTX *ctx));
|
||||
void SSL_set_verify_depth(SSL *s, int depth);
|
||||
void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg);
|
||||
#ifndef OPENSSL_NO_RSA
|
||||
int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa);
|
||||
#endif
|
||||
@ -1895,6 +1896,7 @@ void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,
|
||||
int (*callback)(int, X509_STORE_CTX *));
|
||||
void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth);
|
||||
void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg);
|
||||
void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg);
|
||||
#ifndef OPENSSL_NO_RSA
|
||||
int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
|
||||
#endif
|
||||
@ -2462,6 +2464,7 @@ void ERR_load_SSL_strings(void);
|
||||
#define SSL_R_CA_DN_TOO_LONG 132
|
||||
#define SSL_R_CCS_RECEIVED_EARLY 133
|
||||
#define SSL_R_CERTIFICATE_VERIFY_FAILED 134
|
||||
#define SSL_R_CERT_CB_ERROR 377
|
||||
#define SSL_R_CERT_LENGTH_MISMATCH 135
|
||||
#define SSL_R_CHALLENGE_IS_DIFFERENT 136
|
||||
#define SSL_R_CIPHER_CODE_WRONG_LENGTH 137
|
||||
|
@ -379,6 +379,9 @@ CERT *ssl_cert_dup(CERT *cert)
|
||||
|
||||
ret->cert_flags = cert->cert_flags;
|
||||
|
||||
ret->cert_cb = cert->cert_cb;
|
||||
ret->cert_cb_arg = cert->cert_cb_arg;
|
||||
|
||||
return(ret);
|
||||
|
||||
#if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_ECDH)
|
||||
@ -557,6 +560,12 @@ int ssl_cert_add1_chain_cert(CERT *c, X509 *x)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg)
|
||||
{
|
||||
c->cert_cb = cb;
|
||||
c->cert_cb_arg = arg;
|
||||
}
|
||||
|
||||
SESS_CERT *ssl_sess_cert_new(void)
|
||||
{
|
||||
SESS_CERT *ret;
|
||||
|
@ -356,6 +356,7 @@ static ERR_STRING_DATA SSL_str_reasons[]=
|
||||
{ERR_REASON(SSL_R_CA_DN_TOO_LONG) ,"ca dn too long"},
|
||||
{ERR_REASON(SSL_R_CCS_RECEIVED_EARLY) ,"ccs received early"},
|
||||
{ERR_REASON(SSL_R_CERTIFICATE_VERIFY_FAILED),"certificate verify failed"},
|
||||
{ERR_REASON(SSL_R_CERT_CB_ERROR) ,"cert cb error"},
|
||||
{ERR_REASON(SSL_R_CERT_LENGTH_MISMATCH) ,"cert length mismatch"},
|
||||
{ERR_REASON(SSL_R_CHALLENGE_IS_DIFFERENT),"challenge is different"},
|
||||
{ERR_REASON(SSL_R_CIPHER_CODE_WRONG_LENGTH),"cipher code wrong length"},
|
||||
|
@ -2048,6 +2048,16 @@ void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth)
|
||||
X509_VERIFY_PARAM_set_depth(ctx->param, depth);
|
||||
}
|
||||
|
||||
void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), void *arg)
|
||||
{
|
||||
ssl_cert_set_cert_cb(c->cert, cb, arg);
|
||||
}
|
||||
|
||||
void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg)
|
||||
{
|
||||
ssl_cert_set_cert_cb(s->cert, cb, arg);
|
||||
}
|
||||
|
||||
void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher)
|
||||
{
|
||||
CERT_PKEY *cpk;
|
||||
|
@ -550,6 +550,16 @@ typedef struct cert_st
|
||||
TLS_SIGALGS *shared_sigalgs;
|
||||
size_t shared_sigalgslen;
|
||||
|
||||
/* Certificate setup callback: if set is called whenever a
|
||||
* certificate may be required (client or server). the callback
|
||||
* can then examine any appropriate parameters and setup any
|
||||
* certificates required. This allows advanced applications
|
||||
* to select certificates on the fly: for example based on
|
||||
* supported signature algorithms or curves.
|
||||
*/
|
||||
int (*cert_cb)(SSL *ssl, void *arg);
|
||||
void *cert_cb_arg;
|
||||
|
||||
int references; /* >1 only if SSL_copy_session_id is used */
|
||||
} CERT;
|
||||
|
||||
@ -888,6 +898,7 @@ int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain);
|
||||
int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain);
|
||||
int ssl_cert_add0_chain_cert(CERT *c, X509 *x);
|
||||
int ssl_cert_add1_chain_cert(CERT *c, X509 *x);
|
||||
void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg);
|
||||
|
||||
int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk);
|
||||
int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l);
|
||||
|
@ -3514,5 +3514,10 @@ void tls1_set_cert_validity(SSL *s)
|
||||
tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_DH_DSA);
|
||||
tls1_check_chain(s, NULL, NULL, NULL, SSL_PKEY_ECC);
|
||||
}
|
||||
/* User level utiity function to check a chain is suitable */
|
||||
int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain)
|
||||
{
|
||||
return tls1_check_chain(s, x, pk, chain, -1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -318,6 +318,8 @@ int SSL_get_shared_sigalgs(SSL *s, int idx,
|
||||
int *psign, int *phash, int *psignandhash,
|
||||
unsigned char *rsig, unsigned char *rhash);
|
||||
|
||||
int SSL_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain);
|
||||
|
||||
#define SSL_set_tlsext_host_name(s,name) \
|
||||
SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user