Check-in [cdb6cd6aa6]
EuroTcl/OpenACS 11 - 12 JULY 2024, VIENNA
Overview
Comment:Refactored set default CA certificates path and file to allow env vars to override defaults.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: cdb6cd6aa63836f0494b0267f74b41ef5cb4bbe4cfbceafdb2c2dd0c91de73c6
User & Date: bohagan on 2024-02-10 20:37:51
Other Links: manifest | tags
Context
2024-02-10
21:04
Updated debug documentation check-in: e8ed4fea49 user: bohagan tags: trunk
20:37
Refactored set default CA certificates path and file to allow env vars to override defaults. check-in: cdb6cd6aa6 user: bohagan tags: trunk
03:37
Updated test cases for OpenSSL 3.0 check-in: 6fd974ebf0 user: bohagan tags: trunk
Changes

Modified generic/tls.c from [675528fb5b] to [0fd30b2572].

44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58







-
+








#define F2N(key, dsp) \
	(((key) == NULL) ? (char *) NULL : \
		Tcl_TranslateFileName(interp, (key), (dsp)))

static SSL_CTX *CTX_Init(State *statePtr, int isServer, int proto, char *key,
		char *certfile, unsigned char *key_asn1, unsigned char *cert_asn1,
		int key_asn1_len, int cert_asn1_len, char *CAdir, char *CAfile,
		int key_asn1_len, int cert_asn1_len, char *CApath, char *CAfile,
		char *ciphers, char *ciphersuites, int level, char *DHparams);

static int	TlsLibInit(int uninitialize);

#define TLS_PROTO_SSL2		0x01
#define TLS_PROTO_SSL3		0x02
#define TLS_PROTO_TLS1		0x04
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256




1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274















1275
1276
1277
1278
1279
1280
1281
1246
1247
1248
1249
1250
1251
1252




1253
1254
1255
1256
1257
1258
1259















1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281







-
-
-
-
+
+
+
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 *
 *-------------------------------------------------------------------
 */
static int
ImportObjCmd(ClientData clientData, 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;
    Tcl_Obj *vcmd	        = NULL;
    SSL_CTX *ctx		= NULL;
    Tcl_Obj *script		= NULL;
    Tcl_Obj *password		= NULL;
    Tcl_Obj *vcmd		= NULL;
    Tcl_DString upperChannelTranslation, upperChannelBlocking, upperChannelEncoding, upperChannelEOFChar;
    int idx;
    Tcl_Size len;
    int flags		        = TLS_TCL_INIT;
    int server		        = 0;	/* is connection incoming or outgoing? */
    char *keyfile	        = NULL;
    char *certfile	        = NULL;
    unsigned char *key  	= NULL;
    Tcl_Size key_len                 = 0;
    unsigned char *cert         = NULL;
    Tcl_Size cert_len                = 0;
    char *ciphers	        = NULL;
    char *ciphersuites	        = NULL;
    char *CAfile	        = NULL;
    char *CAdir		        = NULL;
    char *DHparams	        = NULL;
    char *model		        = NULL;
    char *servername	        = NULL;	/* hostname for Server Name Indication */
    int flags			= TLS_TCL_INIT;
    int server			= 0;	/* is connection incoming or outgoing? */
    char *keyfile		= NULL;
    char *certfile		= NULL;
    unsigned char *key		= NULL;
    Tcl_Size key_len		= 0;
    unsigned char *cert		= NULL;
    Tcl_Size cert_len		= 0;
    char *ciphers		= NULL;
    char *ciphersuites		= NULL;
    char *CAfile		= NULL;
    char *CApath		= NULL;
    char *DHparams		= NULL;
    char *model			= NULL;
    char *servername		= NULL;	/* hostname for Server Name Indication */
    const unsigned char *session_id = NULL;
    Tcl_Obj *alpn		= NULL;
    int ssl2 = 0, ssl3 = 0;
    int tls1 = 1, tls1_1 = 1, tls1_2 = 1, tls1_3 = 1;
    int proto = 0, level = -1;
    int verify = 0, require = 0, request = 1, post_handshake = 0;
    (void) clientData;
1313
1314
1315
1316
1317
1318
1319
1320

1321
1322
1323
1324
1325
1326
1327
1313
1314
1315
1316
1317
1318
1319

1320
1321
1322
1323
1324
1325
1326
1327







-
+







    for (idx = 2; idx < objc; idx++) {
	char *opt = Tcl_GetStringFromObj(objv[idx], (Tcl_Size *)NULL);

	if (opt[0] != '-')
	    break;

	OPTOBJ("-alpn", alpn);
	OPTSTR("-cadir", CAdir);
	OPTSTR("-cadir", CApath);
	OPTSTR("-cafile", CAfile);
	OPTBYTE("-cert", cert, cert_len);
	OPTSTR("-certfile", certfile);
	OPTSTR("-cipher", ciphers);
	OPTSTR("-ciphers", ciphers);
	OPTSTR("-ciphersuites", ciphersuites);
	OPTOBJ("-command", script);
1366
1367
1368
1369
1370
1371
1372
1373

1374
1375
1376
1377
1378
1379
1380
1366
1367
1368
1369
1370
1371
1372

1373
1374
1375
1376
1377
1378
1379
1380







-
+







    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 (ciphersuites && !*ciphersuites) ciphersuites    = NULL;
    if (CAfile && !*CAfile)	        CAfile	        = NULL;
    if (CAdir && !*CAdir)	        CAdir	        = NULL;
    if (CApath && !*CApath)	        CApath	        = NULL;
    if (DHparams && !*DHparams)	        DHparams        = NULL;

    /* new SSL state */
    statePtr		= (State *) ckalloc((unsigned) sizeof(State));
    memset(statePtr, 0, sizeof(State));

    statePtr->flags	= flags;
1428
1429
1430
1431
1432
1433
1434
1435

1436
1437
1438
1439
1440
1441
1442
1428
1429
1430
1431
1432
1433
1434

1435
1436
1437
1438
1439
1440
1441
1442







-
+







	    Tcl_SetErrorCode(interp, "TLS", "IMPORT", "CHANNEL", "INVALID", (char *) NULL);
	    Tls_Free((char *) statePtr);
	    return TCL_ERROR;
	}
	ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx;
    } else {
	if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, cert, (int) key_len,
	    (int) cert_len, CAdir, CAfile, ciphers, ciphersuites, level, DHparams)) == NULL) {
	    (int) cert_len, CApath, CAfile, ciphers, ciphersuites, level, DHparams)) == NULL) {
	    Tls_Free((char *) statePtr);
	    return TCL_ERROR;
	}
    }

    statePtr->ctx = ctx;

1710
1711
1712
1713
1714
1715
1716
1717

1718
1719
1720
1721
1722
1723

1724
1725
1726
1727
1728
1729
1730
1710
1711
1712
1713
1714
1715
1716

1717
1718
1719
1720
1721
1722

1723
1724
1725
1726
1727
1728
1729
1730







-
+





-
+







 * Side effects:
 *	constructs SSL context (CTX)
 *
 *-------------------------------------------------------------------
 */
static SSL_CTX *
CTX_Init(State *statePtr, int isServer, int proto, char *keyfile, char *certfile,
    unsigned char *key, unsigned char *cert, int key_len, int cert_len, char *CAdir,
    unsigned char *key, unsigned char *cert, int key_len, int cert_len, char *CApath,
    char *CAfile, char *ciphers, char *ciphersuites, int level, char *DHparams) {
    Tcl_Interp *interp = statePtr->interp;
    SSL_CTX *ctx = NULL;
    Tcl_DString ds;
    Tcl_DString ds1;
    int off = 0;
    int off = 0, abort = 0;
    int load_private_key;
    const SSL_METHOD *method;

    dprintf("Called");

    if (!proto) {
	Tcl_AppendResult(interp, "no valid protocol selected", (char *) NULL);
2007
2008
2009
2010
2011
2012
2013
2014

2015
2016
2017
2018
2019

2020
2021

2022
2023
2024
2025
2026
2027
2028











2029
2030
2031



2032
2033

2034

2035
2036
2037
2038
2039
2040
2041
2042
2043




























2044
2045

2046
2047
2048
2049
2050
2051
2052
2007
2008
2009
2010
2011
2012
2013

2014





2015


2016







2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027



2028
2029
2030


2031
2032
2033









2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062

2063
2064
2065
2066
2067
2068
2069
2070







-
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+

+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







	    Tcl_AppendResult(interp, "private key does not match the certificate public key",
			     (char *) NULL);
	    SSL_CTX_free(ctx);
	    return NULL;
	}
    }

    /* Set verification CAs */
    /* Set to use default location and file for Certificate Authority (CA) certificates. The
    Tcl_DStringInit(&ds);
    Tcl_DStringInit(&ds1);
    /* There is one default directory, one default file, and one default store.
	The default CA certificates directory (and default store) is in the OpenSSL
	certs directory. It can be overridden by the SSL_CERT_DIR env var. The
     * verify path and store can be overridden by the SSL_CERT_DIR env var. The verify file can
	default CA certificates file is called cert.pem in the default OpenSSL
	directory. It can be overridden by the SSL_CERT_FILE env var. */
     * be overridden by the SSL_CERT_FILE env var. */
	/* int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx) and int SSL_CTX_set_default_verify_file(SSL_CTX *ctx) */
    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 */
    if (!SSL_CTX_set_default_verify_paths(ctx)) {
	abort++;
    }

    /* Overrides for the CA verify path and file */
    Tcl_DStringInit(&ds);
    {
#if OPENSSL_VERSION_NUMBER < 0x30000000L
	Tcl_DString ds1;
	Tcl_DStringInit(&ds1);

	Tcl_AppendResult(interp, "SSL default verify paths: ", GET_ERR_REASON(), (char *) NULL);
	SSL_CTX_free(ctx);
	return NULL;
	if (CApath != NULL || CAfile != NULL) {
	    if (!SSL_CTX_load_verify_locations(ctx, F2N(CAfile, &ds), F2N(CApath, &ds1))) {
		abort++;
#endif
    }
	    }

	    /* Set list of CAs to send to client when requesting a client certificate */
    /* 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);
	}
    }

	    /* https://sourceforge.net/p/tls/bugs/57/ */
	    /* XXX:TODO: Let the user supply values here instead of something that exists on the filesystem */
	    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(&ds1);

#else
	if (CApath != NULL) {
	    if (!SSL_CTX_load_verify_dir(ctx, F2N(CApath, &ds))) {
		abort++;
	    }
	}
	if (CAfile != NULL) {
	    if (!SSL_CTX_load_verify_file(ctx, F2N(CAfile, &ds))) {
		abort++;
	    }

	    /* 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);
	    }
	}
#endif
    }
    Tcl_DStringFree(&ds);
    Tcl_DStringFree(&ds1);

    return ctx;
}

/*
 *-------------------------------------------------------------------
 *
 * StatusObjCmd -- return certificate for connected peer.

Modified tests/README.txt from [673cb36188] to [dc27a7e2a2].

1
2
3

4
5

6
7
8

9
10

11
12
13
14
15


16

17






1
2

3
4

5

6

7
8
9
10
11
12
13


14
15
16
17

18
19
20
21
22
23


-
+

-
+
-

-
+


+



-
-
+
+

+
-
+
+
+
+
+
+
Create Test Cases

1. Create test case *.csv file. You can use multiple files. Generally it's a good idea to group like functions in the same file.
1. Create the test case *.csv file. You can use multiple files. Generally it's a good idea to group like functions in the same file.

2. Add test cases to *.csv files.
2. Add test cases to *.csv files. Each test case is on a separate line. The column titles correspond to the tcltest tool options. Leave a column blank if not used.
	Each test case is on a separate line. Each column defines the equivalent input the tcltest tool expects.

3. Define any common functions in common.tcl or in *.csv file.
3. Define any common functions in a common.tcl or in *.csv file.

4. To create the test cases script, execute make_test_files.tcl. This will use the *.csv files to create the *.test files.


Execute Test Suite

5. To run the test suite, execute the all.tcl file. The results will be output to the stdoutlog.txt file.
	On Windows you can also use the run_all_tests.bat file.
5. To run the test suite, execute the all.tcl file.


Special Notes
6. Review stdoutlog.txt for the count of test cases executed successfully and view details of those that failed.

On systems that don't use a standard OpenSSL installation, the following environment variables can be used to set SSL cert info:

SSL_CERT_FILE = Set to file with SSL CA certificates in OpenSSL compatible format. The usual file name is /path/to/cacert.pem.

SSL_CERT_DIR = Path to directory with CA files.