rtspconnection: add Content-Length limit

Add the possible to limit the Content-Length
Define an appropriate request size limit and reject requests exceeding
the limit (413 Request Entity Too Large)
This commit is contained in:
Kristofer Bjorkstrom 2019-03-27 12:30:00 +01:00 committed by Sebastian Dröge
parent c70c6c085e
commit 1dea6d52ce
3 changed files with 115 additions and 6 deletions

View file

@ -198,6 +198,8 @@ struct _GstRTSPConnection
gchar *passwd; gchar *passwd;
GHashTable *auth_params; GHashTable *auth_params;
guint content_length_limit;
/* TLS */ /* TLS */
GTlsDatabase *tls_database; GTlsDatabase *tls_database;
GTlsInteraction *tls_interaction; GTlsInteraction *tls_interaction;
@ -240,7 +242,7 @@ typedef struct
guint line; guint line;
guint8 *body_data; guint8 *body_data;
glong body_len; guint body_len;
} GstRTSPBuilder; } GstRTSPBuilder;
/* function prototypes */ /* function prototypes */
@ -370,6 +372,8 @@ gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
newconn->auth_params = NULL; newconn->auth_params = NULL;
newconn->version = 0; newconn->version = 0;
newconn->content_length_limit = G_MAXUINT;
*conn = newconn; *conn = newconn;
return GST_RTSP_OK; return GST_RTSP_OK;
@ -2417,6 +2421,7 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
/* we have a regular response */ /* we have a regular response */
if (builder->buffer[0] == '\0') { if (builder->buffer[0] == '\0') {
gchar *hdrval; gchar *hdrval;
gint64 content_length_parsed = 0;
/* empty line, end of message header */ /* empty line, end of message header */
/* see if there is a Content-Length header, but ignore it if this /* see if there is a Content-Length header, but ignore it if this
@ -2428,13 +2433,23 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
gst_rtsp_message_get_header (message, gst_rtsp_message_get_header (message,
GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) { GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
/* there is, prepare to read the body */ /* there is, prepare to read the body */
builder->body_len = atol (hdrval); errno = 0;
content_length_parsed = g_ascii_strtoll (hdrval, NULL, 10);
if (errno != 0 || content_length_parsed < 0) {
res = GST_RTSP_EPARSE;
goto invalid_body_len;
} else if (content_length_parsed > conn->content_length_limit) {
res = GST_RTSP_ENOMEM;
goto invalid_body_len;
}
builder->body_len = content_length_parsed;
builder->body_data = g_try_malloc (builder->body_len + 1); builder->body_data = g_try_malloc (builder->body_len + 1);
/* we can't do much here, we need the length to know how many bytes /* we can't do much here, we need the length to know how many bytes
* we need to read next and when allocation fails, something is * we need to read next and when allocation fails, we can't read the payload. */
* probably wrong with the length. */ if (builder->body_data == NULL) {
if (builder->body_data == NULL) res = GST_RTSP_ENOMEM;
goto invalid_body_len; goto invalid_body_len;
}
builder->body_data[builder->body_len] = '\0'; builder->body_data[builder->body_len] = '\0';
builder->offset = 0; builder->offset = 0;
@ -2549,7 +2564,7 @@ invalid_body_len:
{ {
conn->may_cancel = TRUE; conn->may_cancel = TRUE;
GST_DEBUG ("could not allocate body"); GST_DEBUG ("could not allocate body");
return GST_RTSP_ERROR; return res;
} }
invalid_format: invalid_format:
{ {
@ -3237,6 +3252,25 @@ gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
return res; return res;
} }
/**
* gst_rtsp_connection_set_content_length_limit:
* @conn: a #GstRTSPConnection
* @limit: Content-Length limit
*
* Configure @conn to use the specified Content-Length limit.
* Both requests and responses are validated. If content-length is
* exceeded, ENOMEM error will be returned.
*
* Since: 1.18
*/
void
gst_rtsp_connection_set_content_length_limit (GstRTSPConnection * conn,
guint limit)
{
g_return_if_fail (conn != NULL);
conn->content_length_limit = limit;
}
/** /**
* gst_rtsp_connection_get_url: * gst_rtsp_connection_get_url:

View file

@ -189,6 +189,11 @@ GST_RTSP_API
GstRTSPResult gst_rtsp_connection_set_qos_dscp (GstRTSPConnection *conn, GstRTSPResult gst_rtsp_connection_set_qos_dscp (GstRTSPConnection *conn,
guint qos_dscp); guint qos_dscp);
/* Content-Length limit */
GST_RTSP_API
void gst_rtsp_connection_set_content_length_limit (GstRTSPConnection *conn,
guint limit);
/* accessors */ /* accessors */
GST_RTSP_API GST_RTSP_API

View file

@ -859,6 +859,75 @@ GST_START_TEST (test_rtspconnection_ip)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_rtspconnection_send_receive_content_length)
{
GSocketConnection *input_conn = NULL;
GSocketConnection *output_conn = NULL;
GSocket *input_sock;
GSocket *output_sock;
GstRTSPConnection *rtsp_output_conn;
GstRTSPConnection *rtsp_input_conn;
GstRTSPMessage *msg;
create_connection (&input_conn, &output_conn);
input_sock = g_socket_connection_get_socket (input_conn);
fail_unless (input_sock != NULL);
output_sock = g_socket_connection_get_socket (output_conn);
fail_unless (output_sock != NULL);
fail_unless (gst_rtsp_connection_create_from_socket (input_sock, "127.0.0.1",
4444, NULL, &rtsp_input_conn) == GST_RTSP_OK);
fail_unless (rtsp_input_conn != NULL);
fail_unless (gst_rtsp_connection_create_from_socket (output_sock, "127.0.0.1",
4444, NULL, &rtsp_output_conn) == GST_RTSP_OK);
fail_unless (rtsp_output_conn != NULL);
/* send request message with to big payload */
fail_unless (gst_rtsp_message_new_request (&msg, GST_RTSP_SETUP,
"rtsp://example.com/") == GST_RTSP_OK);
fail_unless (gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH,
"2000") == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_send (rtsp_output_conn, msg,
NULL) == GST_RTSP_OK);
fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK);
msg = NULL;
/* receive request message, expect ENOMEM */
gst_rtsp_connection_set_content_length_limit (rtsp_input_conn, 1000);
fail_unless (gst_rtsp_message_new (&msg) == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_receive (rtsp_input_conn, msg, NULL) ==
GST_RTSP_ENOMEM);
fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK);
msg = NULL;
/* send request message with negative payload */
fail_unless (gst_rtsp_message_new_request (&msg, GST_RTSP_SETUP,
"rtsp://example.com/") == GST_RTSP_OK);
fail_unless (gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH,
"-2000") == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_send (rtsp_output_conn, msg,
NULL) == GST_RTSP_OK);
fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK);
msg = NULL;
/* receive request message, expect EPARSE */
fail_unless (gst_rtsp_message_new (&msg) == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_receive (rtsp_input_conn, msg, NULL) ==
GST_RTSP_EPARSE);
fail_unless (gst_rtsp_message_free (msg) == GST_RTSP_OK);
msg = NULL;
fail_unless (gst_rtsp_connection_close (rtsp_input_conn) == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_free (rtsp_input_conn) == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_close (rtsp_output_conn) == GST_RTSP_OK);
fail_unless (gst_rtsp_connection_free (rtsp_output_conn) == GST_RTSP_OK);
g_object_unref (input_conn);
g_object_unref (output_conn);
}
GST_END_TEST;
static Suite * static Suite *
rtspconnection_suite (void) rtspconnection_suite (void)
@ -875,6 +944,7 @@ rtspconnection_suite (void)
tcase_add_test (tc_chain, test_rtspconnection_poll); tcase_add_test (tc_chain, test_rtspconnection_poll);
tcase_add_test (tc_chain, test_rtspconnection_backlog); tcase_add_test (tc_chain, test_rtspconnection_backlog);
tcase_add_test (tc_chain, test_rtspconnection_ip); tcase_add_test (tc_chain, test_rtspconnection_ip);
tcase_add_test (tc_chain, test_rtspconnection_send_receive_content_length);
return s; return s;
} }