Trying cherrypick:
Add support for arbitrary TLS extensions. Contributed by Trevor Perrin. Conflicts: CHANGES ssl/ssl.h ssl/ssltest.c test/testssl Fix compilation due to #endif. Cherrypicking more stuff. Cleanup of custom extension stuff. serverinfo rejects non-empty extensions. Omit extension if no relevant serverinfo data. Improve error-handling in serverinfo callback. Cosmetic cleanups. s_client documentation. s_server documentation. SSL_CTX_serverinfo documentation. Cleaup -1 and NULL callback handling for custom extensions, add tests. Cleanup ssl_rsa.c serverinfo code. Whitespace cleanup. Improve comments in ssl.h for serverinfo. Whitespace. Cosmetic cleanup. Reject non-zero-len serverinfo extensions. Whitespace. Make it build. Conflicts: test/testssl
This commit is contained in:
parent
28c08222c0
commit
e27711cfdd
3
CHANGES
3
CHANGES
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
Changes between 1.0.1 and 1.0.2 [xx XXX xxxx]
|
Changes between 1.0.1 and 1.0.2 [xx XXX xxxx]
|
||||||
|
|
||||||
|
*) Add callbacks for arbitrary TLS extensions.
|
||||||
|
[Trevor Perrin <trevp@trevp.net> and Ben Laurie]
|
||||||
|
|
||||||
*) New option -crl_download in several openssl utilities to download CRLs
|
*) New option -crl_download in several openssl utilities to download CRLs
|
||||||
from CRLDP extension in certificates.
|
from CRLDP extension in certificates.
|
||||||
[Steve Henson]
|
[Steve Henson]
|
||||||
|
@ -364,6 +364,9 @@ static void sc_usage(void)
|
|||||||
# ifndef OPENSSL_NO_NEXTPROTONEG
|
# ifndef OPENSSL_NO_NEXTPROTONEG
|
||||||
BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
|
BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
|
||||||
# endif
|
# endif
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
BIO_printf(bio_err," -serverinfo types - send empty ClientHello extensions (comma-separated numbers)\n");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
|
BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
|
||||||
BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
|
BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
|
||||||
@ -541,6 +544,26 @@ static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, con
|
|||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
# endif /* ndef OPENSSL_NO_NEXTPROTONEG */
|
# endif /* ndef OPENSSL_NO_NEXTPROTONEG */
|
||||||
|
|
||||||
|
static int serverinfo_cli_cb(SSL* s, unsigned short ext_type,
|
||||||
|
const unsigned char* in, unsigned short inlen,
|
||||||
|
int* al, void* arg)
|
||||||
|
{
|
||||||
|
char pem_name[100];
|
||||||
|
unsigned char ext_buf[4 + 65536];
|
||||||
|
|
||||||
|
/* Reconstruct the type/len fields prior to extension data */
|
||||||
|
ext_buf[0] = ext_type >> 8;
|
||||||
|
ext_buf[1] = ext_type & 0xFF;
|
||||||
|
ext_buf[2] = inlen >> 8;
|
||||||
|
ext_buf[3] = inlen & 0xFF;
|
||||||
|
memcpy(ext_buf+4, in, inlen);
|
||||||
|
|
||||||
|
BIO_snprintf(pem_name, sizeof(pem_name), "SERVER_INFO %d", ext_type);
|
||||||
|
PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -613,6 +636,9 @@ int MAIN(int argc, char **argv)
|
|||||||
# ifndef OPENSSL_NO_NEXTPROTONEG
|
# ifndef OPENSSL_NO_NEXTPROTONEG
|
||||||
const char *next_proto_neg_in = NULL;
|
const char *next_proto_neg_in = NULL;
|
||||||
# endif
|
# endif
|
||||||
|
# define MAX_SI_TYPES 100
|
||||||
|
unsigned short serverinfo_types[MAX_SI_TYPES];
|
||||||
|
int serverinfo_types_count = 0;
|
||||||
#endif
|
#endif
|
||||||
char *sess_in = NULL;
|
char *sess_in = NULL;
|
||||||
char *sess_out = NULL;
|
char *sess_out = NULL;
|
||||||
@ -949,6 +975,29 @@ static char *jpake_secret = NULL;
|
|||||||
next_proto_neg_in = *(++argv);
|
next_proto_neg_in = *(++argv);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
else if (strcmp(*argv,"-serverinfo") == 0)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
int start = 0;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (--argc < 1) goto bad;
|
||||||
|
c = *(++argv);
|
||||||
|
serverinfo_types_count = 0;
|
||||||
|
len = strlen(c);
|
||||||
|
for (i = 0; i <= len; ++i)
|
||||||
|
{
|
||||||
|
if (i == len || c[i] == ',')
|
||||||
|
{
|
||||||
|
serverinfo_types[serverinfo_types_count]
|
||||||
|
= atoi(c+start);
|
||||||
|
serverinfo_types_count++;
|
||||||
|
start = i+1;
|
||||||
|
}
|
||||||
|
if (serverinfo_types_count == MAX_SI_TYPES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef FIONBIO
|
#ifdef FIONBIO
|
||||||
else if (strcmp(*argv,"-nbio") == 0)
|
else if (strcmp(*argv,"-nbio") == 0)
|
||||||
@ -1242,6 +1291,19 @@ bad:
|
|||||||
if (next_proto.data)
|
if (next_proto.data)
|
||||||
SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
|
SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
if (serverinfo_types_count)
|
||||||
|
{
|
||||||
|
for (i = 0; i < serverinfo_types_count; i++)
|
||||||
|
{
|
||||||
|
SSL_CTX_set_custom_cli_ext(ctx,
|
||||||
|
serverinfo_types[i],
|
||||||
|
NULL,
|
||||||
|
serverinfo_cli_cb,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
|
if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -311,6 +311,8 @@ static int cert_chain = 0;
|
|||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
static BIO *authz_in = NULL;
|
static BIO *authz_in = NULL;
|
||||||
static const char *s_authz_file = NULL;
|
static const char *s_authz_file = NULL;
|
||||||
|
static BIO *serverinfo_in = NULL;
|
||||||
|
static const char *s_serverinfo_file = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef OPENSSL_NO_PSK
|
#ifndef OPENSSL_NO_PSK
|
||||||
@ -471,6 +473,9 @@ static void sv_usage(void)
|
|||||||
BIO_printf(bio_err," -cert arg - certificate file to use\n");
|
BIO_printf(bio_err," -cert arg - certificate file to use\n");
|
||||||
BIO_printf(bio_err," (default is %s)\n",TEST_CERT);
|
BIO_printf(bio_err," (default is %s)\n",TEST_CERT);
|
||||||
BIO_printf(bio_err," -authz arg - binary authz file for certificate\n");
|
BIO_printf(bio_err," -authz arg - binary authz file for certificate\n");
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
BIO_printf(bio_err," -serverinfo arg - PEM serverinfo file for certificate\n");
|
||||||
|
#endif
|
||||||
BIO_printf(bio_err," -crl_check - check the peer certificate has not been revoked by its CA.\n" \
|
BIO_printf(bio_err," -crl_check - check the peer certificate has not been revoked by its CA.\n" \
|
||||||
" The CRL(s) are appended to the certificate file\n");
|
" The CRL(s) are appended to the certificate file\n");
|
||||||
BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \
|
BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \
|
||||||
@ -1065,6 +1070,11 @@ int MAIN(int argc, char *argv[])
|
|||||||
if (--argc < 1) goto bad;
|
if (--argc < 1) goto bad;
|
||||||
s_authz_file = *(++argv);
|
s_authz_file = *(++argv);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(*argv,"-serverinfo") == 0)
|
||||||
|
{
|
||||||
|
if (--argc < 1) goto bad;
|
||||||
|
s_serverinfo_file = *(++argv);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (strcmp(*argv,"-certform") == 0)
|
else if (strcmp(*argv,"-certform") == 0)
|
||||||
{
|
{
|
||||||
@ -1796,6 +1806,9 @@ bad:
|
|||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file))
|
if (s_authz_file != NULL && !SSL_CTX_use_authz_file(ctx, s_authz_file))
|
||||||
goto end;
|
goto end;
|
||||||
|
if (s_serverinfo_file != NULL
|
||||||
|
&& !SSL_CTX_use_serverinfo_file(ctx, s_serverinfo_file))
|
||||||
|
goto end;
|
||||||
#endif
|
#endif
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain))
|
if (ctx2 && !set_cert_key_stuff(ctx2,s_cert2,s_key2, NULL, build_chain))
|
||||||
@ -1963,6 +1976,8 @@ end:
|
|||||||
EVP_PKEY_free(s_key2);
|
EVP_PKEY_free(s_key2);
|
||||||
if (authz_in != NULL)
|
if (authz_in != NULL)
|
||||||
BIO_free(authz_in);
|
BIO_free(authz_in);
|
||||||
|
if (serverinfo_in != NULL)
|
||||||
|
BIO_free(serverinfo_in);
|
||||||
#endif
|
#endif
|
||||||
ssl_excert_free(exc);
|
ssl_excert_free(exc);
|
||||||
if (ssl_args)
|
if (ssl_args)
|
||||||
|
@ -43,6 +43,7 @@ B<openssl> B<s_client>
|
|||||||
[B<-sess_out filename>]
|
[B<-sess_out filename>]
|
||||||
[B<-sess_in filename>]
|
[B<-sess_in filename>]
|
||||||
[B<-rand file(s)>]
|
[B<-rand file(s)>]
|
||||||
|
[B<-serverinfo types>]
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
@ -237,6 +238,13 @@ Multiple files can be specified separated by a OS-dependent character.
|
|||||||
The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for
|
The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for
|
||||||
all others.
|
all others.
|
||||||
|
|
||||||
|
=item B<-serverinfo types>
|
||||||
|
|
||||||
|
a list of comma-separated TLS Extension Types (numbers between 0 and
|
||||||
|
65535). Each type will be sent as an empty ClientHello TLS Extension.
|
||||||
|
The server's response (if any) will be encoded and displayed as a PEM
|
||||||
|
file.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 CONNECTED COMMANDS
|
=head1 CONNECTED COMMANDS
|
||||||
|
@ -54,6 +54,7 @@ B<openssl> B<s_server>
|
|||||||
[B<-no_ticket>]
|
[B<-no_ticket>]
|
||||||
[B<-id_prefix arg>]
|
[B<-id_prefix arg>]
|
||||||
[B<-rand file(s)>]
|
[B<-rand file(s)>]
|
||||||
|
[B<-serverinfo file>]
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
@ -276,6 +277,14 @@ Multiple files can be specified separated by a OS-dependent character.
|
|||||||
The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for
|
The separator is B<;> for MS-Windows, B<,> for OpenVMS, and B<:> for
|
||||||
all others.
|
all others.
|
||||||
|
|
||||||
|
=item B<-serverinfo file>
|
||||||
|
|
||||||
|
a file containing one or more blocks of PEM data. Each PEM block
|
||||||
|
must encode a TLS ServerHello extension (2 bytes type, 2 bytes length,
|
||||||
|
followed by "length" bytes of extension data). If the client sends
|
||||||
|
an empty TLS ClientHello extension matching the type, the corresponding
|
||||||
|
ServerHello extension will be returned.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 CONNECTED COMMANDS
|
=head1 CONNECTED COMMANDS
|
||||||
|
45
doc/ssl/SSL_CTX_use_serverinfo.pod
Normal file
45
doc/ssl/SSL_CTX_use_serverinfo.pod
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
SSL_CTX_use_serverinfo, SSL_CTX_use_serverinfo_file
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
|
||||||
|
size_t serverinfo_length);
|
||||||
|
|
||||||
|
int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
These functions load "serverinfo" TLS ServerHello Extensions into the SSL_CTX.
|
||||||
|
A "serverinfo" extension is returned in response to an empty ClientHello
|
||||||
|
Extension.
|
||||||
|
|
||||||
|
SSL_CTX_use_serverinfo_file() loads one or more serverinfo extensions from
|
||||||
|
a byte array into B<ctx>. The extensions must be concatenated into a
|
||||||
|
sequence of bytes. Each extension must consist of a 2-byte Extension Type,
|
||||||
|
a 2-byte length, and then length bytes of extension_data.
|
||||||
|
|
||||||
|
SSL_CTX_use_serverinfo_file() loads one or more serverinfo extensions from
|
||||||
|
B<file> into B<ctx>. The extensions must be in PEM format. Each extension
|
||||||
|
must consist of a 2-byte Extension Type, a 2-byte length, and then length
|
||||||
|
bytes of extension_data.
|
||||||
|
|
||||||
|
=head1 NOTES
|
||||||
|
|
||||||
|
=head1 RETURN VALUES
|
||||||
|
|
||||||
|
On success, the functions return 1.
|
||||||
|
On failure, the functions return 0. Check out the error stack to find out
|
||||||
|
the reason.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
=head1 HISTORY
|
||||||
|
|
||||||
|
|
||||||
|
=cut
|
@ -3008,6 +3008,8 @@ void ssl3_free(SSL *s)
|
|||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
if (s->s3->tlsext_authz_client_types != NULL)
|
if (s->s3->tlsext_authz_client_types != NULL)
|
||||||
OPENSSL_free(s->s3->tlsext_authz_client_types);
|
OPENSSL_free(s->s3->tlsext_authz_client_types);
|
||||||
|
if (s->s3->tlsext_custom_types != NULL)
|
||||||
|
OPENSSL_free(s->s3->tlsext_custom_types);
|
||||||
#endif
|
#endif
|
||||||
OPENSSL_cleanse(s->s3,sizeof *s->s3);
|
OPENSSL_cleanse(s->s3,sizeof *s->s3);
|
||||||
OPENSSL_free(s->s3);
|
OPENSSL_free(s->s3);
|
||||||
@ -3058,6 +3060,12 @@ void ssl3_clear(SSL *s)
|
|||||||
OPENSSL_free(s->s3->tlsext_authz_client_types);
|
OPENSSL_free(s->s3->tlsext_authz_client_types);
|
||||||
s->s3->tlsext_authz_client_types = NULL;
|
s->s3->tlsext_authz_client_types = NULL;
|
||||||
}
|
}
|
||||||
|
if (s->s3->tlsext_custom_types != NULL)
|
||||||
|
{
|
||||||
|
OPENSSL_free(s->s3->tlsext_custom_types);
|
||||||
|
s->s3->tlsext_custom_types = NULL;
|
||||||
|
}
|
||||||
|
s->s3->tlsext_custom_types_count = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rp = s->s3->rbuf.buf;
|
rp = s->s3->rbuf.buf;
|
||||||
|
94
ssl/ssl.h
94
ssl/ssl.h
@ -383,6 +383,57 @@ DECLARE_STACK_OF(SRTP_PROTECTION_PROFILE)
|
|||||||
typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
|
typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
|
||||||
typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
|
typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
/* Callbacks and structures for handling custom TLS Extensions:
|
||||||
|
* cli_ext_first_cb - sends data for ClientHello TLS Extension
|
||||||
|
* cli_ext_second_cb - receives data from ServerHello TLS Extension
|
||||||
|
* srv_ext_first_cb - receives data from ClientHello TLS Extension
|
||||||
|
* srv_ext_second_cb - sends data for ServerHello TLS Extension
|
||||||
|
*
|
||||||
|
* All these functions return nonzero on success. Zero will terminate
|
||||||
|
* the handshake (and return a specific TLS Fatal alert, if the function
|
||||||
|
* declaration has an "al" parameter). -1 for the "sending" functions
|
||||||
|
* will cause the TLS Extension to be omitted.
|
||||||
|
*
|
||||||
|
* "ext_type" is a TLS "ExtensionType" from 0-65535.
|
||||||
|
* "in" is a pointer to TLS "extension_data" being provided to the cb.
|
||||||
|
* "out" is used by the callback to return a pointer to "extension data"
|
||||||
|
* which OpenSSL will later copy into the TLS handshake. The contents
|
||||||
|
* of this buffer should not be changed until the handshake is complete.
|
||||||
|
* "inlen" and "outlen" are TLS Extension lengths from 0-65535.
|
||||||
|
* "al" is a TLS "AlertDescription" from 0-255 which WILL be sent as a
|
||||||
|
* fatal TLS alert, if the callback returns zero.
|
||||||
|
*/
|
||||||
|
typedef int (*custom_cli_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg);
|
||||||
|
typedef int (*custom_cli_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg);
|
||||||
|
|
||||||
|
typedef int (*custom_srv_ext_first_cb_fn)(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg);
|
||||||
|
typedef int (*custom_srv_ext_second_cb_fn)(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned short ext_type;
|
||||||
|
custom_cli_ext_first_cb_fn fn1;
|
||||||
|
custom_cli_ext_second_cb_fn fn2;
|
||||||
|
void *arg;
|
||||||
|
} custom_cli_ext_record;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned short ext_type;
|
||||||
|
custom_srv_ext_first_cb_fn fn1;
|
||||||
|
custom_srv_ext_second_cb_fn fn2;
|
||||||
|
void *arg;
|
||||||
|
} custom_srv_ext_record;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef OPENSSL_NO_SSL_INTERN
|
#ifndef OPENSSL_NO_SSL_INTERN
|
||||||
|
|
||||||
@ -1058,6 +1109,12 @@ struct ssl_ctx_st
|
|||||||
int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg);
|
int (*tlsext_authz_server_audit_proof_cb)(SSL *s, void *arg);
|
||||||
void *tlsext_authz_server_audit_proof_cb_arg;
|
void *tlsext_authz_server_audit_proof_cb_arg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Arrays containing the callbacks for custom TLS Extensions. */
|
||||||
|
custom_cli_ext_record *custom_cli_ext_records;
|
||||||
|
size_t custom_cli_ext_records_count;
|
||||||
|
custom_srv_ext_record *custom_srv_ext_records;
|
||||||
|
size_t custom_srv_ext_records_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1163,6 +1220,32 @@ const char *SSL_get_psk_identity_hint(const SSL *s);
|
|||||||
const char *SSL_get_psk_identity(const SSL *s);
|
const char *SSL_get_psk_identity(const SSL *s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
/* Register callbacks to handle custom TLS Extensions as client or server.
|
||||||
|
*
|
||||||
|
* Returns nonzero on success. You cannot register twice for the same
|
||||||
|
* extension number, and registering for an extension number already
|
||||||
|
* handled by OpenSSL will succeed, but the callbacks will not be invoked.
|
||||||
|
*
|
||||||
|
* NULL can be registered for any callback function. For the client
|
||||||
|
* functions, a NULL custom_cli_ext_first_cb_fn sends an empty ClientHello
|
||||||
|
* Extension, and a NULL custom_cli_ext_second_cb_fn ignores the ServerHello
|
||||||
|
* response (if any).
|
||||||
|
*
|
||||||
|
* For the server functions, a NULL custom_srv_ext_first_cb_fn means the
|
||||||
|
* ClientHello extension's data will be ignored, but the extension will still
|
||||||
|
* be noted and custom_srv_ext_second_cb_fn will still be invoked. A NULL
|
||||||
|
* custom_srv_ext_second_cb doesn't send a ServerHello extension.
|
||||||
|
*/
|
||||||
|
int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
|
||||||
|
custom_cli_ext_first_cb_fn fn1,
|
||||||
|
custom_cli_ext_second_cb_fn fn2, void *arg);
|
||||||
|
|
||||||
|
int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
|
||||||
|
custom_srv_ext_first_cb_fn fn1,
|
||||||
|
custom_srv_ext_second_cb_fn fn2, void *arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SSL_NOTHING 1
|
#define SSL_NOTHING 1
|
||||||
#define SSL_WRITING 2
|
#define SSL_WRITING 2
|
||||||
#define SSL_READING 3
|
#define SSL_READING 3
|
||||||
@ -1934,6 +2017,14 @@ const unsigned char *SSL_CTX_get_authz_data(SSL_CTX *ctx, unsigned char type,
|
|||||||
int SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file);
|
int SSL_CTX_use_authz_file(SSL_CTX *ctx, const char *file);
|
||||||
int SSL_use_authz_file(SSL *ssl, const char *file);
|
int SSL_use_authz_file(SSL *ssl, const char *file);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Set serverinfo data for the current active cert. */
|
||||||
|
int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
|
||||||
|
size_t serverinfo_length);
|
||||||
|
#ifndef OPENSSL_NO_STDIO
|
||||||
|
int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file);
|
||||||
|
#endif /* NO_STDIO */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef OPENSSL_NO_STDIO
|
#ifndef OPENSSL_NO_STDIO
|
||||||
@ -2469,6 +2560,8 @@ void ERR_load_SSL_strings(void);
|
|||||||
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY 177
|
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY 177
|
||||||
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1 178
|
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1 178
|
||||||
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE 179
|
#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE 179
|
||||||
|
#define SSL_F_SSL_CTX_USE_SERVERINFO 336
|
||||||
|
#define SSL_F_SSL_CTX_USE_SERVERINFO_FILE 337
|
||||||
#define SSL_F_SSL_DO_HANDSHAKE 180
|
#define SSL_F_SSL_DO_HANDSHAKE 180
|
||||||
#define SSL_F_SSL_GET_NEW_SESSION 181
|
#define SSL_F_SSL_GET_NEW_SESSION 181
|
||||||
#define SSL_F_SSL_GET_PREV_SESSION 217
|
#define SSL_F_SSL_GET_PREV_SESSION 217
|
||||||
@ -2644,6 +2737,7 @@ void ERR_load_SSL_strings(void);
|
|||||||
#define SSL_R_INVALID_COMPRESSION_ALGORITHM 341
|
#define SSL_R_INVALID_COMPRESSION_ALGORITHM 341
|
||||||
#define SSL_R_INVALID_NULL_CMD_NAME 385
|
#define SSL_R_INVALID_NULL_CMD_NAME 385
|
||||||
#define SSL_R_INVALID_PURPOSE 278
|
#define SSL_R_INVALID_PURPOSE 278
|
||||||
|
#define SSL_R_INVALID_SERVERINFO_DATA 388
|
||||||
#define SSL_R_INVALID_SRP_USERNAME 357
|
#define SSL_R_INVALID_SRP_USERNAME 357
|
||||||
#define SSL_R_INVALID_STATUS_RESPONSE 328
|
#define SSL_R_INVALID_STATUS_RESPONSE 328
|
||||||
#define SSL_R_INVALID_TICKET_KEYS_LENGTH 325
|
#define SSL_R_INVALID_TICKET_KEYS_LENGTH 325
|
||||||
|
@ -571,6 +571,15 @@ typedef struct ssl3_state_st
|
|||||||
* server echoed our server_authz extension and therefore must send us
|
* server echoed our server_authz extension and therefore must send us
|
||||||
* a supplemental data handshake message. */
|
* a supplemental data handshake message. */
|
||||||
char tlsext_authz_server_promised;
|
char tlsext_authz_server_promised;
|
||||||
|
|
||||||
|
/* tlsext_custom_types contains an array of TLS Extension types which
|
||||||
|
* were advertised by the client in its ClientHello, which were not
|
||||||
|
* otherwise handled by OpenSSL, and which the server has registered
|
||||||
|
* a custom_srv_ext_record to handle.
|
||||||
|
* The array does not contain any duplicates, and is in the same order
|
||||||
|
* as the types were received in the client hello. */
|
||||||
|
unsigned short *tlsext_custom_types;
|
||||||
|
size_t tlsext_custom_types_count; /* how many tlsext_custom_types */
|
||||||
#endif
|
#endif
|
||||||
} SSL3_STATE;
|
} SSL3_STATE;
|
||||||
|
|
||||||
|
@ -329,6 +329,7 @@ CERT *ssl_cert_dup(CERT *cert)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rpk->valid_flags = 0;
|
rpk->valid_flags = 0;
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
if (cert->pkeys[i].authz != NULL)
|
if (cert->pkeys[i].authz != NULL)
|
||||||
{
|
{
|
||||||
/* Just copy everything. */
|
/* Just copy everything. */
|
||||||
@ -339,12 +340,30 @@ CERT *ssl_cert_dup(CERT *cert)
|
|||||||
if (ret->pkeys[i].authz == NULL)
|
if (ret->pkeys[i].authz == NULL)
|
||||||
{
|
{
|
||||||
SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
|
SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
|
||||||
return(NULL);
|
return NULL;
|
||||||
}
|
}
|
||||||
memcpy(ret->pkeys[i].authz,
|
memcpy(ret->pkeys[i].authz,
|
||||||
cert->pkeys[i].authz,
|
cert->pkeys[i].authz,
|
||||||
cert->pkeys[i].authz_length);
|
cert->pkeys[i].authz_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cert->pkeys[i].serverinfo != NULL)
|
||||||
|
{
|
||||||
|
/* Just copy everything. */
|
||||||
|
ret->pkeys[i].serverinfo_length =
|
||||||
|
cert->pkeys[i].serverinfo_length;
|
||||||
|
ret->pkeys[i].serverinfo =
|
||||||
|
OPENSSL_malloc(ret->pkeys[i].serverinfo_length);
|
||||||
|
if (ret->pkeys[i].serverinfo == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CERT_DUP, ERR_R_MALLOC_FAILURE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(ret->pkeys[i].serverinfo,
|
||||||
|
cert->pkeys[i].serverinfo,
|
||||||
|
cert->pkeys[i].serverinfo_length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->references=1;
|
ret->references=1;
|
||||||
@ -460,8 +479,16 @@ void ssl_cert_clear_certs(CERT *c)
|
|||||||
cpk->chain = NULL;
|
cpk->chain = NULL;
|
||||||
}
|
}
|
||||||
#ifndef OPENSSL_NO_TLSEXT
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
if (cpk->authz != NULL)
|
if (cpk->authz)
|
||||||
|
{
|
||||||
OPENSSL_free(cpk->authz);
|
OPENSSL_free(cpk->authz);
|
||||||
|
cpk->authz = NULL;
|
||||||
|
}
|
||||||
|
if (cpk->serverinfo)
|
||||||
|
{
|
||||||
|
OPENSSL_free(cpk->serverinfo);
|
||||||
|
cpk->serverinfo = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Clear all flags apart from explicit sign */
|
/* Clear all flags apart from explicit sign */
|
||||||
cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN;
|
cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN;
|
||||||
|
@ -233,6 +233,8 @@ static ERR_STRING_DATA SSL_str_functs[]=
|
|||||||
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY), "SSL_CTX_use_RSAPrivateKey"},
|
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY), "SSL_CTX_use_RSAPrivateKey"},
|
||||||
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1), "SSL_CTX_use_RSAPrivateKey_ASN1"},
|
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1), "SSL_CTX_use_RSAPrivateKey_ASN1"},
|
||||||
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE), "SSL_CTX_use_RSAPrivateKey_file"},
|
{ERR_FUNC(SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE), "SSL_CTX_use_RSAPrivateKey_file"},
|
||||||
|
{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO), "SSL_CTX_use_serverinfo"},
|
||||||
|
{ERR_FUNC(SSL_F_SSL_CTX_USE_SERVERINFO_FILE), "SSL_CTX_use_serverinfo_file"},
|
||||||
{ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE), "SSL_do_handshake"},
|
{ERR_FUNC(SSL_F_SSL_DO_HANDSHAKE), "SSL_do_handshake"},
|
||||||
{ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION), "ssl_get_new_session"},
|
{ERR_FUNC(SSL_F_SSL_GET_NEW_SESSION), "ssl_get_new_session"},
|
||||||
{ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"},
|
{ERR_FUNC(SSL_F_SSL_GET_PREV_SESSION), "ssl_get_prev_session"},
|
||||||
@ -411,6 +413,7 @@ static ERR_STRING_DATA SSL_str_reasons[]=
|
|||||||
{ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"},
|
{ERR_REASON(SSL_R_INVALID_COMPRESSION_ALGORITHM),"invalid compression algorithm"},
|
||||||
{ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"},
|
{ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME) ,"invalid null cmd name"},
|
||||||
{ERR_REASON(SSL_R_INVALID_PURPOSE) ,"invalid purpose"},
|
{ERR_REASON(SSL_R_INVALID_PURPOSE) ,"invalid purpose"},
|
||||||
|
{ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA),"invalid serverinfo data"},
|
||||||
{ERR_REASON(SSL_R_INVALID_SRP_USERNAME) ,"invalid srp username"},
|
{ERR_REASON(SSL_R_INVALID_SRP_USERNAME) ,"invalid srp username"},
|
||||||
{ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"},
|
{ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"},
|
||||||
{ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"},
|
{ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"},
|
||||||
|
@ -1710,6 +1710,61 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned
|
|||||||
ctx->next_proto_select_cb_arg = arg;
|
ctx->next_proto_select_cb_arg = arg;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
int SSL_CTX_set_custom_cli_ext(SSL_CTX *ctx, unsigned short ext_type,
|
||||||
|
custom_cli_ext_first_cb_fn fn1,
|
||||||
|
custom_cli_ext_second_cb_fn fn2, void* arg)
|
||||||
|
{
|
||||||
|
/* Check for duplicates */
|
||||||
|
size_t i;
|
||||||
|
custom_cli_ext_record* record;
|
||||||
|
|
||||||
|
for (i=0; i < ctx->custom_cli_ext_records_count; i++)
|
||||||
|
if (ext_type == ctx->custom_cli_ext_records[i].ext_type)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ctx->custom_cli_ext_records = OPENSSL_realloc(ctx->custom_cli_ext_records,
|
||||||
|
(ctx->custom_cli_ext_records_count+1) * sizeof(custom_cli_ext_record));
|
||||||
|
if (!ctx->custom_cli_ext_records) {
|
||||||
|
ctx->custom_cli_ext_records_count = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ctx->custom_cli_ext_records_count++;
|
||||||
|
record = &ctx->custom_cli_ext_records[ctx->custom_cli_ext_records_count - 1];
|
||||||
|
record->ext_type = ext_type;
|
||||||
|
record->fn1 = fn1;
|
||||||
|
record->fn2 = fn2;
|
||||||
|
record->arg = arg;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSL_CTX_set_custom_srv_ext(SSL_CTX *ctx, unsigned short ext_type,
|
||||||
|
custom_srv_ext_first_cb_fn fn1,
|
||||||
|
custom_srv_ext_second_cb_fn fn2, void* arg)
|
||||||
|
{
|
||||||
|
/* Check for duplicates */
|
||||||
|
size_t i;
|
||||||
|
custom_srv_ext_record* record;
|
||||||
|
|
||||||
|
for (i=0; i < ctx->custom_srv_ext_records_count; i++)
|
||||||
|
if (ext_type == ctx->custom_srv_ext_records[i].ext_type)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ctx->custom_srv_ext_records = OPENSSL_realloc(ctx->custom_srv_ext_records,
|
||||||
|
(ctx->custom_srv_ext_records_count+1) * sizeof(custom_srv_ext_record));
|
||||||
|
if (!ctx->custom_srv_ext_records) {
|
||||||
|
ctx->custom_srv_ext_records_count = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ctx->custom_srv_ext_records_count++;
|
||||||
|
record = &ctx->custom_srv_ext_records[ctx->custom_srv_ext_records_count - 1];
|
||||||
|
record->ext_type = ext_type;
|
||||||
|
record->fn1 = fn1;
|
||||||
|
record->fn2 = fn2;
|
||||||
|
record->arg = arg;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
||||||
@ -1909,6 +1964,10 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
|
|||||||
#ifndef OPENSSL_NO_SRP
|
#ifndef OPENSSL_NO_SRP
|
||||||
SSL_CTX_SRP_CTX_init(ret);
|
SSL_CTX_SRP_CTX_init(ret);
|
||||||
#endif
|
#endif
|
||||||
|
ret->custom_cli_ext_records = NULL;
|
||||||
|
ret->custom_cli_ext_records_count = 0;
|
||||||
|
ret->custom_srv_ext_records = NULL;
|
||||||
|
ret->custom_srv_ext_records_count = 0;
|
||||||
#ifndef OPENSSL_NO_BUF_FREELISTS
|
#ifndef OPENSSL_NO_BUF_FREELISTS
|
||||||
ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
|
ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
|
||||||
ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
|
ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
|
||||||
@ -2047,6 +2106,10 @@ void SSL_CTX_free(SSL_CTX *a)
|
|||||||
#ifndef OPENSSL_NO_SRP
|
#ifndef OPENSSL_NO_SRP
|
||||||
SSL_CTX_SRP_CTX_free(a);
|
SSL_CTX_SRP_CTX_free(a);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
|
OPENSSL_free(a->custom_cli_ext_records);
|
||||||
|
OPENSSL_free(a->custom_srv_ext_records);
|
||||||
|
#endif
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
if (a->client_cert_engine)
|
if (a->client_cert_engine)
|
||||||
ENGINE_finish(a->client_cert_engine);
|
ENGINE_finish(a->client_cert_engine);
|
||||||
@ -2492,6 +2555,26 @@ unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length)
|
|||||||
|
|
||||||
return c->pkeys[i].authz;
|
return c->pkeys[i].authz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
|
||||||
|
size_t *serverinfo_length)
|
||||||
|
{
|
||||||
|
CERT *c = NULL;
|
||||||
|
int i = 0;
|
||||||
|
*serverinfo_length = 0;
|
||||||
|
|
||||||
|
c = s->cert;
|
||||||
|
i = ssl_get_server_cert_index(s);
|
||||||
|
|
||||||
|
if (i == -1)
|
||||||
|
return 0;
|
||||||
|
if (c->pkeys[i].serverinfo == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*serverinfo = c->pkeys[i].serverinfo;
|
||||||
|
*serverinfo_length = c->pkeys[i].serverinfo_length;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ssl_update_cache(SSL *s,int mode)
|
void ssl_update_cache(SSL *s,int mode)
|
||||||
|
@ -493,6 +493,14 @@ typedef struct cert_pkey_st
|
|||||||
* uint8_t data[length]; */
|
* uint8_t data[length]; */
|
||||||
unsigned char *authz;
|
unsigned char *authz;
|
||||||
size_t authz_length;
|
size_t authz_length;
|
||||||
|
|
||||||
|
/* serverinfo data for this certificate. The data is in TLS Extension
|
||||||
|
* wire format, specifically it's a series of records like:
|
||||||
|
* uint16_t extension_type; // (RFC 5246, 7.4.1.4, Extension)
|
||||||
|
* uint16_t length;
|
||||||
|
* uint8_t data[length]; */
|
||||||
|
unsigned char *serverinfo;
|
||||||
|
size_t serverinfo_length;
|
||||||
#endif
|
#endif
|
||||||
/* Set if CERT_PKEY can be used with current SSL session: e.g.
|
/* Set if CERT_PKEY can be used with current SSL session: e.g.
|
||||||
* appropriate curve, signature algorithms etc. If zero it can't be
|
* appropriate curve, signature algorithms etc. If zero it can't be
|
||||||
@ -949,7 +957,11 @@ int ssl_undefined_function(SSL *s);
|
|||||||
int ssl_undefined_void_function(void);
|
int ssl_undefined_void_function(void);
|
||||||
int ssl_undefined_const_function(const SSL *s);
|
int ssl_undefined_const_function(const SSL *s);
|
||||||
CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
|
CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
|
||||||
|
#ifndef OPENSSL_NO_TLSEXT
|
||||||
unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length);
|
unsigned char *ssl_get_authz_data(SSL *s, size_t *authz_length);
|
||||||
|
int ssl_get_server_cert_serverinfo(SSL *s, const unsigned char **serverinfo,
|
||||||
|
size_t *serverinfo_length);
|
||||||
|
#endif
|
||||||
EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd);
|
EVP_PKEY *ssl_get_sign_pkey(SSL *s,const SSL_CIPHER *c, const EVP_MD **pmd);
|
||||||
int ssl_cert_type(X509 *x,EVP_PKEY *pkey);
|
int ssl_cert_type(X509 *x,EVP_PKEY *pkey);
|
||||||
void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher);
|
void ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher);
|
||||||
|
264
ssl/ssl_rsa.c
264
ssl/ssl_rsa.c
@ -471,6 +471,14 @@ static int ssl_set_cert(CERT *c, X509 *x)
|
|||||||
c->pkeys[i].authz = NULL;
|
c->pkeys[i].authz = NULL;
|
||||||
c->pkeys[i].authz_length = 0;
|
c->pkeys[i].authz_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free the old serverinfo data, if it exists. */
|
||||||
|
if (c->pkeys[i].serverinfo != NULL)
|
||||||
|
{
|
||||||
|
OPENSSL_free(c->pkeys[i].serverinfo);
|
||||||
|
c->pkeys[i].serverinfo = NULL;
|
||||||
|
c->pkeys[i].serverinfo_length = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
c->key= &(c->pkeys[i]);
|
c->key= &(c->pkeys[i]);
|
||||||
|
|
||||||
@ -855,6 +863,138 @@ static char authz_validate(const unsigned char *authz, size_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int serverinfo_find_extension(const unsigned char *serverinfo,
|
||||||
|
size_t serverinfo_length,
|
||||||
|
unsigned short extension_type,
|
||||||
|
const unsigned char **extension_data,
|
||||||
|
unsigned short *extension_length)
|
||||||
|
{
|
||||||
|
*extension_data = NULL;
|
||||||
|
*extension_length = 0;
|
||||||
|
if (serverinfo == NULL || serverinfo_length == 0)
|
||||||
|
return 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned short type = 0; /* uint16 */
|
||||||
|
unsigned short len = 0; /* uint16 */
|
||||||
|
|
||||||
|
/* end of serverinfo */
|
||||||
|
if (serverinfo_length == 0)
|
||||||
|
return -1; /* Extension not found */
|
||||||
|
|
||||||
|
/* read 2-byte type field */
|
||||||
|
if (serverinfo_length < 2)
|
||||||
|
return 0; /* Error */
|
||||||
|
type = (serverinfo[0] << 8) + serverinfo[1];
|
||||||
|
serverinfo += 2;
|
||||||
|
serverinfo_length -= 2;
|
||||||
|
|
||||||
|
/* read 2-byte len field */
|
||||||
|
if (serverinfo_length < 2)
|
||||||
|
return 0; /* Error */
|
||||||
|
len = (serverinfo[0] << 8) + serverinfo[1];
|
||||||
|
serverinfo += 2;
|
||||||
|
serverinfo_length -= 2;
|
||||||
|
|
||||||
|
if (len > serverinfo_length)
|
||||||
|
return 0; /* Error */
|
||||||
|
|
||||||
|
if (type == extension_type)
|
||||||
|
{
|
||||||
|
*extension_data = serverinfo;
|
||||||
|
*extension_length = len;
|
||||||
|
return 1; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
serverinfo += len;
|
||||||
|
serverinfo_length -= len;
|
||||||
|
}
|
||||||
|
return 0; /* Error */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serverinfo_srv_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (inlen != 0)
|
||||||
|
{
|
||||||
|
*al = SSL_AD_DECODE_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serverinfo_srv_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out, unsigned short *outlen,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
const unsigned char *serverinfo = NULL;
|
||||||
|
size_t serverinfo_length = 0;
|
||||||
|
|
||||||
|
/* Is there serverinfo data for the chosen server cert? */
|
||||||
|
if ((ssl_get_server_cert_serverinfo(s, &serverinfo,
|
||||||
|
&serverinfo_length)) != 0)
|
||||||
|
{
|
||||||
|
/* Find the relevant extension from the serverinfo */
|
||||||
|
int retval = serverinfo_find_extension(serverinfo, serverinfo_length,
|
||||||
|
ext_type, out, outlen);
|
||||||
|
if (retval == 0)
|
||||||
|
return 0; /* Error */
|
||||||
|
if (retval == -1)
|
||||||
|
return -1; /* No extension found, don't send extension */
|
||||||
|
return 1; /* Send extension */
|
||||||
|
}
|
||||||
|
return -1; /* No serverinfo data found, don't send extension */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With a NULL context, this function just checks that the serverinfo data
|
||||||
|
parses correctly. With a non-NULL context, it registers callbacks for
|
||||||
|
the included extensions. */
|
||||||
|
static int serverinfo_process_buffer(const unsigned char *serverinfo,
|
||||||
|
size_t serverinfo_length, SSL_CTX *ctx)
|
||||||
|
{
|
||||||
|
if (serverinfo == NULL || serverinfo_length == 0)
|
||||||
|
return 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned short ext_type = 0; /* uint16 */
|
||||||
|
unsigned short len = 0; /* uint16 */
|
||||||
|
|
||||||
|
/* end of serverinfo */
|
||||||
|
if (serverinfo_length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* read 2-byte type field */
|
||||||
|
if (serverinfo_length < 2)
|
||||||
|
return 0;
|
||||||
|
/* FIXME: check for types we understand explicitly? */
|
||||||
|
|
||||||
|
/* Register callbacks for extensions */
|
||||||
|
ext_type = (serverinfo[0] << 8) + serverinfo[1];
|
||||||
|
if (ctx && !SSL_CTX_set_custom_srv_ext(ctx, ext_type,
|
||||||
|
serverinfo_srv_first_cb,
|
||||||
|
serverinfo_srv_second_cb, NULL))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
serverinfo += 2;
|
||||||
|
serverinfo_length -= 2;
|
||||||
|
|
||||||
|
/* read 2-byte len field */
|
||||||
|
if (serverinfo_length < 2)
|
||||||
|
return 0;
|
||||||
|
len = (serverinfo[0] << 8) + serverinfo[1];
|
||||||
|
serverinfo += 2;
|
||||||
|
serverinfo_length -= 2;
|
||||||
|
|
||||||
|
if (len > serverinfo_length)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
serverinfo += len;
|
||||||
|
serverinfo_length -= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const unsigned char *authz_find_data(const unsigned char *authz,
|
static const unsigned char *authz_find_data(const unsigned char *authz,
|
||||||
size_t authz_length,
|
size_t authz_length,
|
||||||
unsigned char data_type,
|
unsigned char data_type,
|
||||||
@ -906,6 +1046,11 @@ static int ssl_set_authz(CERT *c, unsigned char *authz, size_t authz_length)
|
|||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
current_key->authz = OPENSSL_realloc(current_key->authz, authz_length);
|
current_key->authz = OPENSSL_realloc(current_key->authz, authz_length);
|
||||||
|
if (current_key->authz == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_SET_AUTHZ,ERR_R_MALLOC_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
current_key->authz_length = authz_length;
|
current_key->authz_length = authz_length;
|
||||||
memcpy(current_key->authz, authz, authz_length);
|
memcpy(current_key->authz, authz, authz_length);
|
||||||
return 1;
|
return 1;
|
||||||
@ -927,6 +1072,49 @@ int SSL_CTX_use_authz(SSL_CTX *ctx, unsigned char *authz,
|
|||||||
return ssl_set_authz(ctx->cert, authz, authz_length);
|
return ssl_set_authz(ctx->cert, authz, authz_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo,
|
||||||
|
size_t serverinfo_length)
|
||||||
|
{
|
||||||
|
if (ctx == NULL || serverinfo == NULL || serverinfo_length == 0)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_PASSED_NULL_PARAMETER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!serverinfo_process_buffer(serverinfo, serverinfo_length, NULL))
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
if (!ssl_cert_inst(&ctx->cert))
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctx->cert->key == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ctx->cert->key->serverinfo = OPENSSL_realloc(ctx->cert->key->serverinfo,
|
||||||
|
serverinfo_length);
|
||||||
|
if (ctx->cert->key->serverinfo == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,ERR_R_MALLOC_FAILURE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(ctx->cert->key->serverinfo, serverinfo, serverinfo_length);
|
||||||
|
ctx->cert->key->serverinfo_length = serverinfo_length;
|
||||||
|
|
||||||
|
/* Now that the serverinfo is validated and stored, go ahead and
|
||||||
|
* register callbacks. */
|
||||||
|
if (!serverinfo_process_buffer(serverinfo, serverinfo_length, ctx))
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO,SSL_R_INVALID_SERVERINFO_DATA);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length)
|
int SSL_use_authz(SSL *ssl, unsigned char *authz, size_t authz_length)
|
||||||
{
|
{
|
||||||
if (authz == NULL)
|
if (authz == NULL)
|
||||||
@ -1026,5 +1214,81 @@ int SSL_use_authz_file(SSL *ssl, const char *file)
|
|||||||
OPENSSL_free(authz);
|
OPENSSL_free(authz);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file)
|
||||||
|
{
|
||||||
|
unsigned char *serverinfo = NULL;
|
||||||
|
size_t serverinfo_length = 0;
|
||||||
|
unsigned char* extension = 0;
|
||||||
|
long extension_length = 0;
|
||||||
|
char* name = NULL;
|
||||||
|
char* header = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
BIO *bin = NULL;
|
||||||
|
size_t num_extensions = 0;
|
||||||
|
|
||||||
|
if (ctx == NULL || file == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE,ERR_R_PASSED_NULL_PARAMETER);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bin = BIO_new(BIO_s_file_internal());
|
||||||
|
if (bin == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_BUF_LIB);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (BIO_read_filename(bin, file) <= 0)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_SYS_LIB);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (num_extensions=0;; num_extensions++)
|
||||||
|
{
|
||||||
|
if (PEM_read_bio(bin, &name, &header, &extension, &extension_length) == 0)
|
||||||
|
{
|
||||||
|
/* There must be at least one extension in this file */
|
||||||
|
if (num_extensions == 0)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else /* End of file, we're done */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Check that the decoded PEM data is plausible (valid length field) */
|
||||||
|
if (extension_length < 4 || (extension[2] << 8) + extension[3] != extension_length - 4)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_PEM_LIB);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
/* Append the decoded extension to the serverinfo buffer */
|
||||||
|
serverinfo = OPENSSL_realloc(serverinfo, serverinfo_length + extension_length);
|
||||||
|
if (serverinfo == NULL)
|
||||||
|
{
|
||||||
|
SSLerr(SSL_F_SSL_CTX_USE_SERVERINFO_FILE, ERR_R_MALLOC_FAILURE);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
memcpy(serverinfo + serverinfo_length, extension, extension_length);
|
||||||
|
serverinfo_length += extension_length;
|
||||||
|
|
||||||
|
OPENSSL_free(name); name = NULL;
|
||||||
|
OPENSSL_free(header); header = NULL;
|
||||||
|
OPENSSL_free(extension); extension = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SSL_CTX_use_serverinfo(ctx, serverinfo, serverinfo_length);
|
||||||
|
end:
|
||||||
|
/* SSL_CTX_use_serverinfo makes a local copy of the serverinfo. */
|
||||||
|
OPENSSL_free(name);
|
||||||
|
OPENSSL_free(header);
|
||||||
|
OPENSSL_free(extension);
|
||||||
|
OPENSSL_free(serverinfo);
|
||||||
|
if (bin != NULL)
|
||||||
|
BIO_free(bin);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#endif /* OPENSSL_NO_STDIO */
|
#endif /* OPENSSL_NO_STDIO */
|
||||||
#endif /* OPENSSL_NO_TLSEXT */
|
#endif /* OPENSSL_NO_TLSEXT */
|
||||||
|
325
ssl/ssltest.c
325
ssl/ssltest.c
@ -295,6 +295,242 @@ static int MS_CALLBACK ssl_srp_server_param_cb(SSL *s, int *ad, void *arg)
|
|||||||
static BIO *bio_err=NULL;
|
static BIO *bio_err=NULL;
|
||||||
static BIO *bio_stdout=NULL;
|
static BIO *bio_stdout=NULL;
|
||||||
|
|
||||||
|
#define SCT_EXT_TYPE 18
|
||||||
|
|
||||||
|
/* WARNING : below extension types are *NOT* IETF assigned, and
|
||||||
|
could conflict if these types are reassigned and handled
|
||||||
|
specially by OpenSSL in the future */
|
||||||
|
#define TACK_EXT_TYPE 62208
|
||||||
|
#define CUSTOM_EXT_TYPE_0 1000
|
||||||
|
#define CUSTOM_EXT_TYPE_1 1001
|
||||||
|
#define CUSTOM_EXT_TYPE_2 1002
|
||||||
|
#define CUSTOM_EXT_TYPE_3 1003
|
||||||
|
|
||||||
|
const char custom_ext_cli_string[] = "abc";
|
||||||
|
const char custom_ext_srv_string[] = "defg";
|
||||||
|
|
||||||
|
/* These set from cmdline */
|
||||||
|
char* serverinfo_file = NULL;
|
||||||
|
int serverinfo_sct = 0;
|
||||||
|
int serverinfo_tack = 0;
|
||||||
|
|
||||||
|
/* These set based on extension callbacks */
|
||||||
|
int serverinfo_sct_seen = 0;
|
||||||
|
int serverinfo_tack_seen = 0;
|
||||||
|
int serverinfo_other_seen = 0;
|
||||||
|
|
||||||
|
/* This set from cmdline */
|
||||||
|
int custom_ext = 0;
|
||||||
|
|
||||||
|
/* This set based on extension callbacks */
|
||||||
|
int custom_ext_error = 0;
|
||||||
|
|
||||||
|
static int serverinfo_cli_cb(SSL* s, unsigned short ext_type,
|
||||||
|
const unsigned char* in, unsigned short inlen,
|
||||||
|
int* al, void* arg)
|
||||||
|
{
|
||||||
|
if (ext_type == SCT_EXT_TYPE)
|
||||||
|
serverinfo_sct_seen++;
|
||||||
|
else if (ext_type == TACK_EXT_TYPE)
|
||||||
|
serverinfo_tack_seen++;
|
||||||
|
else
|
||||||
|
serverinfo_other_seen++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_serverinfo()
|
||||||
|
{
|
||||||
|
if (serverinfo_sct != serverinfo_sct_seen)
|
||||||
|
return -1;
|
||||||
|
if (serverinfo_tack != serverinfo_tack_seen)
|
||||||
|
return -1;
|
||||||
|
if (serverinfo_other_seen)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Four test cases for custom extensions:
|
||||||
|
* 0 - no ClientHello extension or ServerHello response
|
||||||
|
* 1 - ClientHello with "abc", no response
|
||||||
|
* 2 - ClientHello with "abc", empty response
|
||||||
|
* 3 - ClientHello with "abc", "defg" response
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int custom_ext_0_cli_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_0)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return -1; /* Don't send an extension */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_0_cli_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
custom_ext_error = 1; /* Shouldn't be called */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_1_cli_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_1)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
*out = (const unsigned char*)custom_ext_cli_string;
|
||||||
|
*outlen = strlen(custom_ext_cli_string);
|
||||||
|
return 1; /* Send "abc" */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_1_cli_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
custom_ext_error = 1; /* Shouldn't be called */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_2_cli_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_2)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
*out = (const unsigned char*)custom_ext_cli_string;
|
||||||
|
*outlen = strlen(custom_ext_cli_string);
|
||||||
|
return 1; /* Send "abc" */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_2_cli_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_2)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (inlen != 0)
|
||||||
|
custom_ext_error = 1; /* Should be empty response */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_3_cli_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_3)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
*out = (const unsigned char*)custom_ext_cli_string;
|
||||||
|
*outlen = strlen(custom_ext_cli_string);
|
||||||
|
return 1; /* Send "abc" */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_3_cli_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_3)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (inlen != strlen(custom_ext_srv_string))
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (memcmp(custom_ext_srv_string, in, inlen) != 0)
|
||||||
|
custom_ext_error = 1; /* Check for "defg" */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int custom_ext_0_srv_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return 0; /* Shouldn't be called */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_0_srv_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return 0; /* Shouldn't be called */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_1_srv_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_1)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
/* Check for "abc" */
|
||||||
|
if (inlen != strlen(custom_ext_cli_string))
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (memcmp(in, custom_ext_cli_string, inlen) != 0)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_1_srv_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
return -1; /* Don't send an extension */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_2_srv_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_2)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
/* Check for "abc" */
|
||||||
|
if (inlen != strlen(custom_ext_cli_string))
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (memcmp(in, custom_ext_cli_string, inlen) != 0)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_2_srv_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
*out = NULL;
|
||||||
|
*outlen = 0;
|
||||||
|
return 1; /* Send empty extension */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_3_srv_first_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char *in,
|
||||||
|
unsigned short inlen, int *al,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (ext_type != CUSTOM_EXT_TYPE_3)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
/* Check for "abc" */
|
||||||
|
if (inlen != strlen(custom_ext_cli_string))
|
||||||
|
custom_ext_error = 1;
|
||||||
|
if (memcmp(in, custom_ext_cli_string, inlen) != 0)
|
||||||
|
custom_ext_error = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int custom_ext_3_srv_second_cb(SSL *s, unsigned short ext_type,
|
||||||
|
const unsigned char **out,
|
||||||
|
unsigned short *outlen, void *arg)
|
||||||
|
{
|
||||||
|
*out = (const unsigned char*)custom_ext_srv_string;
|
||||||
|
*outlen = strlen(custom_ext_srv_string);
|
||||||
|
return 1; /* Send "defg" */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *cipher=NULL;
|
static char *cipher=NULL;
|
||||||
static int verbose=0;
|
static int verbose=0;
|
||||||
static int debug=0;
|
static int debug=0;
|
||||||
@ -374,6 +610,10 @@ static void sv_usage(void)
|
|||||||
fprintf(stderr," -c_support_proof - indicate client support for server_authz audit proofs\n");
|
fprintf(stderr," -c_support_proof - indicate client support for server_authz audit proofs\n");
|
||||||
fprintf(stderr," -c_require_proof - fail if no audit proof is sent\n");
|
fprintf(stderr," -c_require_proof - fail if no audit proof is sent\n");
|
||||||
#endif
|
#endif
|
||||||
|
fprintf(stderr," -serverinfo_file file - have server use this file\n");
|
||||||
|
fprintf(stderr," -serverinfo_sct - have client offer and expect SCT\n");
|
||||||
|
fprintf(stderr," -serverinfo_tack - have client offer and expect TACK\n");
|
||||||
|
fprintf(stderr," -custom_ext - try various custom extension callbacks\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_details(SSL *c_ssl, const char *prefix)
|
static void print_details(SSL *c_ssl, const char *prefix)
|
||||||
@ -845,6 +1085,23 @@ int main(int argc, char *argv[])
|
|||||||
tls1 = 1;
|
tls1 = 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
else if (strcmp(*argv,"-serverinfo_sct") == 0)
|
||||||
|
{
|
||||||
|
serverinfo_sct = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*argv,"-serverinfo_tack") == 0)
|
||||||
|
{
|
||||||
|
serverinfo_tack = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*argv,"-serverinfo_file") == 0)
|
||||||
|
{
|
||||||
|
if (--argc < 1) goto bad;
|
||||||
|
serverinfo_file = *(++argv);
|
||||||
|
}
|
||||||
|
else if (strcmp(*argv,"-custom_ext") == 0)
|
||||||
|
{
|
||||||
|
custom_ext = 1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr,"unknown option %s\n",*argv);
|
fprintf(stderr,"unknown option %s\n",*argv);
|
||||||
@ -1187,6 +1444,50 @@ bad:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (serverinfo_sct)
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, SCT_EXT_TYPE, NULL,
|
||||||
|
serverinfo_cli_cb, NULL);
|
||||||
|
if (serverinfo_tack)
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, TACK_EXT_TYPE, NULL,
|
||||||
|
serverinfo_cli_cb, NULL);
|
||||||
|
|
||||||
|
if (serverinfo_file)
|
||||||
|
if (!SSL_CTX_use_serverinfo_file(s_ctx, serverinfo_file))
|
||||||
|
{
|
||||||
|
BIO_printf(bio_err, "missing serverinfo file\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custom_ext)
|
||||||
|
{
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_0,
|
||||||
|
custom_ext_0_cli_first_cb,
|
||||||
|
custom_ext_0_cli_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_1,
|
||||||
|
custom_ext_1_cli_first_cb,
|
||||||
|
custom_ext_1_cli_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_2,
|
||||||
|
custom_ext_2_cli_first_cb,
|
||||||
|
custom_ext_2_cli_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_cli_ext(c_ctx, CUSTOM_EXT_TYPE_3,
|
||||||
|
custom_ext_3_cli_first_cb,
|
||||||
|
custom_ext_3_cli_second_cb, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_0,
|
||||||
|
custom_ext_0_srv_first_cb,
|
||||||
|
custom_ext_0_srv_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_1,
|
||||||
|
custom_ext_1_srv_first_cb,
|
||||||
|
custom_ext_1_srv_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_2,
|
||||||
|
custom_ext_2_srv_first_cb,
|
||||||
|
custom_ext_2_srv_second_cb, NULL);
|
||||||
|
SSL_CTX_set_custom_srv_ext(s_ctx, CUSTOM_EXT_TYPE_3,
|
||||||
|
custom_ext_3_srv_first_cb,
|
||||||
|
custom_ext_3_srv_second_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
c_ssl=SSL_new(c_ctx);
|
c_ssl=SSL_new(c_ctx);
|
||||||
s_ssl=SSL_new(s_ctx);
|
s_ssl=SSL_new(s_ctx);
|
||||||
|
|
||||||
@ -1641,6 +1942,19 @@ int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count,
|
|||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
print_details(c_ssl, "DONE via BIO pair: ");
|
print_details(c_ssl, "DONE via BIO pair: ");
|
||||||
|
|
||||||
|
if (verify_serverinfo() < 0)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custom_ext_error)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
@ -1936,6 +2250,17 @@ int doit(SSL *s_ssl, SSL *c_ssl, long count)
|
|||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
print_details(c_ssl, "DONE: ");
|
print_details(c_ssl, "DONE: ");
|
||||||
|
|
||||||
|
if (verify_serverinfo() < 0)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (custom_ext_error)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
ret=0;
|
ret=0;
|
||||||
err:
|
err:
|
||||||
/* We have to set the BIO's to NULL otherwise they will be
|
/* We have to set the BIO's to NULL otherwise they will be
|
||||||
|
143
ssl/t1_lib.c
143
ssl/t1_lib.c
@ -1400,6 +1400,40 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
|
|||||||
*(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
|
*(ret++) = TLSEXT_AUTHZDATAFORMAT_audit_proof;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add custom TLS Extensions to ClientHello */
|
||||||
|
if (s->ctx->custom_cli_ext_records_count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
custom_cli_ext_record* record;
|
||||||
|
|
||||||
|
for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
|
||||||
|
{
|
||||||
|
const unsigned char* out = NULL;
|
||||||
|
unsigned short outlen = 0;
|
||||||
|
|
||||||
|
record = &s->ctx->custom_cli_ext_records[i];
|
||||||
|
/* NULL callback sends empty extension */
|
||||||
|
/* -1 from callback omits extension */
|
||||||
|
if (record->fn1)
|
||||||
|
{
|
||||||
|
int cb_retval = 0;
|
||||||
|
cb_retval = record->fn1(s, record->ext_type,
|
||||||
|
&out, &outlen,
|
||||||
|
record->arg);
|
||||||
|
if (cb_retval == 0)
|
||||||
|
return NULL; /* error */
|
||||||
|
if (cb_retval == -1)
|
||||||
|
continue; /* skip this extension */
|
||||||
|
}
|
||||||
|
if (limit < ret + 4 + outlen)
|
||||||
|
return NULL;
|
||||||
|
s2n(record->ext_type, ret);
|
||||||
|
s2n(outlen, ret);
|
||||||
|
memcpy(ret, out, outlen);
|
||||||
|
ret += outlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((extdatalen = ret-p-2) == 0)
|
if ((extdatalen = ret-p-2) == 0)
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
@ -1667,6 +1701,47 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If custom types were sent in ClientHello, add ServerHello responses */
|
||||||
|
if (s->s3->tlsext_custom_types_count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->s3->tlsext_custom_types_count; i++)
|
||||||
|
{
|
||||||
|
size_t j;
|
||||||
|
custom_srv_ext_record *record;
|
||||||
|
|
||||||
|
for (j = 0; j < s->ctx->custom_srv_ext_records_count; j++)
|
||||||
|
{
|
||||||
|
record = &s->ctx->custom_srv_ext_records[j];
|
||||||
|
if (s->s3->tlsext_custom_types[i] == record->ext_type)
|
||||||
|
{
|
||||||
|
const unsigned char *out = NULL;
|
||||||
|
unsigned short outlen = 0;
|
||||||
|
int cb_retval = 0;
|
||||||
|
|
||||||
|
/* NULL callback or -1 omits extension */
|
||||||
|
if (!record->fn2)
|
||||||
|
break;
|
||||||
|
cb_retval = record->fn2(s, record->ext_type,
|
||||||
|
&out, &outlen,
|
||||||
|
record->arg);
|
||||||
|
if (cb_retval == 0)
|
||||||
|
return NULL; /* error */
|
||||||
|
if (cb_retval == -1)
|
||||||
|
break; /* skip this extension */
|
||||||
|
if (limit < ret + 4 + outlen)
|
||||||
|
return NULL;
|
||||||
|
s2n(record->ext_type, ret);
|
||||||
|
s2n(outlen, ret);
|
||||||
|
memcpy(ret, out, outlen);
|
||||||
|
ret += outlen;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((extdatalen = ret-p-2)== 0)
|
if ((extdatalen = ret-p-2)== 0)
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
@ -2235,6 +2310,54 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this ClientHello extension was unhandled and this is
|
||||||
|
* a nonresumed connection, check whether the extension is a
|
||||||
|
* custom TLS Extension (has a custom_srv_ext_record), and if
|
||||||
|
* so call the callback and record the extension number so that
|
||||||
|
* an appropriate ServerHello may be later returned.
|
||||||
|
*/
|
||||||
|
else if (!s->hit && s->ctx->custom_srv_ext_records_count)
|
||||||
|
{
|
||||||
|
custom_srv_ext_record *record;
|
||||||
|
|
||||||
|
for (i=0; i < s->ctx->custom_srv_ext_records_count; i++)
|
||||||
|
{
|
||||||
|
record = &s->ctx->custom_srv_ext_records[i];
|
||||||
|
if (type == record->ext_type)
|
||||||
|
{
|
||||||
|
/* Error on duplicate TLS Extensions */
|
||||||
|
size_t j;
|
||||||
|
|
||||||
|
for (j = 0; j < s->s3->tlsext_custom_types_count; j++)
|
||||||
|
{
|
||||||
|
if (s->s3->tlsext_custom_types[j] == type)
|
||||||
|
{
|
||||||
|
*al = TLS1_AD_DECODE_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback */
|
||||||
|
if (record->fn1 && !record->fn1(s, type, data, size, al, record->arg))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Add the (non-duplicated) entry */
|
||||||
|
s->s3->tlsext_custom_types_count++;
|
||||||
|
s->s3->tlsext_custom_types = OPENSSL_realloc(
|
||||||
|
s->s3->tlsext_custom_types,
|
||||||
|
s->s3->tlsext_custom_types_count*2);
|
||||||
|
if (s->s3->tlsext_custom_types == NULL)
|
||||||
|
{
|
||||||
|
s->s3->tlsext_custom_types = 0;
|
||||||
|
*al = TLS1_AD_INTERNAL_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s->s3->tlsext_custom_types[
|
||||||
|
s->s3->tlsext_custom_types_count-1] = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data+=size;
|
data+=size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2543,6 +2666,26 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
|
|||||||
s->s3->tlsext_authz_server_promised = 1;
|
s->s3->tlsext_authz_server_promised = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this extension type was not otherwise handled, but
|
||||||
|
* matches a custom_cli_ext_record, then send it to the c
|
||||||
|
* callback */
|
||||||
|
else if (s->ctx->custom_cli_ext_records_count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
custom_cli_ext_record* record;
|
||||||
|
|
||||||
|
for (i = 0; i < s->ctx->custom_cli_ext_records_count; i++)
|
||||||
|
{
|
||||||
|
record = &s->ctx->custom_cli_ext_records[i];
|
||||||
|
if (record->ext_type == type)
|
||||||
|
{
|
||||||
|
if (record->fn2 && !record->fn2(s, type, data, size, al, record->arg))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data += size;
|
data += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ test_engine: $(ENGINETEST)
|
|||||||
|
|
||||||
test_ssl: keyU.ss certU.ss certCA.ss certP1.ss keyP1.ss certP2.ss keyP2.ss \
|
test_ssl: keyU.ss certU.ss certCA.ss certP1.ss keyP1.ss certP2.ss keyP2.ss \
|
||||||
intP1.ss intP2.ss $(SSLTEST) testssl testsslproxy \
|
intP1.ss intP2.ss $(SSLTEST) testssl testsslproxy \
|
||||||
../apps/server2.pem
|
../apps/server2.pem serverinfo.pem
|
||||||
@echo "test SSL protocol"
|
@echo "test SSL protocol"
|
||||||
@if [ -n "$(FIPSCANLIB)" ]; then \
|
@if [ -n "$(FIPSCANLIB)" ]; then \
|
||||||
sh ./testfipsssl keyU.ss certU.ss certCA.ss; \
|
sh ./testfipsssl keyU.ss certU.ss certCA.ss; \
|
||||||
|
16
test/serverinfo.pem
Normal file
16
test/serverinfo.pem
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN SCT-----
|
||||||
|
ABIAZMevsj4TC5rgwjZNciLGwh15YXoIK9t5aypGJIG4QzyMowmwwDdqxudkUcGa
|
||||||
|
DvuqlYL7psO5j4/BIHTe677CAZBBH3Ho2NOM5q1zub4AbfUMlKeufuQgeQ2Tj1oe
|
||||||
|
LJLRzrwDnPs=
|
||||||
|
-----END SCT-----
|
||||||
|
|
||||||
|
-----BEGIN TACK EXTENSION-----
|
||||||
|
8wABTwFMh1Dz+3W6zULWJKjav5TNaFEXL1h98YtCXeyZnORYg4mbKpxH5CMbjpgx
|
||||||
|
To3amSqUPF4Ntjc/i9+poutxebYkbgAAAkMcxb8+RaM9YEywaJEGViKJJmpYG/gJ
|
||||||
|
HgfGaefI9kKbXSDmP9ntg8dLvDzuyYw14ktM2850Q9WvBiltpekilZxVuT2bFtfs
|
||||||
|
cmS++SAK9YOM8RrKhL1TLmrktoBEJZ6z5GTukYdQ8/t1us1C1iSo2r+UzWhRFy9Y
|
||||||
|
ffGLQl3smZzkWIOJmyqcR+QjG46YMU6N2pkqlDxeDbY3P4vfqaLrcXm2JG4AAAGN
|
||||||
|
xXQJPbdniI9rEydVXb1Cu1yT/t7FBEx6hLxuoypXjCI1wCGpXsd8zEnloR0Ank5h
|
||||||
|
VO/874E/BZlItzSPpcmDKl5Def6BrAJTErQlE9npo52S05YWORxJw1+VYBdqQ09A
|
||||||
|
x3wA
|
||||||
|
-----END TACK EXTENSION-----
|
19
test/testssl
19
test/testssl
@ -30,6 +30,8 @@ else
|
|||||||
extra="$4"
|
extra="$4"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
serverinfo="./serverinfo.pem"
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
echo test sslv2
|
echo test sslv2
|
||||||
@ -165,6 +167,23 @@ $ssltest -tls1 -cipher PSK -psk abc123 $extra || exit 1
|
|||||||
echo test tls1 with PSK via BIO pair
|
echo test tls1 with PSK via BIO pair
|
||||||
$ssltest -bio_pair -tls1 -cipher PSK -psk abc123 $extra || exit 1
|
$ssltest -bio_pair -tls1 -cipher PSK -psk abc123 $extra || exit 1
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# Custom Extension tests
|
||||||
|
|
||||||
|
echo test tls1 with custom extensions
|
||||||
|
$ssltest -bio_pair -tls1 -custom_ext || exit 1
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# Serverinfo tests
|
||||||
|
|
||||||
|
echo test tls1 with serverinfo
|
||||||
|
$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo || exit 1
|
||||||
|
$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct || exit 1
|
||||||
|
$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_tack || exit 1
|
||||||
|
$ssltest -bio_pair -tls1 -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1
|
||||||
|
$ssltest -bio_pair -tls1 -custom_ext -serverinfo_file $serverinfo -serverinfo_sct -serverinfo_tack || exit 1
|
||||||
|
|
||||||
|
|
||||||
if ../util/shlib_wrap.sh ../apps/openssl no-srp; then
|
if ../util/shlib_wrap.sh ../apps/openssl no-srp; then
|
||||||
echo skipping SRP tests
|
echo skipping SRP tests
|
||||||
else
|
else
|
||||||
|
@ -396,6 +396,7 @@ sub get_tests
|
|||||||
'testss',
|
'testss',
|
||||||
'testssl',
|
'testssl',
|
||||||
'testsslproxy',
|
'testsslproxy',
|
||||||
|
'serverinfo.pem',
|
||||||
);
|
);
|
||||||
my $copies = copy_scripts(1, 'test', @copies);
|
my $copies = copy_scripts(1, 'test', @copies);
|
||||||
$copies .= copy_scripts(0, 'test', ('smcont.txt'));
|
$copies .= copy_scripts(0, 'test', ('smcont.txt'));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user