History

avformat/tls_schannel: fix non-blocking write breaking TLS sessions

Timo Rothenpieler committed 1 year ago

Commit 9b6638e1 parent 9cd86c41 file · +79 −32Browse files

Unified · Split

libavformat/tls_schannel.c+79−32
@@ -608,6 +608,10 @@ typedef struct TLSContext { · 607 unmodified lines
608608 int dec_buf_size;
609609 int dec_buf_offset;
610610
611+ char *send_buf;
612+ int send_buf_size;
613+ int send_buf_offset;
614+
611615 SecPkgContext_StreamSizes sizes;
612616
613617 int connected;
@@ -692,6 +696,35 @@ static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers, · 78 unmodified lines
692696 desc->cBuffers = buffer_count;
693697 }
694698
699+static int tls_process_send_buffer(URLContext *h)
700+{
701+ TLSContext *c = h->priv_data;
702+ TLSShared *s = &c->tls_shared;
703+ URLContext *uc = s->is_dtls ? s->udp : s->tcp;
704+ int ret;
705+
706+ if (!c->send_buf)
707+ return 0;
708+
709+ ret = ffurl_write(uc, c->send_buf + c->send_buf_offset, c->send_buf_size - c->send_buf_offset);
710+ if (ret == AVERROR(EAGAIN)) {
711+ return AVERROR(EAGAIN);
712+ } else if (ret < 0) {
713+ av_log(h, AV_LOG_ERROR, "Writing encrypted data to socket failed\n");
714+ return AVERROR(EIO);
715+ }
716+
717+ c->send_buf_offset += ret;
718+
719+ if (c->send_buf_offset < c->send_buf_size)
720+ return AVERROR(EAGAIN);
721+
722+ av_freep(&c->send_buf);
723+ c->send_buf_size = c->send_buf_offset = 0;
724+
725+ return 0;
726+}
727+
695728 static int tls_shutdown_client(URLContext *h)
696729 {
697730 TLSContext *c = h->priv_data;
@@ -710,6 +743,11 @@ static int tls_shutdown_client(URLContext *h) · 12 unmodified lines
710743 init_sec_buffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
711744 init_sec_buffer_desc(&BuffDesc, &Buffer, 1);
712745
746+ uc->flags &= ~AVIO_FLAG_NONBLOCK;
747+ ret = tls_process_send_buffer(h);
748+ if (ret < 0)
749+ return ret;
750+
713751 sspi_ret = ApplyControlToken(&c->ctxt_handle, &BuffDesc);
714752 if (sspi_ret != SEC_E_OK)
715753 av_log(h, AV_LOG_ERROR, "ApplyControlToken failed\n");
@@ -729,7 +767,6 @@ static int tls_shutdown_client(URLContext *h) · 13 unmodified lines
729767
730768 if (outbuf.pvBuffer) {
731769 if (outbuf.cbBuffer > 0) {
732− uc->flags &= ~AVIO_FLAG_NONBLOCK;
733770 ret = ffurl_write(uc, outbuf.pvBuffer, outbuf.cbBuffer);
734771 if (ret < 0 || ret != outbuf.cbBuffer)
735772 av_log(h, AV_LOG_ERROR, "Failed to send close message\n");
@@ -761,6 +798,9 @@ static int tls_close(URLContext *h) · 25 unmodified lines
761798 av_freep(&c->dec_buf);
762799 c->dec_buf_size = c->dec_buf_offset = 0;
763800
801+ av_freep(&c->send_buf);
802+ c->send_buf_size = c->send_buf_offset = 0;
803+
764804 if (s->is_dtls) {
765805 if (!s->external_sock)
766806 ffurl_closep(&c->tls_shared.udp);
@@ -1264,6 +1304,11 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) · 497 unmodified lines
12641304 goto cleanup;
12651305 }
12661306 sspi_ret = SEC_E_OK;
1307+
1308+ /* if somehow any send data was left, it is now invalid */
1309+ av_freep(&c->send_buf);
1310+ c->send_buf_size = c->send_buf_offset = 0;
1311+
12671312 continue;
12681313 } else if (sspi_ret == SEC_I_CONTEXT_EXPIRED) {
12691314 c->sspi_close_notify = 1;
@@ -1308,10 +1353,16 @@ static int tls_write(URLContext *h, const uint8_t *buf, int len) · 38 unmodified lines
13081353 TLSShared *s = &c->tls_shared;
13091354 URLContext *uc = s->is_dtls ? s->udp : s->tcp;
13101355 SECURITY_STATUS sspi_ret;
1311− int ret = 0, data_size;
1312− uint8_t *data = NULL;
13131356 SecBuffer outbuf[4];
13141357 SecBufferDesc outbuf_desc;
1358+ int ret = 0;
1359+
1360+ uc->flags &= ~AVIO_FLAG_NONBLOCK;
1361+ uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
1362+
1363+ ret = tls_process_send_buffer(h);
1364+ if (ret < 0)
1365+ return ret;
13151366
13161367 if (c->sizes.cbMaximumMessage == 0) {
13171368 sspi_ret = QueryContextAttributes(&c->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &c->sizes);
@@ -1322,50 +1373,46 @@ static int tls_write(URLContext *h, const uint8_t *buf, int len) · 4 unmodified lines
13221373 /* limit how much data we can consume */
13231374 len = FFMIN(len, c->sizes.cbMaximumMessage - c->sizes.cbHeader - c->sizes.cbTrailer);
13241375
1325− data_size = c->sizes.cbHeader + len + c->sizes.cbTrailer;
1326− data = av_malloc(data_size);
1327− if (data == NULL)
1376+ c->send_buf_size = c->sizes.cbHeader + len + c->sizes.cbTrailer;
1377+ c->send_buf = av_malloc(c->send_buf_size);
1378+ if (c->send_buf == NULL)
13281379 return AVERROR(ENOMEM);
13291380
13301381 init_sec_buffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
1331− data, c->sizes.cbHeader);
1382+ c->send_buf, c->sizes.cbHeader);
13321383 init_sec_buffer(&outbuf[1], SECBUFFER_DATA,
1333− data + c->sizes.cbHeader, len);
1384+ c->send_buf + c->sizes.cbHeader, len);
13341385 init_sec_buffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
1335− data + c->sizes.cbHeader + len,
1336− c->sizes.cbTrailer);
1386+ c->send_buf + c->sizes.cbHeader + len,
1387+ c->sizes.cbTrailer);
13371388 init_sec_buffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
13381389 init_sec_buffer_desc(&outbuf_desc, outbuf, 4);
13391390
13401391 memcpy(outbuf[1].pvBuffer, buf, len);
13411392
13421393 sspi_ret = EncryptMessage(&c->ctxt_handle, 0, &outbuf_desc, 0);
1343− if (sspi_ret == SEC_E_OK) {
1344− len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
1345−
1346− uc->flags &= ~AVIO_FLAG_NONBLOCK;
1347− uc->flags |= h->flags & AVIO_FLAG_NONBLOCK;
1348−
1349− ret = ffurl_write(uc, data, len);
1350− if (ret == AVERROR(EAGAIN)) {
1351− goto done;
1352− } else if (ret < 0 || ret != len) {
1353− ret = AVERROR(EIO);
1354− av_log(h, AV_LOG_ERROR, "Writing encrypted data to socket failed\n");
1355− goto done;
1356− }
1357− } else {
1394+ if (sspi_ret != SEC_E_OK) {
1395+ av_freep(&c->send_buf);
13581396 av_log(h, AV_LOG_ERROR, "Encrypting data failed\n");
13591397 if (sspi_ret == SEC_E_INSUFFICIENT_MEMORY)
1360− ret = AVERROR(ENOMEM);
1361− else
1362− ret = AVERROR(EIO);
1363− goto done;
1398+ return AVERROR(ENOMEM);
1399+ return AVERROR(EIO);
1400+ }
1401+
1402+ c->send_buf_size = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
1403+ c->send_buf_offset = 0;
1404+
1405+ ret = tls_process_send_buffer(h);
1406+ if (ret == AVERROR(EAGAIN)) {
1407+ /* We always need to signal that we consumed all (encryped) data since schannel must not
1408+ be fed the same data again. Sending will then be completed next call to this function,
1409+ and EAGAIN returned until all remaining buffer is sent. */
1410+ return outbuf[1].cbBuffer;
1411+ } else if (ret < 0) {
1412+ return ret;
13641413 }
13651414
1366−done:
1367− av_freep(&data);
1368− return ret < 0 ? ret : outbuf[1].cbBuffer;
1415+ return outbuf[1].cbBuffer;
13691416 }
13701417
13711418 static int tls_get_file_handle(URLContext *h)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t