Browse code

avformat: implement SChannel SSP TLS protocol

This implementation does not support TLS listen sockets and loading
CA/Certs from files.

The Windows API does not support loading PEM certs, and would either
require a manual loader or instead be limited to loading Windows PFX
certificates

TLS listen sockets would have to be implemented quite separately, as many
of the APIs are different for server-mode (as opposed to client mode).

Hendrik Leppkes authored on 2015/10/04 21:48:49
Showing 5 changed files
... ...
@@ -281,6 +281,8 @@ External library support:
281 281
   --enable-opengl          enable OpenGL rendering [no]
282 282
   --enable-openssl         enable openssl, needed for https support
283 283
                            if gnutls is not used [no]
284
+  --disable-schannel       disable SChannel SSP, needed for TLS support on
285
+                           Windows if openssl and gnutls are not used [autodetect]
284 286
   --disable-sdl            disable sdl [autodetect]
285 287
   --disable-securetransport disable Secure Transport, needed for TLS support
286 288
                            on OSX if openssl and gnutls are not used [autodetect]
... ...
@@ -1468,6 +1470,7 @@ EXTERNAL_LIBRARY_LIST="
1468 1468
     opencl
1469 1469
     opengl
1470 1470
     openssl
1471
+    schannel
1471 1472
     sdl
1472 1473
     securetransport
1473 1474
     x11grab
... ...
@@ -2763,13 +2766,15 @@ sctp_protocol_deps="struct_sctp_event_subscribe"
2763 2763
 sctp_protocol_select="network"
2764 2764
 srtp_protocol_select="rtp_protocol"
2765 2765
 tcp_protocol_select="network"
2766
-tls_gnutls_protocol_deps="gnutls !tls_securetransport_protocol"
2766
+tls_gnutls_protocol_deps="gnutls !tls_schannel_protocol !tls_securetransport_protocol"
2767 2767
 tls_gnutls_protocol_select="tcp_protocol"
2768
-tls_openssl_protocol_deps="openssl !tls_securetransport_protocol !tls_gnutls_protocol"
2768
+tls_openssl_protocol_deps="openssl !tls_schannel_protocol !tls_securetransport_protocol !tls_gnutls_protocol"
2769 2769
 tls_openssl_protocol_select="tcp_protocol"
2770
+tls_schannel_protocol_deps="schannel"
2771
+tls_schannel_protocol_select="tcp_protocol"
2770 2772
 tls_securetransport_protocol_deps="securetransport"
2771 2773
 tls_securetransport_protocol_select="tcp_protocol"
2772
-tls_protocol_deps_any="tls_securetransport_protocol tls_gnutls_protocol tls_openssl_protocol"
2774
+tls_protocol_deps_any="tls_schannel_protocol tls_securetransport_protocol tls_gnutls_protocol tls_openssl_protocol"
2773 2775
 udp_protocol_select="network"
2774 2776
 udplite_protocol_select="network"
2775 2777
 unix_protocol_deps="sys_un_h"
... ...
@@ -5511,6 +5516,9 @@ disabled securetransport || { check_func SecIdentityCreate "-Wl,-framework,CoreF
5511 5511
     check_lib2 "Security/SecureTransport.h Security/Security.h" "SSLCreateContext SecItemImport" "-Wl,-framework,CoreFoundation -Wl,-framework,Security" &&
5512 5512
     enable securetransport; }
5513 5513
 
5514
+disabled schannel || { check_func_headers "windows.h Security.h" InitializeSecurityContext -DSECURITY_WIN32 -lSecur32 &&
5515
+                       enable schannel && add_extralibs -lSecur32; }
5516
+
5514 5517
 makeinfo --version > /dev/null 2>&1 && enable makeinfo  || disable makeinfo
5515 5518
 enabled makeinfo \
5516 5519
     && [ 0$(makeinfo --version | grep "texinfo" | sed 's/.*texinfo[^0-9]*\([0-9]*\)\..*/\1/') -ge 5 ] \
... ...
@@ -544,6 +544,7 @@ OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
544 544
 OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
545 545
 OBJS-$(CONFIG_TLS_GNUTLS_PROTOCOL)       += tls_gnutls.o tls.o
546 546
 OBJS-$(CONFIG_TLS_OPENSSL_PROTOCOL)      += tls_openssl.o tls.o
547
+OBJS-$(CONFIG_TLS_SCHANNEL_PROTOCOL)     += tls_schannel.o tls.o
547 548
 OBJS-$(CONFIG_TLS_SECURETRANSPORT_PROTOCOL) += tls_securetransport.o tls.o
548 549
 OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o
549 550
 OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o
... ...
@@ -395,6 +395,7 @@ void av_register_all(void)
395 395
     REGISTER_PROTOCOL(SRTP,             srtp);
396 396
     REGISTER_PROTOCOL(SUBFILE,          subfile);
397 397
     REGISTER_PROTOCOL(TCP,              tcp);
398
+    REGISTER_PROTOCOL(TLS_SCHANNEL,     tls_schannel);
398 399
     REGISTER_PROTOCOL(TLS_SECURETRANSPORT, tls_securetransport);
399 400
     REGISTER_PROTOCOL(TLS_GNUTLS,       tls_gnutls);
400 401
     REGISTER_PROTOCOL(TLS_OPENSSL,      tls_openssl);
... ...
@@ -26,7 +26,7 @@
26 26
 #include "url.h"
27 27
 #include "libavutil/opt.h"
28 28
 
29
-#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL | CONFIG_TLS_SECURETRANSPORT_PROTOCOL)
29
+#define CONFIG_TLS_PROTOCOL (CONFIG_TLS_GNUTLS_PROTOCOL | CONFIG_TLS_OPENSSL_PROTOCOL | CONFIG_TLS_SECURETRANSPORT_PROTOCOL | CONFIG_TLS_SCHANNEL_PROTOCOL)
30 30
 
31 31
 typedef struct TLSShared {
32 32
     char *ca_file;
33 33
new file mode 100644
... ...
@@ -0,0 +1,600 @@
0
+/*
1
+ * Copyright (c) 2015 Hendrik Leppkes
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/** Based on the CURL SChannel module */
21
+
22
+#include "avformat.h"
23
+#include "internal.h"
24
+#include "network.h"
25
+#include "os_support.h"
26
+#include "url.h"
27
+#include "tls.h"
28
+
29
+#define SECURITY_WIN32
30
+#include <security.h>
31
+#include <schnlsp.h>
32
+
33
+#define SCHANNEL_INITIAL_BUFFER_SIZE   4096
34
+#define SCHANNEL_FREE_BUFFER_SIZE      1024
35
+
36
+/* mingw does not define this symbol */
37
+#ifndef SECBUFFER_ALERT
38
+#define SECBUFFER_ALERT                17
39
+#endif
40
+
41
+typedef struct TLSContext {
42
+    const AVClass *class;
43
+    TLSShared tls_shared;
44
+
45
+    CredHandle cred_handle;
46
+    TimeStamp cred_timestamp;
47
+
48
+    CtxtHandle ctxt_handle;
49
+    TimeStamp ctxt_timestamp;
50
+
51
+    ULONG request_flags;
52
+    ULONG context_flags;
53
+
54
+    uint8_t *enc_buf;
55
+    int enc_buf_size;
56
+    int enc_buf_offset;
57
+
58
+    uint8_t *dec_buf;
59
+    int dec_buf_size;
60
+    int dec_buf_offset;
61
+
62
+    SecPkgContext_StreamSizes sizes;
63
+
64
+    int connected;
65
+    int connection_closed;
66
+    int sspi_close_notify;
67
+} TLSContext;
68
+
69
+static void init_sec_buffer(SecBuffer *buffer, unsigned long type,
70
+                            void *data, unsigned long size)
71
+{
72
+    buffer->cbBuffer   = size;
73
+    buffer->BufferType = type;
74
+    buffer->pvBuffer   = data;
75
+}
76
+
77
+static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers,
78
+                                 unsigned long buffer_count)
79
+{
80
+    desc->ulVersion = SECBUFFER_VERSION;
81
+    desc->pBuffers = buffers;
82
+    desc->cBuffers = buffer_count;
83
+}
84
+
85
+static int tls_shutdown_client(URLContext *h)
86
+{
87
+    TLSContext *c = h->priv_data;
88
+    TLSShared *s = &c->tls_shared;
89
+    int ret;
90
+
91
+    if (c->connected) {
92
+        SecBufferDesc BuffDesc;
93
+        SecBuffer Buffer;
94
+        SECURITY_STATUS sspi_ret;
95
+        SecBuffer outbuf;
96
+        SecBufferDesc outbuf_desc;
97
+
98
+        DWORD dwshut = SCHANNEL_SHUTDOWN;
99
+        init_sec_buffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
100
+        init_sec_buffer_desc(&BuffDesc, &Buffer, 1);
101
+
102
+        sspi_ret = ApplyControlToken(&c->ctxt_handle, &BuffDesc);
103
+        if (sspi_ret != SEC_E_OK)
104
+            av_log(h, AV_LOG_ERROR, "ApplyControlToken failed\n");
105
+
106
+        init_sec_buffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
107
+        init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);
108
+
109
+        sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host,
110
+                                             c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle,
111
+                                             &outbuf_desc, &c->context_flags, &c->ctxt_timestamp);
112
+        if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) {
113
+            ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
114
+            FreeContextBuffer(outbuf.pvBuffer);
115
+            if (ret < 0 || ret != outbuf.cbBuffer)
116
+                av_log(h, AV_LOG_ERROR, "Failed to send close message\n");
117
+        }
118
+
119
+        c->connected = 0;
120
+    }
121
+    return 0;
122
+}
123
+
124
+static int tls_close(URLContext *h)
125
+{
126
+    TLSContext *c = h->priv_data;
127
+
128
+    tls_shutdown_client(h);
129
+
130
+    DeleteSecurityContext(&c->ctxt_handle);
131
+    FreeCredentialsHandle(&c->cred_handle);
132
+
133
+    av_freep(&c->enc_buf);
134
+    c->enc_buf_size = c->enc_buf_offset = 0;
135
+
136
+    av_freep(&c->dec_buf);
137
+    c->dec_buf_size = c->dec_buf_offset = 0;
138
+
139
+    if (c->tls_shared.tcp)
140
+        ffurl_close(c->tls_shared.tcp);
141
+    return 0;
142
+}
143
+
144
+static int tls_client_handshake_loop(URLContext *h, int initial)
145
+{
146
+    TLSContext *c = h->priv_data;
147
+    TLSShared *s = &c->tls_shared;
148
+    SECURITY_STATUS sspi_ret;
149
+    SecBuffer outbuf[3];
150
+    SecBufferDesc outbuf_desc;
151
+    SecBuffer inbuf[2];
152
+    SecBufferDesc inbuf_desc;
153
+    int i, ret = 0, read_data = initial;
154
+
155
+    if (c->enc_buf == NULL) {
156
+        c->enc_buf_offset = 0;
157
+        ret = av_reallocp(&c->enc_buf, SCHANNEL_INITIAL_BUFFER_SIZE);
158
+        if (ret < 0)
159
+            goto fail;
160
+        c->enc_buf_size = SCHANNEL_INITIAL_BUFFER_SIZE;
161
+    }
162
+
163
+    if (c->dec_buf == NULL) {
164
+        c->dec_buf_offset = 0;
165
+        ret = av_reallocp(&c->dec_buf, SCHANNEL_INITIAL_BUFFER_SIZE);
166
+        if (ret < 0)
167
+            goto fail;
168
+        c->dec_buf_size = SCHANNEL_INITIAL_BUFFER_SIZE;
169
+    }
170
+
171
+    while (1) {
172
+        if (c->enc_buf_size - c->enc_buf_offset < SCHANNEL_FREE_BUFFER_SIZE) {
173
+            c->enc_buf_size = c->enc_buf_offset + SCHANNEL_FREE_BUFFER_SIZE;
174
+            ret = av_reallocp(&c->enc_buf, c->enc_buf_size);
175
+            if (ret < 0) {
176
+                c->enc_buf_size = c->enc_buf_offset = 0;
177
+                goto fail;
178
+            }
179
+        }
180
+
181
+        if (read_data) {
182
+            ret = ffurl_read(c->tls_shared.tcp, c->enc_buf + c->enc_buf_offset,
183
+                             c->enc_buf_size - c->enc_buf_offset);
184
+            if (ret < 0) {
185
+                av_log(h, AV_LOG_ERROR, "Failed to read handshake response\n");
186
+                goto fail;
187
+            }
188
+            c->enc_buf_offset += ret;
189
+        }
190
+
191
+        /* input buffers */
192
+        init_sec_buffer(&inbuf[0], SECBUFFER_TOKEN, av_malloc(c->enc_buf_offset), c->enc_buf_offset);
193
+        init_sec_buffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
194
+        init_sec_buffer_desc(&inbuf_desc, inbuf, 2);
195
+
196
+        if (inbuf[0].pvBuffer == NULL) {
197
+            av_log(h, AV_LOG_ERROR, "Failed to allocate input buffer\n");
198
+            ret = AVERROR(ENOMEM);
199
+            goto fail;
200
+        }
201
+
202
+        memcpy(inbuf[0].pvBuffer, c->enc_buf, c->enc_buf_offset);
203
+
204
+        /* output buffers */
205
+        init_sec_buffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
206
+        init_sec_buffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
207
+        init_sec_buffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
208
+        init_sec_buffer_desc(&outbuf_desc, outbuf, 3);
209
+
210
+        sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host, c->request_flags,
211
+                                             0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &c->context_flags,
212
+                                             &c->ctxt_timestamp);
213
+        av_freep(&inbuf[0].pvBuffer);
214
+
215
+        if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
216
+            av_log(h, AV_LOG_DEBUG, "Received incomplete handshake, need more data\n");
217
+            read_data = 1;
218
+            continue;
219
+        }
220
+
221
+        /* remote requests a client certificate - attempt to continue without one anyway */
222
+        if (sspi_ret == SEC_I_INCOMPLETE_CREDENTIALS &&
223
+            !(c->request_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
224
+            av_log(h, AV_LOG_VERBOSE, "Client certificate has been requested, ignoring\n");
225
+            c->request_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
226
+            read_data = 0;
227
+            continue;
228
+        }
229
+
230
+        /* continue handshake */
231
+        if (sspi_ret == SEC_I_CONTINUE_NEEDED || sspi_ret == SEC_E_OK) {
232
+            for (i = 0; i < 3; i++) {
233
+                if (outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
234
+                    ret = ffurl_write(c->tls_shared.tcp, outbuf[i].pvBuffer, outbuf[i].cbBuffer);
235
+                    if (ret < 0 || ret != outbuf[i].cbBuffer) {
236
+                        av_log(h, AV_LOG_VERBOSE, "Failed to send handshake data\n");
237
+                        ret = AVERROR(EIO);
238
+                        goto fail;
239
+                    }
240
+                }
241
+
242
+                if (outbuf[i].pvBuffer != NULL) {
243
+                    FreeContextBuffer(outbuf[i].pvBuffer);
244
+                    outbuf[i].pvBuffer = NULL;
245
+                }
246
+            }
247
+        } else {
248
+            if (sspi_ret == SEC_E_WRONG_PRINCIPAL)
249
+                av_log(h, AV_LOG_ERROR, "SNI or certificate check failed\n");
250
+            else
251
+                av_log(h, AV_LOG_ERROR, "Creating security context failed (0x%lx)\n", sspi_ret);
252
+            ret = AVERROR_UNKNOWN;
253
+            goto fail;
254
+        }
255
+
256
+        if (inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
257
+            if (c->enc_buf_offset > inbuf[1].cbBuffer) {
258
+                memmove(c->enc_buf, (c->enc_buf + c->enc_buf_offset) - inbuf[1].cbBuffer,
259
+                        inbuf[1].cbBuffer);
260
+                c->enc_buf_offset = inbuf[1].cbBuffer;
261
+                if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
262
+                    read_data = 0;
263
+                    continue;
264
+                }
265
+            }
266
+        } else {
267
+            c->enc_buf_offset  = 0;
268
+        }
269
+
270
+        if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
271
+            read_data = 1;
272
+            continue;
273
+        }
274
+
275
+        break;
276
+    }
277
+
278
+    return 0;
279
+
280
+fail:
281
+    /* free any remaining output data */
282
+    for (i = 0; i < 3; i++) {
283
+        if (outbuf[i].pvBuffer != NULL) {
284
+            FreeContextBuffer(outbuf[i].pvBuffer);
285
+            outbuf[i].pvBuffer = NULL;
286
+        }
287
+    }
288
+
289
+    return ret;
290
+}
291
+
292
+static int tls_client_handshake(URLContext *h)
293
+{
294
+    TLSContext *c = h->priv_data;
295
+    TLSShared *s = &c->tls_shared;
296
+    SecBuffer outbuf;
297
+    SecBufferDesc outbuf_desc;
298
+    SECURITY_STATUS sspi_ret;
299
+    int ret;
300
+
301
+    init_sec_buffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
302
+    init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);
303
+
304
+    c->request_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
305
+                       ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
306
+                       ISC_REQ_STREAM;
307
+
308
+    sspi_ret = InitializeSecurityContext(&c->cred_handle, NULL, s->host, c->request_flags, 0, 0,
309
+                                         NULL, 0, &c->ctxt_handle, &outbuf_desc, &c->context_flags,
310
+                                         &c->ctxt_timestamp);
311
+    if (sspi_ret != SEC_I_CONTINUE_NEEDED) {
312
+        av_log(h, AV_LOG_ERROR, "Unable to create initial security context (0x%lx)\n", sspi_ret);
313
+        ret = AVERROR_UNKNOWN;
314
+        goto fail;
315
+    }
316
+
317
+    ret = ffurl_write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
318
+    FreeContextBuffer(outbuf.pvBuffer);
319
+    if (ret < 0 || ret != outbuf.cbBuffer) {
320
+        av_log(h, AV_LOG_ERROR, "Failed to send initial handshake data\n");
321
+        ret = AVERROR(EIO);
322
+        goto fail;
323
+    }
324
+
325
+    return tls_client_handshake_loop(h, 1);
326
+
327
+fail:
328
+    DeleteSecurityContext(&c->ctxt_handle);
329
+    return ret;
330
+}
331
+
332
+static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
333
+{
334
+    TLSContext *c = h->priv_data;
335
+    TLSShared *s = &c->tls_shared;
336
+    SECURITY_STATUS sspi_ret;
337
+    SCHANNEL_CRED schannel_cred = { 0 };
338
+    int ret;
339
+
340
+    if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
341
+        goto fail;
342
+
343
+    if (s->listen) {
344
+        av_log(h, AV_LOG_ERROR, "TLS Listen Sockets with SChannel is not implemented.\n");
345
+        ret = AVERROR(EINVAL);
346
+        goto fail;
347
+    }
348
+
349
+    /* SChannel Options */
350
+    schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
351
+
352
+    if (s->verify)
353
+        schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
354
+                                SCH_CRED_REVOCATION_CHECK_CHAIN;
355
+    else
356
+        schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
357
+                                SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
358
+                                SCH_CRED_IGNORE_REVOCATION_OFFLINE;
359
+
360
+    /* Get credential handle */
361
+    sspi_ret = AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND,
362
+                                        NULL,  &schannel_cred, NULL, NULL, &c->cred_handle,
363
+                                        &c->cred_timestamp);
364
+    if (sspi_ret != SEC_E_OK) {
365
+        av_log(h, AV_LOG_ERROR, "Unable to acquire security credentials (0x%lx)\n", sspi_ret);
366
+        ret = AVERROR_UNKNOWN;
367
+        goto fail;
368
+    }
369
+
370
+    ret = tls_client_handshake(h);
371
+    if (ret < 0)
372
+        goto fail;
373
+
374
+    c->connected = 1;
375
+
376
+    return 0;
377
+
378
+fail:
379
+    tls_close(h);
380
+    return ret;
381
+}
382
+
383
+static int tls_read(URLContext *h, uint8_t *buf, int len)
384
+{
385
+    TLSContext *c = h->priv_data;
386
+    TLSShared *s = &c->tls_shared;
387
+    SECURITY_STATUS sspi_ret = SEC_E_OK;
388
+    SecBuffer inbuf[4];
389
+    SecBufferDesc inbuf_desc;
390
+    int size, ret;
391
+    int min_enc_buf_size = len + SCHANNEL_FREE_BUFFER_SIZE;
392
+
393
+    if (len <= c->dec_buf_offset)
394
+        goto cleanup;
395
+
396
+    if (c->sspi_close_notify)
397
+        goto cleanup;
398
+
399
+    if (!c->connection_closed) {
400
+        size = c->enc_buf_size - c->enc_buf_offset;
401
+        if (size < SCHANNEL_FREE_BUFFER_SIZE || c->enc_buf_size < min_enc_buf_size) {
402
+            c->enc_buf_size = c->enc_buf_offset + SCHANNEL_FREE_BUFFER_SIZE;
403
+            if (c->enc_buf_size < min_enc_buf_size)
404
+                c->enc_buf_size = min_enc_buf_size;
405
+            ret = av_reallocp(&c->enc_buf, c->enc_buf_size);
406
+            if (ret < 0) {
407
+                c->enc_buf_size = c->enc_buf_offset = 0;
408
+                return ret;
409
+            }
410
+        }
411
+
412
+        ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset,
413
+                         c->enc_buf_size - c->enc_buf_offset);
414
+        if (ret < 0) {
415
+            av_log(h, AV_LOG_ERROR, "Unable to read from socket\n");
416
+            return ret;
417
+        } else if (ret == 0)
418
+            c->connection_closed = 1;
419
+
420
+        c->enc_buf_offset += ret;
421
+    }
422
+
423
+    while (c->enc_buf_offset > 0 && sspi_ret == SEC_E_OK && c->dec_buf_offset < len) {
424
+        /*  input buffer */
425
+        init_sec_buffer(&inbuf[0], SECBUFFER_DATA, c->enc_buf, c->enc_buf_offset);
426
+
427
+        /* additional buffers for possible output */
428
+        init_sec_buffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
429
+        init_sec_buffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
430
+        init_sec_buffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
431
+        init_sec_buffer_desc(&inbuf_desc, inbuf, 4);
432
+
433
+        sspi_ret = DecryptMessage(&c->ctxt_handle, &inbuf_desc, 0, NULL);
434
+        if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_RENEGOTIATE ||
435
+            sspi_ret == SEC_I_CONTEXT_EXPIRED) {
436
+            /* handle decrypted data */
437
+            if (inbuf[1].BufferType == SECBUFFER_DATA) {
438
+                /* grow buffer if needed */
439
+                size = inbuf[1].cbBuffer > SCHANNEL_FREE_BUFFER_SIZE ?
440
+                       inbuf[1].cbBuffer : SCHANNEL_FREE_BUFFER_SIZE;
441
+                if (c->dec_buf_size - c->dec_buf_offset < size || c->dec_buf_size < len)  {
442
+                    c->dec_buf_size = c->dec_buf_offset + size;
443
+                    if (c->dec_buf_size < len)
444
+                        c->dec_buf_size = len;
445
+                    ret = av_reallocp(&c->dec_buf, c->dec_buf_size);
446
+                    if (ret < 0) {
447
+                        c->dec_buf_size = c->dec_buf_offset = 0;
448
+                        return ret;
449
+                    }
450
+                }
451
+
452
+                /* copy decrypted data to buffer */
453
+                size = inbuf[1].cbBuffer;
454
+                if (size) {
455
+                    memcpy(c->dec_buf + c->dec_buf_offset, inbuf[1].pvBuffer, size);
456
+                    c->dec_buf_offset += size;
457
+                }
458
+            }
459
+            if (inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
460
+                if (c->enc_buf_offset > inbuf[3].cbBuffer) {
461
+                    memmove(c->enc_buf, (c->enc_buf + c->enc_buf_offset) - inbuf[3].cbBuffer,
462
+                    inbuf[3].cbBuffer);
463
+                    c->enc_buf_offset = inbuf[3].cbBuffer;
464
+                }
465
+            } else
466
+                c->enc_buf_offset = 0;
467
+
468
+            if (sspi_ret == SEC_I_RENEGOTIATE) {
469
+                if (c->enc_buf_offset) {
470
+                    av_log(h, AV_LOG_ERROR, "Cannot renegotiate, encrypted data buffer not empty\n");
471
+                    ret = AVERROR_UNKNOWN;
472
+                    goto cleanup;
473
+                }
474
+
475
+                av_log(h, AV_LOG_VERBOSE, "Re-negotiating security context\n");
476
+                ret = tls_client_handshake_loop(h, 0);
477
+                if (ret < 0) {
478
+                    goto cleanup;
479
+                }
480
+                sspi_ret = SEC_E_OK;
481
+                continue;
482
+            } else if (sspi_ret == SEC_I_CONTEXT_EXPIRED) {
483
+                c->sspi_close_notify = 1;
484
+                if (!c->connection_closed) {
485
+                    c->connection_closed = 1;
486
+                    av_log(h, AV_LOG_VERBOSE, "Server closed the connection\n");
487
+                }
488
+                ret = 0;
489
+                goto cleanup;
490
+            }
491
+        } else if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
492
+            ret = AVERROR(EAGAIN);
493
+            goto cleanup;
494
+        } else {
495
+            av_log(h, AV_LOG_ERROR, "Unable to decrypt message\n");
496
+            ret = AVERROR(EIO);
497
+            goto cleanup;
498
+        }
499
+    }
500
+
501
+    ret = 0;
502
+
503
+cleanup:
504
+    size = FFMIN(len, c->dec_buf_offset);
505
+    if (size) {
506
+        memcpy(buf, c->dec_buf, size);
507
+        memmove(c->dec_buf, c->dec_buf + size, c->dec_buf_offset - size);
508
+        c->dec_buf_offset -= size;
509
+
510
+        return size;
511
+    }
512
+
513
+    if (ret == 0 && !c->connection_closed)
514
+        ret = AVERROR(EAGAIN);
515
+
516
+    return ret < 0 ? ret : 0;
517
+}
518
+
519
+static int tls_write(URLContext *h, const uint8_t *buf, int len)
520
+{
521
+    TLSContext *c = h->priv_data;
522
+    TLSShared *s = &c->tls_shared;
523
+    SECURITY_STATUS sspi_ret;
524
+    int ret = 0, data_size;
525
+    uint8_t *data = NULL;
526
+    SecBuffer outbuf[4];
527
+    SecBufferDesc outbuf_desc;
528
+
529
+    if (c->sizes.cbMaximumMessage == 0) {
530
+        sspi_ret = QueryContextAttributes(&c->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &c->sizes);
531
+        if (sspi_ret != SEC_E_OK)
532
+            return AVERROR_UNKNOWN;
533
+    }
534
+
535
+    /* limit how much data we can consume */
536
+    len = FFMIN(len, c->sizes.cbMaximumMessage);
537
+
538
+    data_size = c->sizes.cbHeader + len + c->sizes.cbTrailer;
539
+    data = av_malloc(data_size);
540
+    if (data == NULL)
541
+        return AVERROR(ENOMEM);
542
+
543
+    init_sec_buffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
544
+                  data, c->sizes.cbHeader);
545
+    init_sec_buffer(&outbuf[1], SECBUFFER_DATA,
546
+                  data + c->sizes.cbHeader, len);
547
+    init_sec_buffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
548
+                  data + c->sizes.cbHeader + len,
549
+                  c->sizes.cbTrailer);
550
+    init_sec_buffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
551
+    init_sec_buffer_desc(&outbuf_desc, outbuf, 4);
552
+
553
+    memcpy(outbuf[1].pvBuffer, buf, len);
554
+
555
+    sspi_ret = EncryptMessage(&c->ctxt_handle, 0, &outbuf_desc, 0);
556
+    if (sspi_ret == SEC_E_OK)  {
557
+        len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
558
+        ret = ffurl_write(s->tcp, data, len);
559
+        if (ret < 0 || ret != len) {
560
+            ret = AVERROR(EIO);
561
+            av_log(h, AV_LOG_ERROR, "Writing encrypted data to socket failed\n");
562
+            goto done;
563
+        }
564
+    } else {
565
+        av_log(h, AV_LOG_ERROR, "Encrypting data failed\n");
566
+        if (sspi_ret == SEC_E_INSUFFICIENT_MEMORY)
567
+            ret = AVERROR(ENOMEM);
568
+        else
569
+            ret = AVERROR(EIO);
570
+        goto done;
571
+    }
572
+
573
+done:
574
+    av_freep(&data);
575
+    return ret < 0 ? ret : outbuf[1].cbBuffer;
576
+}
577
+
578
+static const AVOption options[] = {
579
+    TLS_COMMON_OPTIONS(TLSContext, tls_shared),
580
+    { NULL }
581
+};
582
+
583
+static const AVClass tls_class = {
584
+    .class_name = "tls",
585
+    .item_name  = av_default_item_name,
586
+    .option     = options,
587
+    .version    = LIBAVUTIL_VERSION_INT,
588
+};
589
+
590
+URLProtocol ff_tls_schannel_protocol = {
591
+    .name           = "tls",
592
+    .url_open2      = tls_open,
593
+    .url_read       = tls_read,
594
+    .url_write      = tls_write,
595
+    .url_close      = tls_close,
596
+    .priv_data_size = sizeof(TLSContext),
597
+    .flags          = URL_PROTOCOL_FLAG_NETWORK,
598
+    .priv_data_class = &tls_class,
599
+};