From 90b24d34b31c1e6affe8ecc6fa5f86d23124f0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 18 Nov 2016 16:51:26 +0200 Subject: [PATCH] rtsp: Add gst_rtsp_message_parse_auth_credentials() to parse authentication credentials https://bugzilla.gnome.org/show_bug.cgi?id=774416 --- docs/libs/gst-plugins-base-libs-sections.txt | 6 + gst-libs/gst/rtsp/gstrtspmessage.c | 217 ++++++++++++++ gst-libs/gst/rtsp/gstrtspmessage.h | 21 ++ tests/check/libs/rtsp.c | 285 +++++++++++++++++++ win32/common/libgstrtsp.def | 2 + 5 files changed, 531 insertions(+) diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index a4cb69c4a8..961c876821 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -1682,6 +1682,12 @@ gst_rtsp_message_set_body gst_rtsp_message_take_body gst_rtsp_message_get_body gst_rtsp_message_steal_body + +GstRTSPAuthCredential +GstRTSPAuthParam +gst_rtsp_message_parse_auth_credentials +gst_rtsp_auth_credentials_free + gst_rtsp_message_dump diff --git a/gst-libs/gst/rtsp/gstrtspmessage.c b/gst-libs/gst/rtsp/gstrtspmessage.c index 99eec2efb1..523ddd5c18 100644 --- a/gst-libs/gst/rtsp/gstrtspmessage.c +++ b/gst-libs/gst/rtsp/gstrtspmessage.c @@ -1021,3 +1021,220 @@ gst_rtsp_message_dump (GstRTSPMessage * msg) } return GST_RTSP_OK; } + + +static const gchar * +skip_lws (const gchar * s) +{ + while (g_ascii_isspace (*s)) + s++; + return s; +} + +static const gchar * +skip_commas (const gchar * s) +{ + /* The grammar allows for multiple commas */ + while (g_ascii_isspace (*s) || *s == ',') + s++; + return s; +} + +static const gchar * +skip_scheme (const gchar * s) +{ + while (*s && !g_ascii_isspace (*s)) + s++; + return s; +} + +static const gchar * +skip_item (const gchar * s) +{ + gboolean quoted = FALSE; + + /* A list item ends at the last non-whitespace character + * before a comma which is not inside a quoted-string. Or at + * the end of the string. + */ + while (*s) { + if (*s == '"') { + quoted = !quoted; + } else if (quoted) { + if (*s == '\\' && *(s + 1)) + s++; + } else { + if (*s == ',' || g_ascii_isspace (*s)) + break; + } + s++; + } + + return s; +} + +static void +decode_quoted_string (gchar * quoted_string) +{ + gchar *src, *dst; + + src = quoted_string + 1; + dst = quoted_string; + while (*src && *src != '"') { + if (*src == '\\' && *(src + 1)) + src++; + *dst++ = *src++; + } + *dst = '\0'; +} + +static void +parse_auth_credentials (GPtrArray * auth_credentials, const gchar * header, + GstRTSPHeaderField field) +{ + while (header[0] != '\0') { + const gchar *end; + GstRTSPAuthCredential *auth_credential; + + /* Skip whitespace at the start of the string */ + header = skip_lws (header); + if (header[0] == '\0') + break; + + /* Skip until end of string or whitespace: end of scheme */ + end = skip_scheme (header); + + auth_credential = g_new0 (GstRTSPAuthCredential, 1); + + if (g_ascii_strncasecmp (header, "basic", 5) == 0) { + auth_credential->scheme = GST_RTSP_AUTH_BASIC; + } else if (g_ascii_strncasecmp (header, "digest", 6) == 0) { + auth_credential->scheme = GST_RTSP_AUTH_DIGEST; + } else { + /* Not supported, skip */ + g_free (auth_credential); + header = end; + continue; + } + + /* Basic Authorization request has only an unformated blurb following, all + * other variants have comma-separated name=value pairs */ + if (end[0] != '\0' && field == GST_RTSP_HDR_AUTHORIZATION + && auth_credential->scheme == GST_RTSP_AUTH_BASIC) { + auth_credential->authorization = g_strdup (end + 1); + header = end; + } else if (end[0] != '\0') { + GPtrArray *params; + + params = g_ptr_array_new (); + + /* Space or start of param */ + header = end; + + /* Parse a header whose content is described by RFC2616 as + * "#something", where "something" does not itself contain commas, + * except as part of quoted-strings, into a list of allocated strings. + */ + while (*header) { + const gchar *item_end; + const gchar *eq; + + header = skip_commas (header); + item_end = skip_item (header); + + for (eq = header; *eq != '\0' && *eq != '=' && eq < item_end; eq++); + if (eq[0] == '=') { + GstRTSPAuthParam *auth_param = g_new0 (GstRTSPAuthParam, 1); + const gchar *value; + + /* have an actual param */ + auth_param->name = g_strndup (header, eq - header); + + value = eq + 1; + value = skip_lws (value); + auth_param->value = g_strndup (value, item_end - value); + if (value[0] == '"') + decode_quoted_string (auth_param->value); + + g_ptr_array_add (params, auth_param); + header = item_end; + } else { + /* at next scheme, header at start of it */ + break; + } + } + if (params->len) + g_ptr_array_add (params, NULL); + auth_credential->params = + (GstRTSPAuthParam **) g_ptr_array_free (params, FALSE); + } else { + header = end; + } + g_ptr_array_add (auth_credentials, auth_credential); + + /* WWW-Authenticate allows multiple, Authorization allows one */ + if (field == GST_RTSP_HDR_AUTHORIZATION) + break; + } +} + +/** + * gst_rtsp_message_parse_auth_credentials: + * @msg: a #GstRTSPMessage + * @field: a #GstRTSPHeaderField + * + * Parses the credentials given in a WWW-Authenticate or Authorization header. + * + * Returns: %NULL-terminated array of GstRTSPAuthCredential or %NULL. + */ +GstRTSPAuthCredential ** +gst_rtsp_message_parse_auth_credentials (GstRTSPMessage * msg, + GstRTSPHeaderField field) +{ + gchar *header; + GPtrArray *auth_credentials; + gint i; + + g_return_val_if_fail (msg != NULL, NULL); + + auth_credentials = g_ptr_array_new (); + + i = 0; + while (gst_rtsp_message_get_header (msg, field, &header, i) == GST_RTSP_OK) { + parse_auth_credentials (auth_credentials, header, field); + i++; + } + + if (auth_credentials->len) + g_ptr_array_add (auth_credentials, NULL); + + return (GstRTSPAuthCredential **) g_ptr_array_free (auth_credentials, FALSE); +} + +void +gst_rtsp_auth_credentials_free (GstRTSPAuthCredential ** credentials) +{ + GstRTSPAuthCredential **p; + + if (!credentials) + return; + + p = credentials; + while (*p) { + GstRTSPAuthParam **param = (*p)->params; + + if (param) { + while (*param) { + g_free ((*param)->name); + g_free ((*param)->value); + g_free (*param); + param++; + } + g_free ((*p)->params); + } + g_free (*p); + p++; + } + + g_free (credentials); +} diff --git a/gst-libs/gst/rtsp/gstrtspmessage.h b/gst-libs/gst/rtsp/gstrtspmessage.h index 6f6ea2d609..b63abec230 100644 --- a/gst-libs/gst/rtsp/gstrtspmessage.h +++ b/gst-libs/gst/rtsp/gstrtspmessage.h @@ -199,6 +199,27 @@ GstRTSPResult gst_rtsp_message_steal_body (GstRTSPMessage *msg, guint8 **data, guint *size); +typedef struct _GstRTSPAuthCredential GstRTSPAuthCredential; +typedef struct _GstRTSPAuthParam GstRTSPAuthParam; +struct _GstRTSPAuthCredential { + GstRTSPAuthMethod scheme; + + /* For Basic/Digest WWW-Authenticate and Digest + * Authorization */ + GstRTSPAuthParam **params; /* NULL terminated */ + + /* For Basic Authorization */ + gchar *authorization; +}; + +struct _GstRTSPAuthParam { + gchar *name; + gchar *value; +}; + +GstRTSPAuthCredential ** gst_rtsp_message_parse_auth_credentials (GstRTSPMessage * msg, GstRTSPHeaderField field); +void gst_rtsp_auth_credentials_free (GstRTSPAuthCredential ** credentials); + /* debug */ GstRTSPResult gst_rtsp_message_dump (GstRTSPMessage *msg); diff --git a/tests/check/libs/rtsp.c b/tests/check/libs/rtsp.c index ba23f0c3b5..4ce901665b 100644 --- a/tests/check/libs/rtsp.c +++ b/tests/check/libs/rtsp.c @@ -652,6 +652,290 @@ GST_START_TEST (test_rtsp_message) GST_END_TEST; +GST_START_TEST (test_rtsp_message_auth_credentials) +{ + GstRTSPMessage *msg; + GstRTSPResult res; + GstRTSPAuthCredential **credentials; + GstRTSPAuthCredential **credential; + GstRTSPAuthParam **param; + + /* Simple basic auth, no params */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, "Basic"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple basic auth, digest auth, no params */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic Digest"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param == NULL); + credential++; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_DIGEST); + param = (*credential)->params; + fail_unless (param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple basic auth */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic foo=\"bar\", baz=foo"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Two simple basic auth headers */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic foo=\"bar\", baz=foo"); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic foo1=\"bar\", baz1=foo"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo1"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz1"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple basic auth, digest auth in one header */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic foo=\"bar\", baz=foo Digest foo1=\"bar\", baz1=foo"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_DIGEST); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo1"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz1"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple basic auth, digest auth in one header, with random commas and spaces */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_WWW_AUTHENTICATE, + "Basic foo=\"bar\",, , baz=foo, Digest , foo1=\"bar\",, baz1=foo"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, + GST_RTSP_HDR_WWW_AUTHENTICATE); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_DIGEST); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo1"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz1"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple basic auth */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_AUTHORIZATION, + "Basic foobarbaz"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, GST_RTSP_HDR_AUTHORIZATION); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_BASIC); + param = (*credential)->params; + fail_unless (param == NULL); + fail_unless_equals_string ((*credential)->authorization, "foobarbaz"); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); + + /* Simple digest auth */ + res = gst_rtsp_message_new_request (&msg, GST_RTSP_PLAY, + "rtsp://foo.bar:8554/test"); + fail_unless_equals_int (res, GST_RTSP_OK); + res = + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_AUTHORIZATION, + "Digest foo=\"bar\" baz=foo"); + credentials = + gst_rtsp_message_parse_auth_credentials (msg, GST_RTSP_HDR_AUTHORIZATION); + fail_unless (credentials != NULL); + + credential = credentials; + fail_unless_equals_int ((*credential)->scheme, GST_RTSP_AUTH_DIGEST); + param = (*credential)->params; + fail_unless (param != NULL); + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "foo"); + fail_unless_equals_string ((*param)->value, "bar"); + param++; + fail_unless (*param != NULL); + fail_unless_equals_string ((*param)->name, "baz"); + fail_unless_equals_string ((*param)->value, "foo"); + param++; + fail_unless (*param == NULL); + credential++; + fail_unless (*credential == NULL); + + gst_rtsp_auth_credentials_free (credentials); + res = gst_rtsp_message_free (msg); + fail_unless_equals_int (res, GST_RTSP_OK); +} + +GST_END_TEST; + static Suite * rtsp_suite (void) { @@ -668,6 +952,7 @@ rtsp_suite (void) tcase_add_test (tc_chain, test_rtsp_range_clock); tcase_add_test (tc_chain, test_rtsp_range_convert); tcase_add_test (tc_chain, test_rtsp_message); + tcase_add_test (tc_chain, test_rtsp_message_auth_credentials); return s; } diff --git a/win32/common/libgstrtsp.def b/win32/common/libgstrtsp.def index 827517cd45..9716e08e14 100644 --- a/win32/common/libgstrtsp.def +++ b/win32/common/libgstrtsp.def @@ -1,4 +1,5 @@ EXPORTS + gst_rtsp_auth_credentials_free gst_rtsp_auth_method_get_type gst_rtsp_connection_accept gst_rtsp_connection_clear_auth_params @@ -76,6 +77,7 @@ EXPORTS gst_rtsp_message_new_data gst_rtsp_message_new_request gst_rtsp_message_new_response + gst_rtsp_message_parse_auth_credentials gst_rtsp_message_parse_data gst_rtsp_message_parse_request gst_rtsp_message_parse_response