summaryrefslogtreecommitdiffstats
path: root/tls/tls.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2019-01-02 21:21:14 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2019-01-02 21:21:14 (GMT)
commit4db70369bb58c270b362c5696a6fe8c91a1a0217 (patch)
tree51cf1fa59473c1451a3b45698e010b8843d53d07 /tls/tls.c
parentfd7981505543bf2c863a97fc94bb2119411fb093 (diff)
downloadblt-4db70369bb58c270b362c5696a6fe8c91a1a0217.zip
blt-4db70369bb58c270b362c5696a6fe8c91a1a0217.tar.gz
blt-4db70369bb58c270b362c5696a6fe8c91a1a0217.tar.bz2
update TEA 3.13
Diffstat (limited to 'tls/tls.c')
-rw-r--r--tls/tls.c1833
1 files changed, 0 insertions, 1833 deletions
diff --git a/tls/tls.c b/tls/tls.c
deleted file mode 100644
index dd95e93..0000000
--- a/tls/tls.c
+++ /dev/null
@@ -1,1833 +0,0 @@
-/*
- * Copyright (C) 1997-1999 Matt Newman <matt@novadigm.com>
- * some modifications:
- * Copyright (C) 2000 Ajuba Solutions
- * Copyright (C) 2002 ActiveState Corporation
- * Copyright (C) 2004 Starfish Systems
- *
- * $Header: /cvsroot/tls/tls/tls.c,v 1.37 2015/07/07 17:16:02 andreas_kupries Exp $
- *
- * TLS (aka SSL) Channel - can be layered on any bi-directional
- * Tcl_Channel (Note: Requires Trf Core Patch)
- *
- * This was built (almost) from scratch based upon observation of
- * OpenSSL 0.9.2B
- *
- * Addition credit is due for Andreas Kupries (a.kupries@westend.com), for
- * providing the Tcl_ReplaceChannel mechanism and working closely with me
- * to enhance it to support full fileevent semantics.
- *
- * Also work done by the follow people provided the impetus to do this "right":
- * tclSSL (Colin McCormack, Shared Technology)
- * SSLtcl (Peter Antman)
- *
- */
-
-#include "tlsInt.h"
-#include "tclOpts.h"
-#include <stdlib.h>
-
-/*
- * External functions
- */
-
-/*
- * Forward declarations
- */
-
-#define F2N( key, dsp) \
- (((key) == NULL) ? (char *) NULL : \
- Tcl_TranslateFileName(interp, (key), (dsp)))
-#define REASON() ERR_reason_error_string(ERR_get_error())
-
-static void InfoCallback _ANSI_ARGS_ ((CONST SSL *ssl, int where, int ret));
-
-static int CiphersObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int HandshakeObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int ImportObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int StatusObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int VersionObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int MiscObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static int UnimportObjCmd _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
-
-static SSL_CTX *CTX_Init _ANSI_ARGS_((State *statePtr, int proto, char *key,
- char *cert, char *CAdir, char *CAfile, char *ciphers,
- char *DHparams));
-
-static int TlsLibInit _ANSI_ARGS_ (()) ;
-
-#define TLS_PROTO_SSL2 0x01
-#define TLS_PROTO_SSL3 0x02
-#define TLS_PROTO_TLS1 0x04
-#define TLS_PROTO_TLS1_1 0x08
-#define TLS_PROTO_TLS1_2 0x10
-#define ENABLED(flag, mask) (((flag) & (mask)) == (mask))
-
-/*
- * Static data structures
- */
-
-#ifndef OPENSSL_NO_DH
-/* code derived from output of 'openssl dhparam -C 2048' */
-
-static unsigned char dh2048_p[]={
- 0xEC,0xFD,0x6F,0x66,0xD8,0xBC,0xB4,0xCB,0xD7,0xE7,0xB4,0xAE,
- 0xEC,0xC0,0x06,0x25,0x40,0x9F,0x3F,0xC4,0xAC,0x34,0x19,0x36,
- 0x8A,0xAB,0xA9,0xF6,0x45,0x36,0x87,0x1F,0x10,0x35,0x3F,0x90,
- 0x00,0xC6,0x7A,0xE8,0x51,0xF4,0x7F,0x50,0x0F,0xC2,0x82,0x91,
- 0xAD,0x60,0x1B,0x49,0xB1,0x0B,0x23,0xC3,0x37,0xAE,0x0D,0x2C,
- 0x49,0xC6,0xFB,0x60,0x9D,0x50,0x2F,0x8C,0x2F,0xDE,0xE6,0x5F,
- 0x53,0x8B,0x5F,0xF9,0x70,0x16,0xEE,0x51,0xD1,0xAB,0x02,0x48,
- 0x61,0xF1,0xA0,0xD7,0xBD,0x04,0x24,0xF0,0xE4,0xD1,0x0A,0x4C,
- 0x28,0xDC,0x22,0x78,0x7C,0xED,0x2A,0xFA,0xF4,0x57,0x7C,0xAE,
- 0xDF,0x52,0xC6,0xA2,0x11,0x28,0xC5,0x3B,0xB8,0x2F,0x95,0x3F,
- 0x1E,0x05,0x66,0xFE,0x7D,0x1A,0x73,0xA0,0x45,0xF8,0xBB,0x8C,
- 0x64,0xB9,0xA9,0x4D,0x23,0xBE,0x20,0x60,0xA2,0xF7,0xC7,0xD8,
- 0xD8,0x49,0x28,0x9A,0x81,0xAC,0xF9,0x7F,0x3C,0xFC,0xBE,0x25,
- 0x5B,0x1D,0xB6,0xAB,0x08,0x06,0x11,0x8D,0x94,0x69,0x3C,0x68,
- 0x98,0x5A,0x90,0xF8,0xEB,0x19,0xCA,0x9F,0x1C,0x50,0x96,0x53,
- 0xEF,0xEC,0x1B,0x93,0x4F,0x53,0xB7,0xD9,0x04,0x8E,0x48,0x99,
- 0x6E,0x24,0xFF,0x66,0xF5,0xB0,0xDF,0x00,0xBA,0x22,0xE2,0xB6,
- 0xE3,0x3A,0xC2,0x95,0xB1,0x14,0x68,0xFB,0xA5,0x37,0x22,0x78,
- 0x56,0x5C,0xA4,0x23,0x31,0x02,0x97,0x7D,0xA9,0x84,0x0B,0x12,
- 0x26,0x58,0x2F,0x86,0x10,0xAD,0xB0,0xAB,0xB9,0x7B,0x05,0x9A,
- 0xDE,0x11,0xF1,0xE7,0x34,0xC7,0x95,0x42,0x1C,0x4F,0xA9,0xA8,
- 0x92,0xDF,0x3F,0x7B,
- };
-static unsigned char dh2048_g[]={
- 0x02,
-};
-
-
-static DH *get_dh2048()
-{
- DH *dh=NULL;
-
- if ((dh=DH_new()) == NULL) return(NULL);
-
- dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
- dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
-
- if ((dh->p == NULL) || (dh->g == NULL))
- return(NULL);
- return(dh);
-}
-#endif
-
-/*
- * Defined in Tls_Init to determine what kind of channels we are using
- * (old-style 8.2.0-8.3.1 or new-style 8.3.2+).
- */
-int channelTypeVersion;
-
-/*
- * We lose the tcl password callback when we use the RSA BSAFE SSL-C 1.1.2
- * libraries instead of the current OpenSSL libraries.
- */
-
-#ifdef BSAFE
-#define PRE_OPENSSL_0_9_4 1
-#endif
-
-/*
- * Pre OpenSSL 0.9.4 Compat
- */
-
-#ifndef STACK_OF
-#define STACK_OF(x) STACK
-#define sk_SSL_CIPHER_num(sk) sk_num((sk))
-#define sk_SSL_CIPHER_value( sk, index) (SSL_CIPHER*)sk_value((sk), (index))
-#endif
-
-/*
- * Thread-Safe TLS Code
- */
-
-#ifdef TCL_THREADS
-#define OPENSSL_THREAD_DEFINES
-#include <openssl/opensslconf.h>
-
-#ifdef OPENSSL_THREADS
-#include <openssl/crypto.h>
-
-/*
- * Threaded operation requires locking callbacks
- * Based from /crypto/cryptlib.c of OpenSSL and NSOpenSSL.
- */
-
-static Tcl_Mutex locks[CRYPTO_NUM_LOCKS];
-static Tcl_Mutex init_mx;
-static int initialized;
-
-static void CryptoThreadLockCallback (int mode, int n, const char *file, int line);
-static unsigned long CryptoThreadIdCallback (void);
-
-static void
-CryptoThreadLockCallback(int mode, int n, const char *file, int line)
-{
- if (mode & CRYPTO_LOCK) {
- Tcl_MutexLock(&locks[n]);
- } else {
- Tcl_MutexUnlock(&locks[n]);
- }
-}
-
-static unsigned long
-CryptoThreadIdCallback(void)
-{
- return (unsigned long) Tcl_GetCurrentThread();
-}
-#endif /* OPENSSL_THREADS */
-#endif /* TCL_THREADS */
-
-
-/*
- *-------------------------------------------------------------------
- *
- * InfoCallback --
- *
- * monitors SSL connection process
- *
- * Results:
- * None
- *
- * Side effects:
- * Calls callback (if defined)
- *-------------------------------------------------------------------
- */
-static void
-InfoCallback(CONST SSL *ssl, int where, int ret)
-{
- State *statePtr = (State*)SSL_get_app_data((SSL *)ssl);
- Tcl_Obj *cmdPtr;
- char *major; char *minor;
-
- if (statePtr->callback == (Tcl_Obj*)NULL)
- return;
-
- cmdPtr = Tcl_DuplicateObj(statePtr->callback);
-
-#if 0
- if (where & SSL_CB_ALERT) {
- sev = SSL_alert_type_string_long(ret);
- if (strcmp( sev, "fatal")==0) { /* Map to error */
- Tls_Error(statePtr, SSL_ERROR(ssl, 0));
- return;
- }
- }
-#endif
- if (where & SSL_CB_HANDSHAKE_START) {
- major = "handshake";
- minor = "start";
- } else if (where & SSL_CB_HANDSHAKE_DONE) {
- major = "handshake";
- minor = "done";
- } else {
- if (where & SSL_CB_ALERT) major = "alert";
- else if (where & SSL_ST_CONNECT) major = "connect";
- else if (where & SSL_ST_ACCEPT) major = "accept";
- else major = "unknown";
-
- if (where & SSL_CB_READ) minor = "read";
- else if (where & SSL_CB_WRITE) minor = "write";
- else if (where & SSL_CB_LOOP) minor = "loop";
- else if (where & SSL_CB_EXIT) minor = "exit";
- else minor = "unknown";
- }
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( "info", -1));
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( Tcl_GetChannelName(statePtr->self), -1) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( major, -1) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( minor, -1) );
-
- if (where & (SSL_CB_LOOP|SSL_CB_EXIT)) {
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( SSL_state_string_long(ssl), -1) );
- } else if (where & SSL_CB_ALERT) {
- CONST char *cp = (char *) SSL_alert_desc_string_long(ret);
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( cp, -1) );
- } else {
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( SSL_state_string_long(ssl), -1) );
- }
- Tcl_Preserve( (ClientData) statePtr->interp);
- Tcl_Preserve( (ClientData) statePtr);
-
- Tcl_IncrRefCount( cmdPtr);
- (void) Tcl_EvalObjEx(statePtr->interp, cmdPtr, TCL_EVAL_GLOBAL);
- Tcl_DecrRefCount( cmdPtr);
-
- Tcl_Release( (ClientData) statePtr);
- Tcl_Release( (ClientData) statePtr->interp);
-
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * VerifyCallback --
- *
- * Monitors SSL certificate validation process.
- * This is called whenever a certificate is inspected
- * or decided invalid.
- *
- * Results:
- * A callback bound to the socket may return one of:
- * 0 - the certificate is deemed invalid
- * 1 - the certificate is deemed valid
- * empty string - no change to certificate validation
- *
- * Side effects:
- * The err field of the currently operative State is set
- * to a string describing the SSL negotiation failure reason
- *-------------------------------------------------------------------
- */
-static int
-VerifyCallback(int ok, X509_STORE_CTX *ctx)
-{
- Tcl_Obj *cmdPtr, *result;
- char *errStr, *string;
- int length;
- SSL *ssl = (SSL*)X509_STORE_CTX_get_app_data(ctx);
- X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
- State *statePtr = (State*)SSL_get_app_data(ssl);
- int depth = X509_STORE_CTX_get_error_depth(ctx);
- int err = X509_STORE_CTX_get_error(ctx);
-
- dprintf(stderr, "Verify: %d\n", ok);
-
- if (!ok) {
- errStr = (char*)X509_verify_cert_error_string(err);
- } else {
- errStr = (char *)0;
- }
-
- if (statePtr->callback == (Tcl_Obj*)NULL) {
- if (statePtr->vflags & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
- return ok;
- } else {
- return 1;
- }
- }
- cmdPtr = Tcl_DuplicateObj(statePtr->callback);
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( "verify", -1));
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( Tcl_GetChannelName(statePtr->self), -1) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewIntObj( depth) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tls_NewX509Obj( statePtr->interp, cert) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewIntObj( ok) );
-
- Tcl_ListObjAppendElement( statePtr->interp, cmdPtr,
- Tcl_NewStringObj( errStr ? errStr : "", -1) );
-
- Tcl_Preserve( (ClientData) statePtr->interp);
- Tcl_Preserve( (ClientData) statePtr);
-
- statePtr->flags |= TLS_TCL_CALLBACK;
-
- Tcl_IncrRefCount( cmdPtr);
- if (Tcl_EvalObjEx(statePtr->interp, cmdPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
- /* It got an error - reject the certificate. */
- Tcl_BackgroundError( statePtr->interp);
- ok = 0;
- } else {
- result = Tcl_GetObjResult(statePtr->interp);
- string = Tcl_GetStringFromObj(result, &length);
- /* An empty result leaves verification unchanged. */
- if (length > 0) {
- if (Tcl_GetIntFromObj(statePtr->interp, result, &ok) != TCL_OK) {
- Tcl_BackgroundError(statePtr->interp);
- ok = 0;
- }
- }
- }
- Tcl_DecrRefCount( cmdPtr);
-
- statePtr->flags &= ~(TLS_TCL_CALLBACK);
-
- Tcl_Release( (ClientData) statePtr);
- Tcl_Release( (ClientData) statePtr->interp);
-
- return(ok); /* By default, leave verification unchanged. */
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * Tls_Error --
- *
- * Calls callback with $fd and $msg - so the callback can decide
- * what to do with errors.
- *
- * Side effects:
- * The err field of the currently operative State is set
- * to a string describing the SSL negotiation failure reason
- *-------------------------------------------------------------------
- */
-void
-Tls_Error(State *statePtr, char *msg)
-{
- Tcl_Obj *cmdPtr;
-
- if (msg && *msg) {
- Tcl_SetErrorCode(statePtr->interp, "SSL", msg, (char *)NULL);
- } else {
- msg = Tcl_GetStringFromObj(Tcl_GetObjResult(statePtr->interp), NULL);
- }
- statePtr->err = msg;
-
- if (statePtr->callback == (Tcl_Obj*)NULL) {
- char buf[BUFSIZ];
- sprintf(buf, "SSL channel \"%s\": error: %s",
- Tcl_GetChannelName(statePtr->self), msg);
- Tcl_SetResult( statePtr->interp, buf, TCL_VOLATILE);
- Tcl_BackgroundError( statePtr->interp);
- return;
- }
- cmdPtr = Tcl_DuplicateObj(statePtr->callback);
-
- Tcl_ListObjAppendElement(statePtr->interp, cmdPtr,
- Tcl_NewStringObj("error", -1));
-
- Tcl_ListObjAppendElement(statePtr->interp, cmdPtr,
- Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1));
-
- Tcl_ListObjAppendElement(statePtr->interp, cmdPtr,
- Tcl_NewStringObj(msg, -1));
-
- Tcl_Preserve((ClientData) statePtr->interp);
- Tcl_Preserve((ClientData) statePtr);
-
- Tcl_IncrRefCount(cmdPtr);
- if (Tcl_EvalObjEx(statePtr->interp, cmdPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
- Tcl_BackgroundError(statePtr->interp);
- }
- Tcl_DecrRefCount(cmdPtr);
-
- Tcl_Release((ClientData) statePtr);
- Tcl_Release((ClientData) statePtr->interp);
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * PasswordCallback --
- *
- * Called when a password is needed to unpack RSA and PEM keys.
- * Evals any bound password script and returns the result as
- * the password string.
- *-------------------------------------------------------------------
- */
-#ifdef PRE_OPENSSL_0_9_4
-/*
- * No way to handle user-data therefore no way without a global
- * variable to access the Tcl interpreter.
-*/
-static int
-PasswordCallback(char *buf, int size, int verify)
-{
- return -1;
-}
-#else
-static int
-PasswordCallback(char *buf, int size, int verify, void *udata)
-{
- State *statePtr = (State *) udata;
- Tcl_Interp *interp = statePtr->interp;
- Tcl_Obj *cmdPtr;
- int result;
-
- if (statePtr->password == NULL) {
- if (Tcl_EvalEx(interp, "tls::password", -1, TCL_EVAL_GLOBAL)
- == TCL_OK) {
- char *ret = (char *) Tcl_GetStringResult(interp);
- strncpy(buf, ret, (size_t) size);
- return (int)strlen(ret);
- } else {
- return -1;
- }
- }
-
- cmdPtr = Tcl_DuplicateObj(statePtr->password);
-
- Tcl_Preserve((ClientData) statePtr->interp);
- Tcl_Preserve((ClientData) statePtr);
-
- Tcl_IncrRefCount(cmdPtr);
- result = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL);
- if (result != TCL_OK) {
- Tcl_BackgroundError(statePtr->interp);
- }
- Tcl_DecrRefCount(cmdPtr);
-
- Tcl_Release((ClientData) statePtr);
- Tcl_Release((ClientData) statePtr->interp);
-
- if (result == TCL_OK) {
- char *ret = (char *) Tcl_GetStringResult(interp);
- strncpy(buf, ret, (size_t) size);
- return (int)strlen(ret);
- } else {
- return -1;
- }
-}
-#endif
-
-/*
- *-------------------------------------------------------------------
- *
- * CiphersObjCmd -- list available ciphers
- *
- * This procedure is invoked to process the "tls::ciphers" command
- * to list available ciphers, based upon protocol selected.
- *
- * Results:
- * A standard Tcl result list.
- *
- * Side effects:
- * constructs and destroys SSL context (CTX)
- *
- *-------------------------------------------------------------------
- */
-static int
-CiphersObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- static CONST84 char *protocols[] = {
- "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", NULL
- };
- enum protocol {
- TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_NONE
- };
- Tcl_Obj *objPtr;
- SSL_CTX *ctx = NULL;
- SSL *ssl = NULL;
- STACK_OF(SSL_CIPHER) *sk;
- char *cp, buf[BUFSIZ];
- int index, verbose = 0;
-
- if (objc < 2 || objc > 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "protocol ?verbose?");
- return TCL_ERROR;
- }
- if (Tcl_GetIndexFromObj( interp, objv[1], protocols, "protocol", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- if (objc > 2 && Tcl_GetBooleanFromObj( interp, objv[2],
- &verbose) != TCL_OK) {
- return TCL_ERROR;
- }
- switch ((enum protocol)index) {
- case TLS_SSL2:
-#if defined(NO_SSL2)
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return TCL_ERROR;
-#else
- ctx = SSL_CTX_new(SSLv2_method()); break;
-#endif
- case TLS_SSL3:
-#if defined(NO_SSL3)
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return TCL_ERROR;
-#else
- ctx = SSL_CTX_new(SSLv3_method()); break;
-#endif
- case TLS_TLS1:
-#if defined(NO_TLS1)
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return TCL_ERROR;
-#else
- ctx = SSL_CTX_new(TLSv1_method()); break;
-#endif
- case TLS_TLS1_1:
-#if defined(NO_TLS1_1)
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return TCL_ERROR;
-#else
- ctx = SSL_CTX_new(TLSv1_1_method()); break;
-#endif
- case TLS_TLS1_2:
-#if defined(NO_TLS1_2)
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return TCL_ERROR;
-#else
- ctx = SSL_CTX_new(TLSv1_2_method()); break;
-#endif
- default:
- break;
- }
- if (ctx == NULL) {
- Tcl_AppendResult(interp, REASON(), (char *) NULL);
- return TCL_ERROR;
- }
- ssl = SSL_new(ctx);
- if (ssl == NULL) {
- Tcl_AppendResult(interp, REASON(), (char *) NULL);
- SSL_CTX_free(ctx);
- return TCL_ERROR;
- }
- objPtr = Tcl_NewListObj( 0, NULL);
-
- if (!verbose) {
- for (index = 0; ; index++) {
- cp = (char*)SSL_get_cipher_list( ssl, index);
- if (cp == NULL) break;
- Tcl_ListObjAppendElement( interp, objPtr,
- Tcl_NewStringObj( cp, -1) );
- }
- } else {
- sk = SSL_get_ciphers(ssl);
-
- for (index = 0; index < sk_SSL_CIPHER_num(sk); index++) {
- register size_t i;
- SSL_CIPHER_description( sk_SSL_CIPHER_value( sk, index),
- buf, sizeof(buf));
- for (i = strlen(buf) - 1; i ; i--) {
- if (buf[i] == ' ' || buf[i] == '\n' ||
- buf[i] == '\r' || buf[i] == '\t') {
- buf[i] = '\0';
- } else {
- break;
- }
- }
- Tcl_ListObjAppendElement( interp, objPtr,
- Tcl_NewStringObj( buf, -1) );
- }
- }
- SSL_free(ssl);
- SSL_CTX_free(ctx);
-
- Tcl_SetObjResult( interp, objPtr);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * HandshakeObjCmd --
- *
- * This command is used to verify whether the handshake is complete
- * or not.
- *
- * Results:
- * A standard Tcl result. 1 means handshake complete, 0 means pending.
- *
- * Side effects:
- * May force SSL negotiation to take place.
- *
- *-------------------------------------------------------------------
- */
-
-static int
-HandshakeObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- Tcl_Channel chan; /* The channel to set a mode on. */
- State *statePtr; /* client state for ssl socket */
- int ret = 1;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channel");
- return TCL_ERROR;
- }
-
- chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL);
- if (chan == (Tcl_Channel) NULL) {
- return TCL_ERROR;
- }
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- /*
- * Make sure to operate on the topmost channel
- */
- chan = Tcl_GetTopChannel(chan);
- }
- if (Tcl_GetChannelType(chan) != Tls_ChannelType()) {
- Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan),
- "\": not a TLS channel", NULL);
- return TCL_ERROR;
- }
- statePtr = (State *)Tcl_GetChannelInstanceData(chan);
-
- if (!SSL_is_init_finished(statePtr->ssl)) {
- int err;
- ret = Tls_WaitForConnect(statePtr, &err);
- if ((statePtr->flags & TLS_TCL_ASYNC) && err == EAGAIN) {
- ret = 0;
- }
- if (ret < 0) {
- CONST char *errStr = statePtr->err;
- Tcl_ResetResult(interp);
- Tcl_SetErrno(err);
-
- if (!errStr || *errStr == 0) {
- errStr = Tcl_PosixError(interp);
- }
-
- Tcl_AppendResult(interp, "handshake failed: ", errStr,
- (char *) NULL);
- return TCL_ERROR;
- }
- }
-
- Tcl_SetObjResult(interp, Tcl_NewIntObj(ret));
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * ImportObjCmd --
- *
- * This procedure is invoked to process the "ssl" command
- *
- * The ssl command pushes SSL over a (newly connected) tcp socket
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May modify the behavior of an IO channel.
- *
- *-------------------------------------------------------------------
- */
-
-static int
-ImportObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- 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;
- 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;
-#ifndef OPENSSL_NO_TLSEXT
- char *servername = NULL; /* hostname for Server Name Indication */
-#endif
-#if defined(NO_SSL2)
- int ssl2 = 0;
-#else
- int ssl2 = 1;
-#endif
-#if defined(NO_SSL3)
- int ssl3 = 0;
-#else
- int ssl3 = 1;
-#endif
- int tls1 = 1;
- int tls1_1 = 1;
- int tls1_2 = 1;
- int proto = 0;
- int verify = 0, require = 0, request = 1;
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channel ?options?");
- return TCL_ERROR;
- }
-
- chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL);
- if (chan == (Tcl_Channel) NULL) {
- return TCL_ERROR;
- }
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- /*
- * Make sure to operate on the topmost channel
- */
- chan = Tcl_GetTopChannel(chan);
- }
-
- for (idx = 2; idx < objc; idx++) {
- char *opt = Tcl_GetStringFromObj(objv[idx], NULL);
-
- if (opt[0] != '-')
- break;
-
- OPTSTR( "-cadir", CAdir);
- OPTSTR( "-cafile", CAfile);
- OPTSTR( "-certfile", cert);
- OPTSTR( "-cipher", ciphers);
- OPTOBJ( "-command", script);
- OPTSTR( "-dhparams", DHparams);
- OPTSTR( "-keyfile", key);
- OPTSTR( "-model", model);
- OPTOBJ( "-password", password);
- OPTBOOL( "-require", require);
- OPTBOOL( "-request", request);
- OPTBOOL( "-server", server);
-#ifndef OPENSSL_NO_TLSEXT
- OPTSTR( "-servername", servername);
-#endif
-
- OPTBOOL( "-ssl2", ssl2);
- OPTBOOL( "-ssl3", ssl3);
- OPTBOOL( "-tls1", tls1);
- OPTBOOL( "-tls1.1", tls1_1);
- OPTBOOL( "-tls1.2", tls1_2);
-
- OPTBAD( "option", "-cadir, -cafile, -certfile, -cipher, -command, -dhparams, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1 or -tls1.2");
-
- return TCL_ERROR;
- }
- if (request) verify |= SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER;
- if (request && require) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- if (verify == 0) verify = SSL_VERIFY_NONE;
-
- proto |= (ssl2 ? TLS_PROTO_SSL2 : 0);
- proto |= (ssl3 ? TLS_PROTO_SSL3 : 0);
- proto |= (tls1 ? TLS_PROTO_TLS1 : 0);
- proto |= (tls1_1 ? TLS_PROTO_TLS1_1 : 0);
- proto |= (tls1_2 ? TLS_PROTO_TLS1_2 : 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;
-
- /* new SSL state */
- statePtr = (State *) ckalloc((unsigned) sizeof(State));
- memset(statePtr, 0, sizeof(State));
-
- statePtr->flags = flags;
- statePtr->interp = interp;
- statePtr->vflags = verify;
- statePtr->err = "";
-
- /* allocate script */
- if (script) {
- (void) Tcl_GetStringFromObj(script, &len);
- if (len) {
- statePtr->callback = script;
- Tcl_IncrRefCount(statePtr->callback);
- }
- }
-
- /* allocate password */
- if (password) {
- (void) Tcl_GetStringFromObj(password, &len);
- if (len) {
- statePtr->password = password;
- Tcl_IncrRefCount(statePtr->password);
- }
- }
-
- if (model != NULL) {
- int mode;
- /* Get the "model" context */
- chan = Tcl_GetChannel(interp, model, &mode);
- if (chan == (Tcl_Channel) NULL) {
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- /*
- * Make sure to operate on the topmost channel
- */
- chan = Tcl_GetTopChannel(chan);
- }
- if (Tcl_GetChannelType(chan) != Tls_ChannelType()) {
- Tcl_AppendResult(interp, "bad channel \"",
- Tcl_GetChannelName(chan), "\": not a TLS channel", NULL);
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
- ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx;
- } else {
- if ((ctx = CTX_Init(statePtr, proto, key, cert, CAdir, CAfile, ciphers,
- DHparams)) == (SSL_CTX*)0) {
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
- }
-
- statePtr->ctx = ctx;
-
- /*
- * We need to make sure that the channel works in binary (for the
- * encryption not to get goofed up).
- * We only want to adjust the buffering in pre-v2 channels, where
- * each channel in the stack maintained its own buffers.
- */
- Tcl_SetChannelOption(interp, chan, "-translation", "binary");
- if (channelTypeVersion == TLS_CHANNEL_VERSION_1) {
- Tcl_SetChannelOption(interp, chan, "-buffering", "none");
- }
-
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- statePtr->self = Tcl_StackChannel(interp, Tls_ChannelType(),
- (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE), chan);
- } else {
- statePtr->self = chan;
- Tcl_StackChannel(interp, Tls_ChannelType(),
- (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE), chan);
- }
- if (statePtr->self == (Tcl_Channel) NULL) {
- /*
- * No use of Tcl_EventuallyFree because no possible Tcl_Preserve.
- */
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
-
- /*
- * SSL Initialization
- */
-
- statePtr->ssl = SSL_new(statePtr->ctx);
- if (!statePtr->ssl) {
- /* SSL library error */
- Tcl_AppendResult(interp, "couldn't construct ssl session: ", REASON(),
- (char *) NULL);
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
-
-#ifndef OPENSSL_NO_TLSEXT
- if (servername) {
- if (!SSL_set_tlsext_host_name(statePtr->ssl, servername) && require) {
- Tcl_AppendResult(interp, "setting TLS host name extension failed",
- (char *) NULL);
- Tls_Free((char *) statePtr);
- return TCL_ERROR;
- }
- }
-#endif
-
- /*
- * SSL Callbacks
- */
-
- SSL_set_app_data(statePtr->ssl, (VOID *)statePtr); /* point back to us */
-
- SSL_set_verify(statePtr->ssl, verify, VerifyCallback);
-
- SSL_CTX_set_info_callback(statePtr->ctx, InfoCallback);
-
- /* Create Tcl_Channel BIO Handler */
- statePtr->p_bio = BIO_new_tcl(statePtr, BIO_CLOSE);
- statePtr->bio = BIO_new(BIO_f_ssl());
-
- if (server) {
- statePtr->flags |= TLS_TCL_SERVER;
- SSL_set_accept_state(statePtr->ssl);
- } else {
- SSL_set_connect_state(statePtr->ssl);
- }
- SSL_set_bio(statePtr->ssl, statePtr->p_bio, statePtr->p_bio);
- BIO_set_ssl(statePtr->bio, statePtr->ssl, BIO_NOCLOSE);
-
- /*
- * End of SSL Init
- */
- Tcl_SetResult(interp, (char *) Tcl_GetChannelName(statePtr->self),
- TCL_VOLATILE);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * UnimportObjCmd --
- *
- * This procedure is invoked to remove the topmost channel filter.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May modify the behavior of an IO channel.
- *
- *-------------------------------------------------------------------
- */
-
-static int
-UnimportObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- Tcl_Channel chan; /* The channel to set a mode on. */
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "channel");
- return TCL_ERROR;
- }
-
- chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
- if (chan == (Tcl_Channel) NULL) {
- return TCL_ERROR;
- }
-
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- /*
- * Make sure to operate on the topmost channel
- */
- chan = Tcl_GetTopChannel(chan);
- }
-
- if (Tcl_GetChannelType(chan) != Tls_ChannelType()) {
- Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan),
- "\": not a TLS channel", NULL);
- return TCL_ERROR;
- }
-
- if (Tcl_UnstackChannel(interp, chan) == TCL_ERROR) {
- return TCL_ERROR;
- }
-
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * CTX_Init -- construct a SSL_CTX instance
- *
- * Results:
- * A valid SSL_CTX instance or NULL.
- *
- * Side effects:
- * constructs SSL context (CTX)
- *
- *-------------------------------------------------------------------
- */
-
-static SSL_CTX *
-CTX_Init(statePtr, proto, key, cert, CAdir, CAfile, ciphers, DHparams)
- State *statePtr;
- int proto;
- char *key;
- char *cert;
- char *CAdir;
- char *CAfile;
- char *ciphers;
- char *DHparams;
-{
- Tcl_Interp *interp = statePtr->interp;
- SSL_CTX *ctx = NULL;
- Tcl_DString ds;
- Tcl_DString ds1;
- int off = 0;
- const SSL_METHOD *method;
-
- if (!proto) {
- Tcl_AppendResult(interp, "no valid protocol selected", NULL);
- return (SSL_CTX *)0;
- }
-
- /* create SSL context */
-#if defined(NO_SSL2)
- if (ENABLED(proto, TLS_PROTO_SSL2)) {
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return (SSL_CTX *)0;
- }
-#endif
-#if defined(NO_SSL3)
- if (ENABLED(proto, TLS_PROTO_SSL3)) {
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return (SSL_CTX *)0;
- }
-#endif
-#if defined(NO_TLS1)
- if (ENABLED(proto, TLS_PROTO_TLS1)) {
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return (SSL_CTX *)0;
- }
-#endif
-#if defined(NO_TLS1_1)
- if (ENABLED(proto, TLS_PROTO_TLS1_1)) {
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return (SSL_CTX *)0;
- }
-#endif
-#if defined(NO_TLS1_2)
- if (ENABLED(proto, TLS_PROTO_TLS1_2)) {
- Tcl_AppendResult(interp, "protocol not supported", NULL);
- return (SSL_CTX *)0;
- }
-#endif
-
- switch (proto) {
-#if !defined(NO_SSL2)
- case TLS_PROTO_SSL2:
- method = SSLv2_method ();
- break;
-#endif
-#if !defined(NO_SSL3)
- case TLS_PROTO_SSL3:
- method = SSLv3_method ();
- break;
-#endif
-#if !defined(NO_TLS1)
- case TLS_PROTO_TLS1:
- method = TLSv1_method ();
- break;
-#endif
-#if !defined(NO_TLS1_1)
- case TLS_PROTO_TLS1_1:
- method = TLSv1_1_method ();
- break;
-#endif
-#if !defined(NO_TLS1_2)
- case TLS_PROTO_TLS1_2:
- method = TLSv1_2_method ();
- break;
-#endif
- default:
- method = SSLv23_method ();
-#if !defined(NO_SSL2)
- off |= (ENABLED(proto, TLS_PROTO_SSL2) ? 0 : SSL_OP_NO_SSLv2);
-#endif
-#if !defined(NO_SSL3)
- off |= (ENABLED(proto, TLS_PROTO_SSL3) ? 0 : SSL_OP_NO_SSLv3);
-#endif
-#if !defined(NO_TLS1)
- off |= (ENABLED(proto, TLS_PROTO_TLS1) ? 0 : SSL_OP_NO_TLSv1);
-#endif
-#if !defined(NO_TLS1_1)
- off |= (ENABLED(proto, TLS_PROTO_TLS1_1) ? 0 : SSL_OP_NO_TLSv1_1);
-#endif
-#if !defined(NO_TLS1_2)
- off |= (ENABLED(proto, TLS_PROTO_TLS1_2) ? 0 : SSL_OP_NO_TLSv1_2);
-#endif
- break;
- }
-
- ctx = SSL_CTX_new (method);
-
- SSL_CTX_set_app_data( ctx, (VOID*)interp); /* remember the interpreter */
- SSL_CTX_set_options( ctx, SSL_OP_ALL); /* all SSL bug workarounds */
- SSL_CTX_set_options( ctx, off); /* all SSL bug workarounds */
- SSL_CTX_sess_set_cache_size( ctx, 128);
-
- if (ciphers != NULL)
- SSL_CTX_set_cipher_list(ctx, ciphers);
-
- /* set some callbacks */
- SSL_CTX_set_default_passwd_cb(ctx, PasswordCallback);
-
-#ifndef BSAFE
- SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)statePtr);
-#endif
-
- /* read a Diffie-Hellman parameters file, or use the built-in one */
-#ifdef OPENSSL_NO_DH
- if (DHparams != NULL) {
- Tcl_AppendResult(interp,
- "DH parameter support not available", (char *) NULL);
- SSL_CTX_free(ctx);
- return (SSL_CTX *)0;
- }
-#else
- {
- DH* dh;
- if (DHparams != NULL) {
- BIO *bio;
- Tcl_DStringInit(&ds);
- bio = BIO_new_file(F2N(DHparams, &ds), "r");
- if (!bio) {
- Tcl_DStringFree(&ds);
- Tcl_AppendResult(interp,
- "Could not find DH parameters file", (char *) NULL);
- SSL_CTX_free(ctx);
- return (SSL_CTX *)0;
- }
-
- dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- BIO_free(bio);
- Tcl_DStringFree(&ds);
- if (!dh) {
- Tcl_AppendResult(interp,
- "Could not read DH parameters from file", (char *) NULL);
- SSL_CTX_free(ctx);
- return (SSL_CTX *)0;
- }
- } else {
- dh = get_dh2048();
- }
- SSL_CTX_set_tmp_dh(ctx, dh);
- 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);
- /* 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
- }
- }
-
- 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
- Tcl_DStringFree(&ds);
- Tcl_DStringFree(&ds1);
- /* Don't currently care if this fails */
- Tcl_AppendResult(interp, "SSL default verify paths: ",
- REASON(), (char *) NULL);
- SSL_CTX_free(ctx);
- return (SSL_CTX *)0;
-#endif
- }
-
- /* https://sourceforge.net/p/tls/bugs/57/ */
- 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 );
- }
- }
-
- Tcl_DStringFree(&ds);
- Tcl_DStringFree(&ds1);
- return ctx;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * StatusObjCmd -- return certificate for connected peer.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------
- */
-static int
-StatusObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- State *statePtr;
- X509 *peer;
- Tcl_Obj *objPtr;
- Tcl_Channel chan;
- char *channelName, *ciphers;
- int mode;
-
- switch (objc) {
- case 2:
- channelName = Tcl_GetStringFromObj(objv[1], NULL);
- break;
-
- case 3:
- if (!strcmp (Tcl_GetString (objv[1]), "-local")) {
- channelName = Tcl_GetStringFromObj(objv[2], NULL);
- break;
- }
- /* else fall... */
- default:
- Tcl_WrongNumArgs(interp, 1, objv, "?-local? channel");
- return TCL_ERROR;
- }
-
- chan = Tcl_GetChannel(interp, channelName, &mode);
- if (chan == (Tcl_Channel) NULL) {
- return TCL_ERROR;
- }
- if (channelTypeVersion == TLS_CHANNEL_VERSION_2) {
- /*
- * Make sure to operate on the topmost channel
- */
- chan = Tcl_GetTopChannel(chan);
- }
- if (Tcl_GetChannelType(chan) != Tls_ChannelType()) {
- Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan),
- "\": not a TLS channel", NULL);
- return TCL_ERROR;
- }
- statePtr = (State *) Tcl_GetChannelInstanceData(chan);
- if (objc == 2) {
- peer = SSL_get_peer_certificate(statePtr->ssl);
- } else {
- peer = SSL_get_certificate(statePtr->ssl);
- }
- if (peer) {
- objPtr = Tls_NewX509Obj(interp, peer);
- if (objc == 2) { X509_free(peer); }
- } else {
- objPtr = Tcl_NewListObj(0, NULL);
- }
-
- Tcl_ListObjAppendElement (interp, objPtr,
- Tcl_NewStringObj ("sbits", -1));
- Tcl_ListObjAppendElement (interp, objPtr,
- Tcl_NewIntObj (SSL_get_cipher_bits (statePtr->ssl, NULL)));
-
- ciphers = (char*)SSL_get_cipher(statePtr->ssl);
- if (ciphers != NULL && strcmp(ciphers, "(NONE)")!=0) {
- Tcl_ListObjAppendElement(interp, objPtr,
- Tcl_NewStringObj("cipher", -1));
- Tcl_ListObjAppendElement(interp, objPtr,
- Tcl_NewStringObj(SSL_get_cipher(statePtr->ssl), -1));
- }
- Tcl_SetObjResult( interp, objPtr);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * VersionObjCmd -- return version string from OpenSSL.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------
- */
-static int
-VersionObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- Tcl_Obj *objPtr;
-
- objPtr = Tcl_NewStringObj(OPENSSL_VERSION_TEXT, -1);
-
- Tcl_SetObjResult(interp, objPtr);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * MiscObjCmd -- misc commands
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------
- */
-static int
-MiscObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Not used. */
- Tcl_Interp *interp;
- int objc;
- Tcl_Obj *CONST objv[];
-{
- static CONST84 char *commands [] = { "req", NULL };
- enum command { C_REQ, C_DUMMY };
- int cmd;
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?");
- return TCL_ERROR;
- }
- if (Tcl_GetIndexFromObj(interp, objv[1], commands,
- "command", 0,&cmd) != TCL_OK) {
- return TCL_ERROR;
- }
-
- switch ((enum command) cmd) {
- case C_REQ: {
- EVP_PKEY *pkey=NULL;
- X509 *cert=NULL;
- X509_NAME *name=NULL;
- Tcl_Obj **listv;
- int listc,i;
-
- BIO *out=NULL;
-
- char *k_C="",*k_ST="",*k_L="",*k_O="",*k_OU="",*k_CN="",*k_Email="";
- char *keyout,*pemout,*str;
- int keysize,serial=0,days=365;
-
- if ((objc<5) || (objc>6)) {
- Tcl_WrongNumArgs(interp, 2, objv, "keysize keyfile certfile ?info?");
- return TCL_ERROR;
- }
-
- if (Tcl_GetIntFromObj(interp, objv[2], &keysize) != TCL_OK) {
- return TCL_ERROR;
- }
- keyout=Tcl_GetString(objv[3]);
- pemout=Tcl_GetString(objv[4]);
-
- if (objc>=6) {
- if (Tcl_ListObjGetElements(interp, objv[5],
- &listc, &listv) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if ((listc%2) != 0) {
- Tcl_SetResult(interp,"Information list must have even number of arguments",NULL);
- return TCL_ERROR;
- }
- for (i=0; i<listc; i+=2) {
- str=Tcl_GetString(listv[i]);
- if (strcmp(str,"days")==0) {
- if (Tcl_GetIntFromObj(interp,listv[i+1],&days)!=TCL_OK)
- return TCL_ERROR;
- } else if (strcmp(str,"serial")==0) {
- if (Tcl_GetIntFromObj(interp,listv[i+1],&serial)!=TCL_OK)
- return TCL_ERROR;
- } else if (strcmp(str,"serial")==0) {
- if (Tcl_GetIntFromObj(interp,listv[i+1],&serial)!=TCL_OK)
- return TCL_ERROR;
- } else if (strcmp(str,"C")==0) {
- k_C=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"ST")==0) {
- k_ST=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"L")==0) {
- k_L=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"O")==0) {
- k_O=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"OU")==0) {
- k_OU=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"CN")==0) {
- k_CN=Tcl_GetString(listv[i+1]);
- } else if (strcmp(str,"Email")==0) {
- k_Email=Tcl_GetString(listv[i+1]);
- } else {
- Tcl_SetResult(interp,"Unknown parameter",NULL);
- return TCL_ERROR;
- }
- }
- }
- if ((pkey = EVP_PKEY_new()) != NULL) {
- if (!EVP_PKEY_assign_RSA(pkey,
- RSA_generate_key(keysize, 0x10001, NULL, NULL))) {
- Tcl_SetResult(interp,"Error generating private key",NULL);
- EVP_PKEY_free(pkey);
- return TCL_ERROR;
- }
- out=BIO_new(BIO_s_file());
- BIO_write_filename(out,keyout);
- PEM_write_bio_PrivateKey(out,pkey,NULL,NULL,0,NULL,NULL);
- BIO_free_all(out);
-
- if ((cert=X509_new())==NULL) {
- Tcl_SetResult(interp,"Error generating certificate request",NULL);
- EVP_PKEY_free(pkey);
- return(TCL_ERROR);
- }
-
- X509_set_version(cert,2);
- ASN1_INTEGER_set(X509_get_serialNumber(cert),serial);
- X509_gmtime_adj(X509_get_notBefore(cert),0);
- X509_gmtime_adj(X509_get_notAfter(cert),(long)60*60*24*days);
- X509_set_pubkey(cert,pkey);
-
- name=X509_get_subject_name(cert);
-
- X509_NAME_add_entry_by_txt(name,"C", MBSTRING_ASC, k_C, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"ST", MBSTRING_ASC, k_ST, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"L", MBSTRING_ASC, k_L, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"O", MBSTRING_ASC, k_O, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"OU", MBSTRING_ASC, k_OU, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, k_CN, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name,"Email", MBSTRING_ASC, k_Email, -1, -1, 0);
-
- X509_set_subject_name(cert,name);
-
- if (!X509_sign(cert,pkey,EVP_md5())) {
- X509_free(cert);
- EVP_PKEY_free(pkey);
- Tcl_SetResult(interp,"Error signing certificate",NULL);
- return TCL_ERROR;
- }
-
- out=BIO_new(BIO_s_file());
- BIO_write_filename(out,pemout);
-
- PEM_write_bio_X509(out,cert);
- BIO_free_all(out);
-
- X509_free(cert);
- EVP_PKEY_free(pkey);
- } else {
- Tcl_SetResult(interp,"Error generating private key",NULL);
- return TCL_ERROR;
- }
- }
- break;
- default:
- break;
- }
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * Tls_Free --
- *
- * This procedure cleans up when a SSL socket based channel
- * is closed and its reference count falls below 1
- *
- * Results:
- * none
- *
- * Side effects:
- * Frees all the state
- *
- *-------------------------------------------------------------------
- */
-void
-Tls_Free( char *blockPtr )
-{
- State *statePtr = (State *)blockPtr;
-
- Tls_Clean(statePtr);
- ckfree(blockPtr);
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * Tls_Clean --
- *
- * This procedure cleans up when a SSL socket based channel
- * is closed and its reference count falls below 1. This should
- * be called synchronously by the CloseProc, not in the
- * EventuallyFree callback.
- *
- * Results:
- * none
- *
- * Side effects:
- * Frees all the state
- *
- *-------------------------------------------------------------------
- */
-void
-Tls_Clean(State *statePtr)
-{
- /*
- * we're assuming here that we're single-threaded
- */
-
- if (statePtr->timer != (Tcl_TimerToken) NULL) {
- Tcl_DeleteTimerHandler(statePtr->timer);
- statePtr->timer = NULL;
- }
-
- if (statePtr->bio) {
- /* This will call SSL_shutdown. Bug 1414045 */
- dprintf(stderr, "BIO_free_all(%p)\n", statePtr->bio);
- BIO_free_all(statePtr->bio);
- statePtr->bio = NULL;
- }
- if (statePtr->ssl) {
- dprintf(stderr, "SSL_free(%p)\n", statePtr->ssl);
- SSL_free(statePtr->ssl);
- statePtr->ssl = NULL;
- }
- if (statePtr->ctx) {
- SSL_CTX_free(statePtr->ctx);
- statePtr->ctx = NULL;
- }
- if (statePtr->callback) {
- Tcl_DecrRefCount(statePtr->callback);
- statePtr->callback = NULL;
- }
- if (statePtr->password) {
- Tcl_DecrRefCount(statePtr->password);
- statePtr->password = NULL;
- }
-}
-
-/*
- *-------------------------------------------------------------------
- *
- * Tls_Init --
- *
- * This is a package initialization procedure, which is called
- * by Tcl when this package is to be added to an interpreter.
- *
- * Results: Ssl configured and loaded
- *
- * Side effects:
- * create the ssl command, initialise ssl context
- *
- *-------------------------------------------------------------------
- */
-
-int
-Tls_Init(Tcl_Interp *interp) /* Interpreter in which the package is
- * to be made available. */
-{
- int major, minor, patchlevel, release;
-
- /*
- * The original 8.2.0 stacked channel implementation (and the patch
- * that preceded it) had problems with scalability and robustness.
- * These were address in 8.3.2 / 8.4a2, so we now require that as a
- * minimum for TLS 1.4+. We only support 8.2+ now (8.3.2+ preferred).
- */
- if (
-#ifdef USE_TCL_STUBS
- Tcl_InitStubs(interp, "8.2", 0)
-#else
- Tcl_PkgRequire(interp, "Tcl", "8.2", 0)
-#endif
- == NULL) {
- return TCL_ERROR;
- }
-
- /*
- * Get the version so we can runtime switch on available functionality.
- * TLS should really only be used in 8.3.2+, but the other works for
- * some limited functionality, so an attempt at support is made.
- */
- Tcl_GetVersion(&major, &minor, &patchlevel, &release);
- if ((major > 8) || ((major == 8) && ((minor > 3) || ((minor == 3) &&
- (release == TCL_FINAL_RELEASE) && (patchlevel >= 2))))) {
- /* 8.3.2+ */
- channelTypeVersion = TLS_CHANNEL_VERSION_2;
- } else {
- /* 8.2.0 - 8.3.1 */
- channelTypeVersion = TLS_CHANNEL_VERSION_1;
- }
-
- if (TlsLibInit() != TCL_OK) {
- Tcl_AppendResult(interp, "could not initialize SSL library", NULL);
- return TCL_ERROR;
- }
-
- Tcl_CreateObjCommand(interp, "tls::ciphers", CiphersObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::handshake", HandshakeObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::import", ImportObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::unimport", UnimportObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::version", VersionObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd,
- (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
-
- return Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION);
-}
-
-/*
- *------------------------------------------------------*
- *
- * Tls_SafeInit --
- *
- * ------------------------------------------------*
- * Standard procedure required by 'load'.
- * Initializes this extension for a safe interpreter.
- * ------------------------------------------------*
- *
- * Sideeffects:
- * As of 'Tls_Init'
- *
- * Result:
- * A standard Tcl error code.
- *
- *------------------------------------------------------*
- */
-
-int
-Tls_SafeInit (Tcl_Interp* interp)
-{
- return Tls_Init (interp);
-}
-
-
-/*
- *------------------------------------------------------*
- *
- * TlsLibInit --
- *
- * ------------------------------------------------*
- * Initializes SSL library once per application
- * ------------------------------------------------*
- *
- * Side effects:
- * initilizes SSL library
- *
- * Result:
- * none
- *
- *------------------------------------------------------*
- */
-static int
-TlsLibInit ()
-{
- int i;
- char rnd_seed[16] = "GrzSlplKqUdnnzP!"; /* 16 bytes */
-#if defined(OPENSSL_THREADS) && defined(TCL_THREADS)
- size_t num_locks;
-#endif
- int status=TCL_OK;
-
- if (!initialized) {
- Tcl_MutexLock(&init_mx);
- if (!initialized) {
- initialized = 1;
-
- if (CRYPTO_set_mem_functions((void *(*)(size_t))Tcl_Alloc,
- (void *(*)(void *, size_t))Tcl_Realloc,
- (void(*)(void *))Tcl_Free) == 0) {
- /* Not using Tcl's mem functions ... not critical */
- }
-
-#if defined(OPENSSL_THREADS) && defined(TCL_THREADS)
- /* should we consider allocating mutexes? */
- num_locks = CRYPTO_num_locks();
- if (num_locks > CRYPTO_NUM_LOCKS) {
- status=TCL_ERROR;
- goto done;
- }
-
- CRYPTO_set_locking_callback(CryptoThreadLockCallback);
- CRYPTO_set_id_callback(CryptoThreadIdCallback);
-#endif
-
- if (SSL_library_init() != 1) {
- status=TCL_ERROR;
- goto done;
- }
- SSL_load_error_strings();
- ERR_load_crypto_strings();
-
- /*
- * Seed the random number generator in the SSL library,
- * using the do/while construct because of the bug note in the
- * OpenSSL FAQ at http://www.openssl.org/support/faq.html#USER1
- *
- * The crux of the problem is that Solaris 7 does not have a
- * /dev/random or /dev/urandom device so it cannot gather enough
- * entropy from the RAND_seed() when TLS initializes and refuses
- * to go further. Earlier versions of OpenSSL carried on regardless.
- */
- srand((unsigned int) time((time_t *) NULL));
- do {
- for (i = 0; i < 16; i++) {
- rnd_seed[i] = 1 + (char) (255.0 * rand()/(RAND_MAX+1.0));
- }
- RAND_seed(rnd_seed, sizeof(rnd_seed));
- } while (RAND_status() != 1);
- }
- done:
-
- Tcl_MutexUnlock(&init_mx);
- }
- return status;
-}