Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -1452,11 +1452,11 @@ Tcl_GetChannelOption(interp, chan, "-blocking", &upperChannelBlocking); /* Ensure the channel works in binary mode (for the encryption not to get goofed up). */ Tcl_SetChannelOption(interp, chan, "-translation", "binary"); Tcl_SetChannelOption(interp, chan, "-blocking", "true"); - + /* Create stacked channel */ dprintf("Consuming Tcl channel %s", Tcl_GetChannelName(chan)); statePtr->self = Tcl_StackChannel(interp, Tls_ChannelType(), (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE), chan); dprintf("Created channel named %s", Tcl_GetChannelName(statePtr->self)); @@ -1743,10 +1743,14 @@ Tcl_DStringFree(&upperChannelEncoding); Tcl_DStringFree(&upperChannelEOFChar); Tcl_DStringFree(&upperChannelBlocking); return res; } + +static int +TlsLoadClientCAFileFromMemory(Tcl_Interp *interp, SSL_CTX *ctx, const Tcl_Obj *file); + /* *------------------------------------------------------------------- * * CTX_Init -- construct a SSL_CTX instance @@ -2101,38 +2105,162 @@ if (!SSL_CTX_load_verify_dir(ctx, F2N(CApath, &ds))) { abort++; } Tcl_DStringFree(&ds); } - + /* Set URI for to a store, which may be a single container or a catalog of containers. */ if (CAstore != NULL) { if (!SSL_CTX_load_verify_store(ctx, F2N(CAstore, &ds))) { abort++; } Tcl_DStringFree(&ds); } - + /* Set file of CA certificates in PEM format. */ if (CAfile != NULL) { - if (!SSL_CTX_load_verify_file(ctx, F2N(CAfile, &ds))) { - abort++; - } - Tcl_DStringFree(&ds); - - /* Set list of CAs to send to client when requesting a client certificate */ - 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_Obj *cafileobj = Tcl_NewStringObj(CAfile, -1); + Tcl_IncrRefCount(cafileobj); + + Tcl_Obj *fsinfo = Tcl_FSFileSystemInfo(cafileobj); + if (fsinfo) { + Tcl_IncrRefCount(fsinfo); + + Tcl_Obj *fstype = NULL; + Tcl_ListObjIndex(interp, fsinfo, 0, &fstype); + + if (Tcl_StringMatch("native", Tcl_GetString(fstype))) { + if (!SSL_CTX_load_verify_file(ctx, F2N(CAfile, &ds))) { + abort++; + } + Tcl_DStringFree(&ds); + + /* Set list of CAs to send to client when requesting a client certificate */ + 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); + + } else { + /* Load certificate into memory */ + if (!TlsLoadClientCAFileFromMemory(interp, ctx, cafileobj)) { + abort++; + } + } + Tcl_DecrRefCount(fsinfo); + + } else { + abort++; /* Path is not recognized */ + } + Tcl_DecrRefCount(cafileobj); } #endif } return ctx; } + +static int +TlsLoadClientCAFileFromMemory(Tcl_Interp *interp, SSL_CTX *ctx, const Tcl_Obj *file) { + BIO *bio = NULL; + X509 *cert = NULL; + X509_STORE *store = NULL; + Tcl_Obj *buf = NULL; + const void *data = NULL; + X509_NAME *name = NULL; + X509_NAME *name_copy = NULL; + STACK_OF(X509_NAME) *certNames = NULL; + int ret = 0; + int len = 0; + + /* Read file into memory */ + Tcl_Channel in = Tcl_FSOpenFileChannel(interp, file, "r", 0); + if (in == NULL) { + goto cleanup; + } + Tcl_SetChannelOption(interp, in, "-encoding", "binary"); + buf = Tcl_NewObj(); + Tcl_IncrRefCount(buf); + + if (Tcl_ReadChars(in, buf, -1, 0) < 0) { + Tcl_Close(interp, in); + goto cleanup; + } + Tcl_Close(interp, in); + + data = (const void *) Tcl_GetByteArrayFromObj(buf, &len); + bio = BIO_new_mem_buf(data, len); + if (bio == NULL) { + goto cleanup; + } + + /* Where the certs go */ + store = SSL_CTX_get_cert_store(ctx); + if (store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { + goto cleanup; + } + } + + /* Where the CA names go */ + certNames = sk_X509_NAME_new_null(); + if (!certNames) { + goto cleanup; + } + + /* Attempt to load all certs from the PEM file */ + while ((cert = PEM_read_bio_X509(bio, NULL, 0, NULL)) != NULL) { + if (X509_STORE_add_cert(store, cert) == 0) { + X509_free(cert); + ret = 0; + goto cleanup; + } + /* Copy name to stack before certificate gets freed */ + name = X509_get_subject_name(cert); + if (name) { + X509_NAME *name_copy = X509_NAME_dup(name); + if (!name_copy || !sk_X509_NAME_push(certNames, name_copy)) { + X509_free(cert); + ret = 0; + goto cleanup; + } + } + X509_free(cert); + ret ++; + } + + /* At least one cert was added so retain the store and CA list */ + if (ret) { + if (SSL_CTX_get_cert_store(ctx) == NULL) { + SSL_CTX_set_cert_store(ctx, store); + } + SSL_CTX_set_client_CA_list(ctx, certNames); + } + + cleanup: + + if (! ret) { + /* New store is not required */ + if (store != SSL_CTX_get_cert_store(ctx)) { + X509_STORE_free(store); + } + /* Cert names will not be used */ + if (certNames) { + sk_X509_NAME_pop_free(certNames, X509_NAME_free); + } + } + + BIO_free(bio); + + if (buf) + Tcl_DecrRefCount(buf); + + return ret; +} + /* *------------------------------------------------------------------- * * StatusObjCmd -- return certificate for connected peer info. @@ -2314,11 +2442,11 @@ const unsigned char *proto; unsigned int ulen; /* Initialization finished */ LAPPEND_BOOL(interp, objPtr, "init_finished", SSL_is_init_finished(ssl)); - + /* connection state */ LAPPEND_STR(interp, objPtr, "state", SSL_state_string_long(ssl), -1); /* Get SNI requested server name */ LAPPEND_STR(interp, objPtr, "servername", SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name), -1); @@ -3004,11 +3132,11 @@ return TCL_ERROR; } /* Create BIO handlers */ BIO_new_tcl(NULL, 0); - + /* Create exit handler */ Tcl_CreateExitHandler(TlsLibShutdown, NULL); initialized = 1; } return TCL_OK; DELETED manifest.uuid Index: manifest.uuid ================================================================== --- manifest.uuid +++ /dev/null @@ -1,1 +0,0 @@ -git-