Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -79,19 +79,19 @@ clean: rm -f tls.o tlsBIO.o tlsIO.o tlsX509.o rm -f @EXTENSION_TARGET@ shared-@EXTENSION_TARGET@ static-@EXTENSION_TARGET@ rm -f shared-@EXTENSION_TARGET@.def shared-@EXTENSION_TARGET@.lib rm -f tls.tcl.h tls.tcl.h.new.1 tls.tcl.h.new.2 - rm -f tcltls.syms # Clean the local build directory back to what it was after unpacking the # distribution tarball distclean: clean rm -f config.log config.status rm -f dh_params.h.new dh_params.h rm -f Makefile pkgIndex.tcl rm -f tcltls.a.linkadd + rm -f tcltls.syms # Clean the local build directory back to only thing things that exist in # version control system mrproper: distclean rm -f @srcdir@/configure @srcdir@/config.sub @srcdir@/config.guess @srcdir@/install-sh Index: tclOpts.h ================================================================== --- tclOpts.h +++ tclOpts.h @@ -42,10 +42,15 @@ if (Tcl_GetBooleanFromObj(interp, objv[idx],\ &(var)) != TCL_OK) { \ return TCL_ERROR; \ } \ OPT_POSTLOG() + +#define OPTBYTE(option, var, lvar) \ + OPT_PROLOG(option) \ + var = Tcl_GetByteArrayFromObj(objv[idx], &(lvar));\ + OPT_POSTLOG() #define OPTBAD(type, list) \ Tcl_AppendResult(interp, "bad ", (type), \ " \"", opt, "\": must be ", \ (list), (char *) NULL) Index: tls.c ================================================================== --- tls.c +++ tls.c @@ -60,12 +60,13 @@ static int UnimportObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static SSL_CTX *CTX_Init(State *statePtr, int isServer, int proto, char *key, - char *cert, char *CAdir, char *CAfile, char *ciphers, - char *DHparams); + char *certfile, unsigned char *key_asn1, unsigned char *cert_asn1, + int key_asn1_len, int cert_asn1_len, char *CAdir, char *CAfile, + char *ciphers, char *DHparams); static int TlsLibInit(int uninitialize); #define TLS_PROTO_SSL2 0x01 #define TLS_PROTO_SSL3 0x02 @@ -727,26 +728,30 @@ int objc; Tcl_Obj *CONST objv[]; { Tcl_Channel chan; /* The channel to set a mode on. */ State *statePtr; /* client state for ssl socket */ - SSL_CTX *ctx = NULL; - Tcl_Obj *script = NULL; - Tcl_Obj *password = NULL; + SSL_CTX *ctx = NULL; + Tcl_Obj *script = NULL; + Tcl_Obj *password = NULL; Tcl_DString upperChannelTranslation, upperChannelBlocking, upperChannelEncoding, upperChannelEOFChar; int idx, len; - int flags = TLS_TCL_INIT; - int server = 0; /* is connection incoming or outgoing? */ - char *key = NULL; - char *cert = NULL; - char *ciphers = NULL; - char *CAfile = NULL; - char *CAdir = NULL; - char *DHparams = NULL; - char *model = NULL; + int flags = TLS_TCL_INIT; + int server = 0; /* is connection incoming or outgoing? */ + char *keyfile = NULL; + char *certfile = NULL; + unsigned char *key = NULL; + int key_len = 0; + unsigned char *cert = NULL; + int cert_len = 0; + char *ciphers = NULL; + char *CAfile = NULL; + char *CAdir = NULL; + char *DHparams = NULL; + char *model = NULL; #ifndef OPENSSL_NO_TLSEXT - char *servername = NULL; /* hostname for Server Name Indication */ + char *servername = NULL; /* hostname for Server Name Indication */ #endif int ssl2 = 0, ssl3 = 0; int tls1 = 1, tls1_1 = 1, tls1_2 = 1, tls1_3 = 1; int proto = 0; int verify = 0, require = 0, request = 1; @@ -793,15 +798,15 @@ if (opt[0] != '-') break; OPTSTR( "-cadir", CAdir); OPTSTR( "-cafile", CAfile); - OPTSTR( "-certfile", cert); + OPTSTR( "-certfile", certfile); OPTSTR( "-cipher", ciphers); OPTOBJ( "-command", script); OPTSTR( "-dhparams", DHparams); - OPTSTR( "-keyfile", key); + OPTSTR( "-keyfile", keyfile); OPTSTR( "-model", model); OPTOBJ( "-password", password); OPTBOOL( "-require", require); OPTBOOL( "-request", request); OPTBOOL( "-server", server); @@ -813,12 +818,14 @@ OPTBOOL( "-ssl3", ssl3); OPTBOOL( "-tls1", tls1); OPTBOOL( "-tls1.1", tls1_1); OPTBOOL( "-tls1.2", tls1_2); OPTBOOL( "-tls1.3", tls1_3); + OPTBYTE("-cert", cert, cert_len); + OPTBYTE("-key", key, key_len); - OPTBAD( "option", "-cadir, -cafile, -certfile, -cipher, -command, -dhparams, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); + OPTBAD( "option", "-cadir, -cafile, -cert, -certfile, -cipher, -command, -dhparams, -key, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); return TCL_ERROR; } if (request) verify |= SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER; if (request && require) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; @@ -830,16 +837,18 @@ proto |= (tls1_1 ? TLS_PROTO_TLS1_1 : 0); proto |= (tls1_2 ? TLS_PROTO_TLS1_2 : 0); proto |= (tls1_3 ? TLS_PROTO_TLS1_3 : 0); /* reset to NULL if blank string provided */ - if (cert && !*cert) cert = NULL; - if (key && !*key) key = NULL; - if (ciphers && !*ciphers) ciphers = NULL; - if (CAfile && !*CAfile) CAfile = NULL; - if (CAdir && !*CAdir) CAdir = NULL; - if (DHparams && !*DHparams) DHparams = NULL; + if (cert && !*cert) cert = NULL; + if (key && !*key) key = NULL; + if (certfile && !*certfile) certfile = NULL; + if (keyfile && !*keyfile) keyfile = NULL; + if (ciphers && !*ciphers) ciphers = NULL; + if (CAfile && !*CAfile) CAfile = NULL; + if (CAdir && !*CAdir) CAdir = NULL; + if (DHparams && !*DHparams) DHparams = NULL; /* new SSL state */ statePtr = (State *) ckalloc((unsigned) sizeof(State)); memset(statePtr, 0, sizeof(State)); @@ -885,12 +894,13 @@ Tls_Free((char *) statePtr); return TCL_ERROR; } ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx; } else { - if ((ctx = CTX_Init(statePtr, server, proto, key, cert, CAdir, CAfile, ciphers, - DHparams)) == (SSL_CTX*)0) { + if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, + cert, key_len, cert_len, CAdir, CAfile, ciphers, + DHparams)) == (SSL_CTX*)0) { Tls_Free((char *) statePtr); return TCL_ERROR; } } @@ -1054,16 +1064,21 @@ * *------------------------------------------------------------------- */ static SSL_CTX * -CTX_Init(statePtr, isServer, proto, key, cert, CAdir, CAfile, ciphers, DHparams) +CTX_Init(statePtr, isServer, proto, keyfile, certfile, key, cert, + key_len, cert_len, CAdir, CAfile, ciphers, DHparams) State *statePtr; int isServer; int proto; - char *key; - char *cert; + char *keyfile; + char *certfile; + unsigned char *key; + unsigned char *cert; + int key_len; + int cert_len; char *CAdir; char *CAfile; char *ciphers; char *DHparams; { @@ -1070,10 +1085,11 @@ Tcl_Interp *interp = statePtr->interp; SSL_CTX *ctx = NULL; Tcl_DString ds; Tcl_DString ds1; int off = 0; + int load_private_key; const SSL_METHOD *method; dprintf("Called"); if (!proto) { @@ -1249,63 +1265,99 @@ DH_free(dh); } #endif /* set our certificate */ - if (cert != NULL) { - Tcl_DStringInit(&ds); - - if (SSL_CTX_use_certificate_file(ctx, F2N( cert, &ds), - SSL_FILETYPE_PEM) <= 0) { - Tcl_DStringFree(&ds); - Tcl_AppendResult(interp, - "unable to set certificate file ", cert, ": ", - REASON(), (char *) NULL); - SSL_CTX_free(ctx); - return (SSL_CTX *)0; - } - - /* get the private key associated with this certificate */ - if (key == NULL) key=cert; - - if (SSL_CTX_use_PrivateKey_file(ctx, F2N( key, &ds), - SSL_FILETYPE_PEM) <= 0) { - Tcl_DStringFree(&ds); - /* flush the passphrase which might be left in the result */ - Tcl_SetResult(interp, NULL, TCL_STATIC); - Tcl_AppendResult(interp, - "unable to set public key file ", key, " ", - REASON(), (char *) NULL); - SSL_CTX_free(ctx); - return (SSL_CTX *)0; - } - Tcl_DStringFree(&ds); + load_private_key = 0; + if (certfile != NULL) { + load_private_key = 1; + + Tcl_DStringInit(&ds); + + if (SSL_CTX_use_certificate_file(ctx, F2N( certfile, &ds), + SSL_FILETYPE_PEM) <= 0) { + Tcl_DStringFree(&ds); + Tcl_AppendResult(interp, + "unable to set certificate file ", certfile, ": ", + REASON(), (char *) NULL); + SSL_CTX_free(ctx); + return (SSL_CTX *)0; + } + } else if (cert != NULL) { + load_private_key = 1; + if (SSL_CTX_use_certificate_ASN1(ctx, cert_len, cert) <= 0) { + Tcl_DStringFree(&ds); + Tcl_AppendResult(interp, + "unable to set certificate: ", + REASON(), (char *) NULL); + SSL_CTX_free(ctx); + return (SSL_CTX *)0; + } + } else { + certfile = (char*)X509_get_default_cert_file(); + + if (SSL_CTX_use_certificate_file(ctx, certfile, + SSL_FILETYPE_PEM) <= 0) { +#if 0 + Tcl_DStringFree(&ds); + Tcl_AppendResult(interp, + "unable to use default certificate file ", certfile, ": ", + REASON(), (char *) NULL); + SSL_CTX_free(ctx); + return (SSL_CTX *)0; +#endif + } + } + + /* set our private key */ + if (load_private_key) { + if (keyfile == NULL && key == NULL) { + keyfile = certfile; + } + + if (keyfile != NULL) { + /* get the private key associated with this certificate */ + if (keyfile == NULL) { + keyfile = certfile; + } + + if (SSL_CTX_use_PrivateKey_file(ctx, F2N( keyfile, &ds), SSL_FILETYPE_PEM) <= 0) { + Tcl_DStringFree(&ds); + /* flush the passphrase which might be left in the result */ + Tcl_SetResult(interp, NULL, TCL_STATIC); + Tcl_AppendResult(interp, + "unable to set public key file ", keyfile, " ", + REASON(), (char *) NULL); + SSL_CTX_free(ctx); + return (SSL_CTX *)0; + } + + Tcl_DStringFree(&ds); + } else if (key != NULL) { + if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ctx, key,key_len) <= 0) { + Tcl_DStringFree(&ds); + /* flush the passphrase which might be left in the result */ + Tcl_SetResult(interp, NULL, TCL_STATIC); + Tcl_AppendResult(interp, + "unable to set public key: ", + REASON(), (char *) NULL); + SSL_CTX_free(ctx); + return (SSL_CTX *)0; + } + } /* Now we know that a key and cert have been set against * the SSL context */ if (!SSL_CTX_check_private_key(ctx)) { Tcl_AppendResult(interp, "private key does not match the certificate public key", (char *) NULL); SSL_CTX_free(ctx); return (SSL_CTX *)0; } - } else { - cert = (char*)X509_get_default_cert_file(); - - if (SSL_CTX_use_certificate_file(ctx, cert, - SSL_FILETYPE_PEM) <= 0) { -#if 0 - Tcl_DStringFree(&ds); - Tcl_AppendResult(interp, - "unable to use default certificate file ", cert, ": ", - REASON(), (char *) NULL); - SSL_CTX_free(ctx); - return (SSL_CTX *)0; -#endif - } - } - + } + + /* Set verification CAs */ Tcl_DStringInit(&ds); Tcl_DStringInit(&ds1); if (!SSL_CTX_load_verify_locations(ctx, F2N(CAfile, &ds), F2N(CAdir, &ds1)) || !SSL_CTX_set_default_verify_paths(ctx)) { #if 0 @@ -1318,10 +1370,11 @@ return (SSL_CTX *)0; #endif } /* https://sourceforge.net/p/tls/bugs/57/ */ + /* XXX:TODO: Let the user supply values here instead of something that exists on the filesystem */ if ( CAfile != NULL ) { STACK_OF(X509_NAME) *certNames = SSL_load_client_CA_file( F2N(CAfile, &ds) ); if ( certNames != NULL ) { SSL_CTX_set_client_CA_list(ctx, certNames ); } Index: tls.htm ================================================================== --- tls.htm +++ tls.htm @@ -165,11 +165,13 @@
-cadir dir
Provide the directory containing the CA certificates.
-cafile filename
Provide the CA file.
-certfile filename
-
Provide the certificate to use.
+
Provide the name of a file containing certificate to use.
+
-cert filename
+
Provide the contents of a certificate to use, as a DER encoded binary value (X.509 DER).
-cipher string
Provide the cipher suites to use. Syntax is as per OpenSSL.
-command callback
If specified, this callback will be invoked at several points @@ -183,10 +185,12 @@
-dhparams filename
Provide a Diffie-Hellman parameters file.
-keyfile filename
Provide the private key file. (default: value of -certfile)
+
-key filename
+
Provide the private key to use as a DER encoded value (PKCS#1 DER)
-model channel
This will force this channel to share the same SSL_CTX structure as the specified channel, and therefore share callbacks etc.
-password callback
Index: tls.tcl ================================================================== --- tls.tcl +++ tls.tcl @@ -32,14 +32,16 @@ {* -myaddr sopts 1} {0 -myport sopts 1} {* -type sopts 1} {* -cadir iopts 1} {* -cafile iopts 1} + {* -cert iopts 1} {* -certfile iopts 1} {* -cipher iopts 1} {* -command iopts 1} {* -dhparams iopts 1} + {* -key iopts 1} {* -keyfile iopts 1} {* -password iopts 1} {* -request iopts 1} {* -require iopts 1} {* -autoservername discardOpts 1}