rtspconnection: add Content-Length limit
authorKristofer Bjorkstrom <kristofb@axis.com>
Wed, 27 Mar 2019 11:30:00 +0000 (12:30 +0100)
committerSebastian Dröge <slomo@coaxion.net>
Mon, 22 Apr 2019 09:01:32 +0000 (09:01 +0000)
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)

gst-libs/gst/rtsp/gstrtspconnection.c
gst-libs/gst/rtsp/gstrtspconnection.h
tests/check/libs/rtspconnection.c

index dba9b7b..d6773b1 100644 (file)
@@ -198,6 +198,8 @@ struct _GstRTSPConnection
   gchar *passwd;
   GHashTable *auth_params;
 
+  guint content_length_limit;
+
   /* TLS */
   GTlsDatabase *tls_database;
   GTlsInteraction *tls_interaction;
@@ -240,7 +242,7 @@ typedef struct
 
   guint line;
   guint8 *body_data;
-  glong body_len;
+  guint body_len;
 } GstRTSPBuilder;
 
 /* function prototypes */
@@ -370,6 +372,8 @@ gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
   newconn->auth_params = NULL;
   newconn->version = 0;
 
+  newconn->content_length_limit = G_MAXUINT;
+
   *conn = newconn;
 
   return GST_RTSP_OK;
@@ -2417,6 +2421,7 @@ build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
         /* we have a regular response */
         if (builder->buffer[0] == '\0') {
           gchar *hdrval;
+          gint64 content_length_parsed = 0;
 
           /* empty line, end of message header */
           /* 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_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
             /* 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);
             /* 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
-             * probably wrong with the length. */
-            if (builder->body_data == NULL)
+             * we need to read next and when allocation fails, we can't read the payload. */
+            if (builder->body_data == NULL) {
+              res = GST_RTSP_ENOMEM;
               goto invalid_body_len;
+            }
 
             builder->body_data[builder->body_len] = '\0';
             builder->offset = 0;
@@ -2549,7 +2564,7 @@ invalid_body_len:
   {
     conn->may_cancel = TRUE;
     GST_DEBUG ("could not allocate body");
-    return GST_RTSP_ERROR;
+    return res;
   }
 invalid_format:
   {
@@ -3237,6 +3252,25 @@ gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
   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:
index b27f813..70498c5 100644 (file)
@@ -189,6 +189,11 @@ GST_RTSP_API
 GstRTSPResult      gst_rtsp_connection_set_qos_dscp   (GstRTSPConnection *conn,
                                                        guint qos_dscp);
 
+/* Content-Length limit */
+GST_RTSP_API
+void               gst_rtsp_connection_set_content_length_limit (GstRTSPConnection *conn,
+                                                                 guint limit);
+
 /* accessors */
 
 GST_RTSP_API
index 56f83e5..ae3e280 100644 (file)
@@ -859,6 +859,75 @@ GST_START_TEST (test_rtspconnection_ip)
 
 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 *
 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_backlog);
   tcase_add_test (tc_chain, test_rtspconnection_ip);
+  tcase_add_test (tc_chain, test_rtspconnection_send_receive_content_length);
 
   return s;
 }