From: Mathieu Duponchelle Date: Wed, 20 Jun 2018 02:37:11 +0000 (+0200) Subject: rtsp-auth: Add support for parsing .htdigest files X-Git-Tag: 1.16.2~106 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5ede2a5c5c89734d195b7d3f9e6ab5fd80bb1d53;p=platform%2Fupstream%2Fgst-rtsp-server.git rtsp-auth: Add support for parsing .htdigest files Passwords are usually not stored in clear text, but instead stored already hashed in a .htdigest file. Add support for parsing such files, add API to allow setting a custom realm in RTSPAuth, and update the digest example. https://bugzilla.gnome.org/show_bug.cgi?id=796637 --- diff --git a/docs/libs/gst-rtsp-server-sections.txt b/docs/libs/gst-rtsp-server-sections.txt index 953bf71..969abf6 100644 --- a/docs/libs/gst-rtsp-server-sections.txt +++ b/docs/libs/gst-rtsp-server-sections.txt @@ -48,11 +48,14 @@ gst_rtsp_auth_get_tls_database gst_rtsp_auth_set_tls_database gst_rtsp_auth_get_tls_authentication_mode gst_rtsp_auth_set_tls_authentication_mode +gst_rtsp_auth_set_realm +gst_rtsp_auth_get_realm gst_rtsp_auth_make_basic gst_rtsp_auth_add_basic gst_rtsp_auth_remove_basic gst_rtsp_auth_add_digest gst_rtsp_auth_remove_digest +gst_rtsp_auth_parse_htdigest gst_rtsp_auth_check gst_rtsp_auth_get_default_token gst_rtsp_auth_set_default_token diff --git a/examples/test-auth-digest.c b/examples/test-auth-digest.c index 6384da3..f779c05 100644 --- a/examples/test-auth-digest.c +++ b/examples/test-auth-digest.c @@ -21,6 +21,18 @@ #include +static gchar *htdigest_path = NULL; +static gchar *realm = NULL; + +static GOptionEntry entries[] = { + {"htdigest-path", 'h', 0, G_OPTION_ARG_STRING, &htdigest_path, + "Path to an htdigest file to parse (default: None)", "PATH"}, + {"realm", 'r', 0, G_OPTION_ARG_STRING, &realm, + "Authentication realm (default: None)", "REALM"}, + {NULL} +}; + + static gboolean remove_func (GstRTSPSessionPool * pool, GstRTSPSession * session, GstRTSPServer * server) @@ -63,8 +75,19 @@ main (int argc, char *argv[]) GstRTSPMediaFactory *factory; GstRTSPAuth *auth; GstRTSPToken *token; - - gst_init (&argc, &argv); + GOptionContext *optctx; + GError *error = NULL; + + optctx = g_option_context_new (NULL); + g_option_context_add_main_entries (optctx, entries, NULL); + g_option_context_add_group (optctx, gst_init_get_option_group ()); + if (!g_option_context_parse (optctx, &argc, &argv, &error)) { + g_printerr ("Error parsing options: %s\n", error->message); + g_option_context_free (optctx); + g_clear_error (&error); + return -1; + } + g_option_context_free (optctx); loop = g_main_loop_new (NULL, FALSE); @@ -142,6 +165,23 @@ main (int argc, char *argv[]) gst_rtsp_auth_add_digest (auth, "user", "password", token); gst_rtsp_token_unref (token); + if (htdigest_path) { + token = + gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING, + "user", NULL); + + if (!gst_rtsp_auth_parse_htdigest (auth, htdigest_path, token)) { + g_printerr ("Could not parse htdigest at %s\n", htdigest_path); + gst_rtsp_token_unref (token); + goto failed; + } + + gst_rtsp_token_unref (token); + } + + if (realm) + gst_rtsp_auth_set_realm (auth, realm); + /* make admin token */ token = gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING, @@ -171,6 +211,10 @@ main (int argc, char *argv[]) g_print ("stream with user:password ready at rtsp://127.0.0.1:8554/test\n"); g_print ("stream with admin:power ready at rtsp://127.0.0.1:8554/test\n"); g_print ("stream with admin2:power2 ready at rtsp://127.0.0.1:8554/test2\n"); + + if (htdigest_path) + g_print ("stream with htdigest users ready at rtsp://127.0.0.1:8554/test\n"); + g_main_loop_run (loop); return 0; diff --git a/gst/rtsp-server/rtsp-auth.c b/gst/rtsp-server/rtsp-auth.c index d019275..02c20e9 100644 --- a/gst/rtsp-server/rtsp-auth.c +++ b/gst/rtsp-server/rtsp-auth.c @@ -67,12 +67,14 @@ struct _GstRTSPAuthPrivate GstRTSPToken *default_token; GstRTSPMethod methods; GstRTSPAuthMethod auth_methods; + gchar *realm; }; typedef struct { GstRTSPToken *token; gchar *pass; + gchar *md5_pass; } GstRTSPDigestEntry; typedef struct @@ -88,6 +90,7 @@ gst_rtsp_digest_entry_free (GstRTSPDigestEntry * entry) { gst_rtsp_token_unref (entry->token); g_free (entry->pass); + g_free (entry->md5_pass); g_free (entry); } @@ -195,6 +198,7 @@ gst_rtsp_auth_init (GstRTSPAuth * auth) /* bitwise or of all methods that need authentication */ priv->methods = 0; priv->auth_methods = GST_RTSP_AUTH_BASIC; + priv->realm = g_strdup ("GStreamer RTSP Server"); } static void @@ -213,6 +217,7 @@ gst_rtsp_auth_finalize (GObject * obj) g_hash_table_unref (priv->digest); g_hash_table_unref (priv->nonces); g_mutex_clear (&priv->lock); + g_free (priv->realm); G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj); } @@ -565,6 +570,99 @@ gst_rtsp_auth_add_digest (GstRTSPAuth * auth, const gchar * user, g_mutex_unlock (&priv->lock); } +/* With auth lock taken */ +static gboolean +update_digest_cb (gchar *key, GstRTSPDigestEntry *entry, GHashTable *digest) +{ + g_hash_table_replace (digest, key, entry); + + return TRUE; +} + +/** + * gst_rtsp_auth_parse_htdigest: + * @path: (type filename): Path to the htdigest file + * @token: (transfer none): authorisation token + * + * Parse the contents of the file at @path and enable the privileges + * listed in @token for the users it describes. + * + * The format of the file is expected to match the format described by + * , + * as output by the `htdigest` command. + * + * Returns: %TRUE if the file was successfully parsed, %FALSE otherwise. + * + * Since: 1.16 + */ +gboolean +gst_rtsp_auth_parse_htdigest (GstRTSPAuth *auth, const gchar *path, + GstRTSPToken *token) +{ + GstRTSPAuthPrivate *priv; + gboolean ret = FALSE; + gchar *line = NULL; + gchar *eol = NULL; + gchar *contents = NULL; + GError *error = NULL; + GHashTable *new_entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gst_rtsp_digest_entry_free); + + + g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (GST_IS_RTSP_TOKEN (token), FALSE); + + priv = auth->priv; + if (!g_file_get_contents (path, &contents, NULL, &error)) { + GST_ERROR_OBJECT(auth, "Could not parse htdigest: %s", error->message); + goto done; + } + + for (line = contents; line && *line; line = eol ? eol + 1 : NULL) { + GstRTSPDigestEntry *entry; + gchar **strv; + eol = strchr (line, '\n'); + + if (eol) + *eol = '\0'; + + strv = g_strsplit (line, ":", -1); + + if (!(strv[0] && strv[1] && strv[2] && !strv[3])) { + GST_ERROR_OBJECT (auth, "Invalid htdigest format"); + g_strfreev (strv); + goto done; + } + + if (strlen (strv[2]) != 32) { + GST_ERROR_OBJECT (auth, "Invalid htdigest format, hash is expected to be 32 characters long"); + g_strfreev (strv); + goto done; + } + + entry = g_new0 (GstRTSPDigestEntry, 1); + entry->token = gst_rtsp_token_ref (token); + entry->md5_pass = g_strdup (strv[2]); + g_hash_table_replace (new_entries, g_strdup (strv[0]), entry); + g_strfreev (strv); + } + + ret = TRUE; + + /* We only update digest if the file was entirely valid */ + g_mutex_lock (&priv->lock); + g_hash_table_foreach_steal (new_entries, (GHRFunc) update_digest_cb, priv->digest); + g_mutex_unlock (&priv->lock); + +done: + if (error) + g_clear_error (&error); + g_free(contents); + g_hash_table_unref (new_entries); + return ret; +} + /** * gst_rtsp_auth_remove_digest: * @auth: a #GstRTSPAuth @@ -709,10 +807,17 @@ default_digest_auth (GstRTSPAuth * auth, GstRTSPContext * ctx, if (nonce_entry->client && nonce_entry->client != ctx->client) goto out; - expected_response = - gst_rtsp_generate_digest_auth_response (NULL, - gst_rtsp_method_as_text (ctx->method), "GStreamer RTSP Server", user, - digest_entry->pass, uri, nonce); + if (digest_entry->md5_pass) { + expected_response = gst_rtsp_generate_digest_auth_response_from_md5 (NULL, + gst_rtsp_method_as_text (ctx->method), digest_entry->md5_pass, + uri, nonce); + } else { + expected_response = + gst_rtsp_generate_digest_auth_response (NULL, + gst_rtsp_method_as_text (ctx->method), realm, user, + digest_entry->pass, uri, nonce); + } + if (!expected_response || strcmp (response, expected_response) != 0) goto out; @@ -794,8 +899,10 @@ static void default_generate_authenticate_header (GstRTSPAuth * auth, GstRTSPContext * ctx) { if (auth->priv->auth_methods & GST_RTSP_AUTH_BASIC) { + gchar *auth_header = g_strdup_printf("Basic realm=\"%s\"", auth->priv->realm); gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE, - "Basic realm=\"GStreamer RTSP Server\""); + auth_header); + g_free (auth_header); } if (auth->priv->auth_methods & GST_RTSP_AUTH_DIGEST) { @@ -807,7 +914,7 @@ default_generate_authenticate_header (GstRTSPAuth * auth, GstRTSPContext * ctx) auth_header = g_strdup_printf - ("Digest realm=\"GStreamer RTSP Server\", nonce=\"%s\"", nonce_value); + ("Digest realm=\"%s\", nonce=\"%s\"", auth->priv->realm, nonce_value); gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE, auth_header); g_free (auth_header); @@ -1117,3 +1224,37 @@ gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass) return result; } + +/** + * gst_rtsp_auth_set_realm: + * + * Set the @realm of @auth + * + * Since: 1.16 + */ +void +gst_rtsp_auth_set_realm (GstRTSPAuth *auth, const gchar *realm) +{ + g_return_if_fail (GST_IS_RTSP_AUTH (auth)); + g_return_if_fail (realm != NULL); + + if (auth->priv->realm) + g_free (auth->priv->realm); + + auth->priv->realm = g_strdup (realm); +} + +/** + * gst_rtsp_auth_get_realm: + * + * Returns: (transfer full): the @realm of @auth + * + * Since: 1.16 + */ +gchar * +gst_rtsp_auth_get_realm (GstRTSPAuth *auth) +{ + g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), NULL); + + return g_strdup(auth->priv->realm); +} diff --git a/gst/rtsp-server/rtsp-auth.h b/gst/rtsp-server/rtsp-auth.h index 3bfe63d..05a3e5a 100644 --- a/gst/rtsp-server/rtsp-auth.h +++ b/gst/rtsp-server/rtsp-auth.h @@ -135,6 +135,14 @@ GstRTSPAuthMethod gst_rtsp_auth_get_supported_methods (GstRTSPAuth *auth); GST_RTSP_SERVER_API gboolean gst_rtsp_auth_check (const gchar *check); +GST_RTSP_SERVER_API +gboolean gst_rtsp_auth_parse_htdigest (GstRTSPAuth *auth, const gchar *path, GstRTSPToken *token); + +GST_RTSP_SERVER_API +void gst_rtsp_auth_set_realm (GstRTSPAuth *auth, const gchar *realm); + +GST_RTSP_SERVER_API +gchar * gst_rtsp_auth_get_realm (GstRTSPAuth *auth); /* helpers */