rtsp: Add gst_rtsp_message_parse_auth_credentials() to parse authentication credentials

https://bugzilla.gnome.org/show_bug.cgi?id=774416
This commit is contained in:
Sebastian Dröge 2016-11-18 16:51:26 +02:00
parent 828c8604dd
commit 90b24d34b3
5 changed files with 531 additions and 0 deletions

View file

@ -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
</SECTION>

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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