Diff

Differences From Artifact [82977b2388]:

To Artifact [201a06bc4e]:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16





17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11





12
13
14
15
16
17
18
19
20
21
22
23











-
-
-
-
-
+
+
+
+
+







/*
 * Provides Custom BIO layer to interface OpenSSL with TCL. These functions
 * directly interface between the TCL IO channel and BIO buffers.
 *
 * Copyright (C) 1997-2000 Matt Newman <[email protected]>
 * Copyright (C) 2024 Brian O'Hagan
 *
 */

/*
		tlsBIO.c				tlsIO.c
  +------+                         +-----+                                     +------+
  |      |Tcl_WriteRaw <-- BioWrite| SSL |BIO_write <-- TlsOutputProc <-- Write|      |
  |socket|      <encrypted>        | BIO |            <unencrypted>            | App  |
  |      |Tcl_ReadRaw  -->  BioRead|     |BIO_Read  --> TlsInputProc  -->  Read|      |
  +------+                         +-----+                                     +------+
 +------+                        +---+                                 +---+
 |      |Tcl_WriteRaw<--BioOutput|SSL|BIO_write<--TlsOutputProc<--Write|   |
 |socket|      <encrypted>       |BIO|            <unencrypted>        |App|
 |      |Tcl_ReadRaw --> BioInput|   |BIO_Read -->TlsInputProc --> Read|   |
 +------+                        +---+                                 +---+
*/

#include "tlsInt.h"
#include <openssl/bio.h>

/* Define BIO methods structure */
static BIO_METHOD *BioMethods = NULL;
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86


87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104







-
+















-
+





-
+
+









-
+








    return res;
}

/*
 *-----------------------------------------------------------------------------
 *
 * BioWrite --
 * BioOutput --
 *
 *	This function is used to read encrypted data from the BIO and write it
 *	into the socket. This function will be called in response to the
 *	application calling the BIO_write_ex() or BIO_write() functions.
 *
 * Results:
 *	Returns the number of bytes written to channel, 0 for EOF, or -1 for
 *	error.
 *
 * Side effects:
 *	Writes BIO data to channel.
 *
 *-----------------------------------------------------------------------------
 */

static int BioWrite(BIO *bio, const char *buf, int bufLen) {
static int BioOutput(BIO *bio, const char *buf, int bufLen) {
    Tcl_Size ret;
    int is_eof, tclErrno;
    State *statePtr = (State *) BIO_get_data(bio);
    Tcl_Channel chan = Tls_GetParent(statePtr, 0);

    dprintf("[chan=%p] BioWrite(bio=%p, buf=%p, len=%d)", (void *)chan, (void *) bio, buf, bufLen);
    dprintf("[chan=%p] BioOutput(bio=%p, buf=%p, len=%d)", (void *)chan,
	(void *) bio, buf, bufLen);

    BIO_clear_retry_flags(bio);
    Tcl_SetErrno(0);

    /* Write data to underlying channel */
    ret = Tcl_WriteRaw(chan, buf, (Tcl_Size) bufLen);
    is_eof = Tcl_Eof(chan);
    tclErrno = Tcl_GetErrno();

    dprintf("[chan=%p] BioWrite(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; tclErrno=%d: %s]",
    dprintf("[chan=%p] BioOutput(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; tclErrno=%d: %s]",
	(void *) chan, bufLen, ret, is_eof, tclErrno, Tcl_ErrnoMsg(tclErrno));

    if (ret > 0) {
	dprintf("Successfully wrote %" TCL_SIZE_MODIFIER "d bytes of data", ret);

    } else if (ret == 0) {
	if (is_eof) {
121
122
123
124
125
126
127
128

129
130
131
132
133
134
135

136
137
138
139
140
141
142
122
123
124
125
126
127
128

129
130
131
132
133
134
135

136
137
138
139
140
141
142
143







-
+






-
+







	    BIO_set_retry_write(bio);

	} else {
	    dprintf("Unexpected error: %i=%s", tclErrno, Tcl_ErrnoMsg(tclErrno));
	}
    }

    dprintf("BioWrite returning %" TCL_SIZE_MODIFIER "d", ret);
    dprintf("BioOutput returning %" TCL_SIZE_MODIFIER "d", ret);
    return (int) ret;
}

/*
 *-----------------------------------------------------------------------------
 *
 * BioRead --
 * BioInput --
 *
 *	This function is used to read encrypted data from the socket and
 *	write it into the BIO. This function will be called in response to the
 *	application calling the BIO_read_ex() or BIO_read() functions.
 *
 * Results:
 *	Returns the number of bytes read from channel, 0 for EOF, or -1 for
151
152
153
154
155
156
157
158

159
160
161
162
163
164


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
152
153
154
155
156
157
158

159
160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189







-
+





-
+
+















-
+







 * returns the number of bytes which have been processed, buffered, and are
 * available inside ssl for immediate read. SSL_has_pending() returns 1 if
 * data is buffered (whether processed or unprocessed) and 0 otherwise.
 *
 *-----------------------------------------------------------------------------
 */

static int BioRead(BIO *bio, char *buf, int bufLen) {
static int BioInput(BIO *bio, char *buf, int bufLen) {
    Tcl_Size ret = 0;
    int is_eof, tclErrno, is_blocked;
    State *statePtr = (State *) BIO_get_data(bio);
    Tcl_Channel chan = Tls_GetParent(statePtr, 0);

    dprintf("[chan=%p] BioRead(bio=%p, buf=%p, len=%d)", (void *) chan, (void *) bio, buf, bufLen);
    dprintf("[chan=%p] BioInput(bio=%p, buf=%p, len=%d)", (void *) chan,
	(void *) bio, buf, bufLen);

    if (buf == NULL || bufLen <= 0) {
	return 0;
    }

    BIO_clear_retry_flags(bio);
    Tcl_SetErrno(0);

    /* Read data from underlying channel */
    ret = Tcl_ReadRaw(chan, buf, (Tcl_Size) bufLen);

    is_eof = Tcl_Eof(chan);
    tclErrno = Tcl_GetErrno();
    is_blocked = Tcl_InputBlocked(chan);

    dprintf("[chan=%p] BioRead(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; blocked=%d; tclErrno=%d: %s]",
    dprintf("[chan=%p] BioInput(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; blocked=%d; tclErrno=%d: %s]",
	(void *) chan, bufLen, ret, is_eof, is_blocked, tclErrno, Tcl_ErrnoMsg(tclErrno));

    if (ret > 0) {
	dprintf("Successfully read %" TCL_SIZE_MODIFIER "d bytes of data", ret);

    } else if (ret == 0) {
	if (is_eof) {
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218







-
+







	    BIO_set_retry_read(bio);

	} else {
	    dprintf("Unexpected error: %i=%s", tclErrno, Tcl_ErrnoMsg(tclErrno));
	}
    }

    dprintf("BioRead returning %" TCL_SIZE_MODIFIER "d", ret);
    dprintf("BioInput returning %" TCL_SIZE_MODIFIER "d", ret);
    return (int) ret;
}

/*
 *-----------------------------------------------------------------------------
 *
 * BioPuts --
227
228
229
230
231
232
233
234

235
236
237
238
239
240
241
229
230
231
232
233
234
235

236
237
238
239
240
241
242
243







-
+







 *
 *-----------------------------------------------------------------------------
 */

static int BioPuts(BIO *bio, const char *str) {
    dprintf("BioPuts(%p) \"%s\"", bio, str);

    return BioWrite(bio, str, (int) strlen(str));
    return BioOutput(bio, str, (int) strlen(str));
}

/*
 *-----------------------------------------------------------------------------
 *
 * BioCtrl --
 *
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
320
321
322
323
324
325
326

327
328
329
330
331
332
333
334







-
+







		break;
	case BIO_CTRL_FLUSH:
		/* opt - Flush any buffered output. Implements BIO_flush. */
		dprintf("Got BIO_CTRL_FLUSH");
		/* Use Tcl_WriteRaw instead of Tcl_Flush to operate on right chan in stack */
		/* Returns 1 for success, <=0 for error/retry. */
		ret = ((chan) && (Tcl_WriteRaw(chan, "", 0) >= 0) ? 1 : -1);
		/*ret = BioWrite(bio, NULL, 0);*/
		/*ret = BioOutput(bio, NULL, 0);*/
		break;
	case BIO_CTRL_DUP:
		/* man - extra stuff for 'duped' BIO. Implements BIO_dup_state */
		dprintf("Got BIO_CTRL_DUP");
		ret = 1;
		break;
	case BIO_CTRL_WPENDING:
491
492
493
494
495
496
497
498

499
500

501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516


517
518
519
520
521
522
523
524
525
526


527
528


529
530
531
532
533
534
535
536
537
538
539
540
541


542
543
544
545
546
547
548
493
494
495
496
497
498
499

500
501

502
503
504
505
506
507
508
509
510
511
512
513
514
515
516


517
518
519
520
521
522
523
524
525
526
527

528
529
530

531
532
533
534
535
536
537
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552
553







-
+

-
+














-
-
+
+









-
+
+

-
+
+












-
+
+







	BioMethods = BIO_meth_new(BIO_TYPE_BIO, "tcl");
	if (BioMethods == NULL) {
	    dprintf("Memory allocation error");

	    return NULL;
	}
	/* Not used BIO_meth_set_write_ex */
	BIO_meth_set_write(BioMethods, BioWrite);
	BIO_meth_set_write(BioMethods, BioOutput);
	/* Not used BIO_meth_set_read_ex */
	BIO_meth_set_read(BioMethods, BioRead);
	BIO_meth_set_read(BioMethods, BioInput);
	BIO_meth_set_puts(BioMethods, BioPuts);
	BIO_meth_set_ctrl(BioMethods, BioCtrl);
	BIO_meth_set_create(BioMethods, BioNew);
	BIO_meth_set_destroy(BioMethods, BioFree);
    }

    if (statePtr == NULL) {
	dprintf("Asked to setup a NULL state, just creating the initial configuration");

	return NULL;
    }

#ifdef TCLTLS_SSL_USE_FASTPATH
    /*
     * If the channel can be mapped back to a file descriptor, just use the file descriptor
     * with the SSL library since it will likely be optimized for this.
     * If the channel can be mapped back to a file descriptor, just use the file
     * descriptor with the SSL library since it will likely be optimized for this.
     */
    parentChannel = Tls_GetParent(statePtr, 0);
    parentChannelType = Tcl_GetChannelType(parentChannel);

    validParentChannelFd = 0;
    if (strcmp(parentChannelType->typeName, "tcp") == 0) {
	void *parentChannelFdIn_p, *parentChannelFdOut_p;
	int tclGetChannelHandleRet;

	tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel, TCL_READABLE, &parentChannelFdIn_p);
	tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel,
	    TCL_READABLE, &parentChannelFdIn_p);
	if (tclGetChannelHandleRet == TCL_OK) {
	    tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel, TCL_WRITABLE, &parentChannelFdOut_p);
	    tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel,
	        TCL_WRITABLE, &parentChannelFdOut_p);
	    if (tclGetChannelHandleRet == TCL_OK) {
		parentChannelFdIn = PTR2INT(parentChannelFdIn_p);
		parentChannelFdOut = PTR2INT(parentChannelFdOut_p);
		if (parentChannelFdIn == parentChannelFdOut) {
		    parentChannelFd = parentChannelFdIn;
		    validParentChannelFd = 1;
		}
	    }
	}
    }

    if (validParentChannelFd) {
	dprintf("We found a shortcut, this channel is backed by a socket: %i", parentChannelFdIn);
	dprintf("We found a shortcut, this channel is backed by a socket: %i",
	    parentChannelFdIn);
	bio = BIO_new_socket(parentChannelFd, flags);
	statePtr->flags |= TLS_TCL_FASTPATH;
	BIO_set_data(bio, statePtr);
	BIO_set_shutdown(bio, flags);
	BIO_set_init(bio, 1);
	return bio;
    }