1 /*
2 * Copyright (c) 2015 Hendrik Leppkes
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /** Based on the CURL SChannel module */
22
29
30 #define SECURITY_WIN32
31 #include <windows.h>
32 #include <security.h>
33 #include <schnlsp.h>
34
35 #define SCHANNEL_INITIAL_BUFFER_SIZE 4096
36 #define SCHANNEL_FREE_BUFFER_SIZE 1024
37
38 /* mingw does not define this symbol */
39 #ifndef SECBUFFER_ALERT
40 #define SECBUFFER_ALERT 17
41 #endif
42
46
49
52
55
59
63
64 SecPkgContext_StreamSizes
sizes;
65
70
73 {
77 }
78
80 unsigned long buffer_count)
81 {
82 desc->ulVersion = SECBUFFER_VERSION;
83 desc->pBuffers = buffers;
84 desc->cBuffers = buffer_count;
85 }
86
88 {
92
94 SecBufferDesc BuffDesc;
95 SecBuffer Buffer;
96 SECURITY_STATUS sspi_ret;
97 SecBuffer outbuf;
98 SecBufferDesc outbuf_desc;
99
100 DWORD dwshut = SCHANNEL_SHUTDOWN;
103
104 sspi_ret = ApplyControlToken(&
c->ctxt_handle, &BuffDesc);
105 if (sspi_ret != SEC_E_OK)
107
110
111 sspi_ret = InitializeSecurityContext(&
c->cred_handle, &
c->ctxt_handle,
s->host,
112 c->request_flags, 0, 0,
NULL, 0, &
c->ctxt_handle,
113 &outbuf_desc, &
c->context_flags, &
c->ctxt_timestamp);
114 if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) {
116 FreeContextBuffer(outbuf.pvBuffer);
117 if (
ret < 0 ||
ret != outbuf.cbBuffer)
119 }
120
122 }
123 return 0;
124 }
125
127 {
129
131
132 DeleteSecurityContext(&
c->ctxt_handle);
133 FreeCredentialsHandle(&
c->cred_handle);
134
136 c->enc_buf_size =
c->enc_buf_offset = 0;
137
139 c->dec_buf_size =
c->dec_buf_offset = 0;
140
142 return 0;
143 }
144
146 {
149 SECURITY_STATUS sspi_ret;
150 SecBuffer outbuf[3] = { 0 };
151 SecBufferDesc outbuf_desc;
152 SecBuffer inbuf[2];
153 SecBufferDesc inbuf_desc;
155
156 if (
c->enc_buf ==
NULL) {
157 c->enc_buf_offset = 0;
162 }
163
164 if (
c->dec_buf ==
NULL) {
165 c->dec_buf_offset = 0;
170 }
171
172 while (1) {
177 c->enc_buf_size =
c->enc_buf_offset = 0;
179 }
180 }
181
184 c->enc_buf_size -
c->enc_buf_offset);
188 }
189 c->enc_buf_offset +=
ret;
190 }
191
192 /* input buffers */
196
197 if (inbuf[0].pvBuffer ==
NULL) {
201 }
202
203 memcpy(inbuf[0].pvBuffer,
c->enc_buf,
c->enc_buf_offset);
204
205 /* output buffers */
210
211 sspi_ret = InitializeSecurityContext(&
c->cred_handle, &
c->ctxt_handle,
s->host,
c->request_flags,
212 0, 0, &inbuf_desc, 0,
NULL, &outbuf_desc, &
c->context_flags,
215
216 if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
219 continue;
220 }
221
222 /* remote requests a client certificate - attempt to continue without one anyway */
223 if (sspi_ret == SEC_I_INCOMPLETE_CREDENTIALS &&
224 !(
c->request_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
226 c->request_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
228 continue;
229 }
230
231 /* continue handshake */
232 if (sspi_ret == SEC_I_CONTINUE_NEEDED || sspi_ret == SEC_E_OK) {
233 for (
i = 0;
i < 3;
i++) {
234 if (outbuf[
i].BufferType == SECBUFFER_TOKEN && outbuf[
i].cbBuffer > 0) {
236 if (
ret < 0 ||
ret != outbuf[
i].cbBuffer) {
240 }
241 }
242
243 if (outbuf[
i].pvBuffer !=
NULL) {
244 FreeContextBuffer(outbuf[
i].pvBuffer);
245 outbuf[
i].pvBuffer =
NULL;
246 }
247 }
248 } else {
249 if (sspi_ret == SEC_E_WRONG_PRINCIPAL)
251 else
255 }
256
257 if (inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
258 if (
c->enc_buf_offset > inbuf[1].cbBuffer) {
259 memmove(
c->enc_buf, (
c->enc_buf +
c->enc_buf_offset) - inbuf[1].cbBuffer,
260 inbuf[1].cbBuffer);
261 c->enc_buf_offset = inbuf[1].cbBuffer;
262 if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
264 continue;
265 }
266 }
267 } else {
268 c->enc_buf_offset = 0;
269 }
270
271 if (sspi_ret == SEC_I_CONTINUE_NEEDED) {
273 continue;
274 }
275
276 break;
277 }
278
279 return 0;
280
282 /* free any remaining output data */
283 for (
i = 0;
i < 3;
i++) {
284 if (outbuf[
i].pvBuffer !=
NULL) {
285 FreeContextBuffer(outbuf[
i].pvBuffer);
286 outbuf[
i].pvBuffer =
NULL;
287 }
288 }
289
291 }
292
294 {
297 SecBuffer outbuf;
298 SecBufferDesc outbuf_desc;
299 SECURITY_STATUS sspi_ret;
301
304
305 c->request_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
306 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
307 ISC_REQ_STREAM;
308
309 sspi_ret = InitializeSecurityContext(&
c->cred_handle,
NULL,
s->host,
c->request_flags, 0, 0,
310 NULL, 0, &
c->ctxt_handle, &outbuf_desc, &
c->context_flags,
312 if (sspi_ret != SEC_I_CONTINUE_NEEDED) {
316 }
317
319 FreeContextBuffer(outbuf.pvBuffer);
320 if (
ret < 0 ||
ret != outbuf.cbBuffer) {
324 }
325
327
329 DeleteSecurityContext(&
c->ctxt_handle);
331 }
332
334 {
337 SECURITY_STATUS sspi_ret;
338 SCHANNEL_CRED schannel_cred = { 0 };
340
343
348 }
349
350 /* SChannel Options */
351 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
352
354 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
355 SCH_CRED_REVOCATION_CHECK_CHAIN;
356 else
357 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
358 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
359 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
360
361 /* Get credential handle */
362 sspi_ret = AcquireCredentialsHandle(
NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND,
365 if (sspi_ret != SEC_E_OK) {
369 }
370
374
376
377 return 0;
378
382 }
383
385 {
388 SECURITY_STATUS sspi_ret = SEC_E_OK;
389 SecBuffer inbuf[4];
390 SecBufferDesc inbuf_desc;
393
394 /* If we have some left-over data from previous network activity,
395 * return it first in case it is enough. It may contain
396 * data that is required to know whether this connection
397 * is still required or not, esp. in case of HTTP keep-alive
398 * connections. */
399 if (
c->dec_buf_offset > 0)
401
402 if (
c->sspi_close_notify)
404
405 if (!
c->connection_closed) {
406 size =
c->enc_buf_size -
c->enc_buf_offset;
407 if (size < SCHANNEL_FREE_BUFFER_SIZE || c->enc_buf_size < min_enc_buf_size) {
409 if (
c->enc_buf_size < min_enc_buf_size)
410 c->enc_buf_size = min_enc_buf_size;
413 c->enc_buf_size =
c->enc_buf_offset = 0;
415 }
416 }
417
419 c->enc_buf_size -
c->enc_buf_offset);
421 c->connection_closed = 1;
423 }
else if (
ret < 0) {
426 }
427
428 c->enc_buf_offset +=
ret;
429 }
430
431 while (
c->enc_buf_offset > 0 && sspi_ret == SEC_E_OK) {
432 /* input buffer */
434
435 /* additional buffers for possible output */
440
441 sspi_ret = DecryptMessage(&
c->ctxt_handle, &inbuf_desc, 0,
NULL);
442 if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_RENEGOTIATE ||
443 sspi_ret == SEC_I_CONTEXT_EXPIRED) {
444 /* handle decrypted data */
445 if (inbuf[1].BufferType == SECBUFFER_DATA) {
446 /* grow buffer if needed */
449 if (
c->dec_buf_size -
c->dec_buf_offset <
size ||
c->dec_buf_size <
len) {
450 c->dec_buf_size =
c->dec_buf_offset +
size;
451 if (
c->dec_buf_size <
len)
452 c->dec_buf_size =
len;
455 c->dec_buf_size =
c->dec_buf_offset = 0;
457 }
458 }
459
460 /* copy decrypted data to buffer */
461 size = inbuf[1].cbBuffer;
463 memcpy(
c->dec_buf +
c->dec_buf_offset, inbuf[1].pvBuffer,
size);
464 c->dec_buf_offset +=
size;
465 }
466 }
467 if (inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
468 if (
c->enc_buf_offset > inbuf[3].cbBuffer) {
469 memmove(
c->enc_buf, (
c->enc_buf +
c->enc_buf_offset) - inbuf[3].cbBuffer,
470 inbuf[3].cbBuffer);
471 c->enc_buf_offset = inbuf[3].cbBuffer;
472 }
473 } else
474 c->enc_buf_offset = 0;
475
476 if (sspi_ret == SEC_I_RENEGOTIATE) {
477 if (
c->enc_buf_offset) {
481 }
482
487 }
488 sspi_ret = SEC_E_OK;
489 continue;
490 } else if (sspi_ret == SEC_I_CONTEXT_EXPIRED) {
491 c->sspi_close_notify = 1;
492 if (!
c->connection_closed) {
493 c->connection_closed = 1;
495 }
498 }
499 } else if (sspi_ret == SEC_E_INCOMPLETE_MESSAGE) {
502 } else {
506 }
507 }
508
510
514 memcpy(buf,
c->dec_buf,
size);
515 memmove(
c->dec_buf,
c->dec_buf +
size,
c->dec_buf_offset -
size);
516 c->dec_buf_offset -=
size;
517
519 }
520
521 if (
ret == 0 && !
c->connection_closed)
523
525 }
526
528 {
531 SECURITY_STATUS sspi_ret;
532 int ret = 0, data_size;
534 SecBuffer outbuf[4];
535 SecBufferDesc outbuf_desc;
536
537 if (
c->sizes.cbMaximumMessage == 0) {
538 sspi_ret = QueryContextAttributes(&
c->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &
c->sizes);
539 if (sspi_ret != SEC_E_OK)
541 }
542
543 /* limit how much data we can consume */
545
546 data_size =
c->sizes.cbHeader +
len +
c->sizes.cbTrailer;
550
552 data,
c->sizes.cbHeader);
560
561 memcpy(outbuf[1].pvBuffer, buf,
len);
562
563 sspi_ret = EncryptMessage(&
c->ctxt_handle, 0, &outbuf_desc, 0);
564 if (sspi_ret == SEC_E_OK) {
565 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
570 goto done;
571 }
572 } else {
574 if (sspi_ret == SEC_E_INSUFFICIENT_MEMORY)
576 else
578 goto done;
579 }
580
581 done:
583 return ret < 0 ?
ret : outbuf[1].cbBuffer;
584 }
585
587 {
590 }
591
593 {
596 }
597
601 };
602
608 };
609
621 };