rtsp-stream: obtain stream position from pad
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-auth.c
index 68ab719..85758c4 100644 (file)
@@ -19,7 +19,7 @@
 /**
  * SECTION:rtsp-auth
  * @short_description: Authentication and authorization
- * @see_also: #GstRTSPPermission, #GstRTSPtoken
+ * @see_also: #GstRTSPPermissions, #GstRTSPToken
  *
  * The #GstRTSPAuth object is responsible for checking if the current user is
  * allowed to perform requested actions. The default implementation has some
@@ -30,8 +30,8 @@
  *
  * The RTSP server will call gst_rtsp_auth_check() with a string describing the
  * check to perform. The possible checks are prefixed with
- * #GST_RTSP_AUTH_CHECK_*. Depending on the check, the default implementation
- * will use the current #GstRTSPToken, #GstRTSPClientState and
+ * GST_RTSP_AUTH_CHECK_*. Depending on the check, the default implementation
+ * will use the current #GstRTSPToken, #GstRTSPContext and
  * #GstRTSPPermissions on the object to check if an operation is allowed.
  *
  * The default #GstRTSPAuth object has support for basic authentication. With
@@ -59,17 +59,60 @@ struct _GstRTSPAuthPrivate
 
   /* the TLS certificate */
   GTlsCertificate *certificate;
+  GTlsDatabase *database;
+  GTlsAuthenticationMode mode;
   GHashTable *basic;            /* protected by lock */
+  GHashTable *digest, *nonces;  /* protected by lock */
+  guint64 last_nonce_check;
   GstRTSPToken *default_token;
   GstRTSPMethod methods;
+  GstRTSPAuthMethod auth_methods;
 };
 
+typedef struct
+{
+  GstRTSPToken *token;
+  gchar *pass;
+} GstRTSPDigestEntry;
+
+typedef struct
+{
+  gchar *nonce;
+  gchar *ip;
+  guint64 timestamp;
+  gpointer client;
+} GstRTSPDigestNonce;
+
+static void
+gst_rtsp_digest_entry_free (GstRTSPDigestEntry * entry)
+{
+  gst_rtsp_token_unref (entry->token);
+  g_free (entry->pass);
+  g_free (entry);
+}
+
+static void
+gst_rtsp_digest_nonce_free (GstRTSPDigestNonce * nonce)
+{
+  g_free (nonce->nonce);
+  g_free (nonce->ip);
+  g_free (nonce);
+}
+
 enum
 {
   PROP_0,
   PROP_LAST
 };
 
+enum
+{
+  SIGNAL_ACCEPT_CERTIFICATE,
+  SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
 GST_DEBUG_CATEGORY_STATIC (rtsp_auth_debug);
 #define GST_CAT_DEFAULT rtsp_auth_debug
 
@@ -79,10 +122,12 @@ static void gst_rtsp_auth_set_property (GObject * object, guint propid,
     const GValue * value, GParamSpec * pspec);
 static void gst_rtsp_auth_finalize (GObject * obj);
 
-static gboolean default_authenticate (GstRTSPAuth * auth,
-    GstRTSPClientState * state);
-static gboolean default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
+static gboolean default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx);
+static gboolean default_check (GstRTSPAuth * auth, GstRTSPContext * ctx,
     const gchar * check);
+static void default_generate_authenticate_header (GstRTSPAuth * auth,
+    GstRTSPContext * ctx);
+
 
 G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
 
@@ -101,8 +146,34 @@ gst_rtsp_auth_class_init (GstRTSPAuthClass * klass)
 
   klass->authenticate = default_authenticate;
   klass->check = default_check;
+  klass->generate_authenticate_header = default_generate_authenticate_header;
 
   GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
+
+  /**
+   * GstRTSPAuth::accept-certificate:
+   * @auth: a #GstRTSPAuth
+   * @connection: a #GTlsConnection
+   * @peer_cert: the peer's #GTlsCertificate
+   * @errors: the problems with @peer_cert.
+   *
+   * Emitted during the TLS handshake after the client certificate has
+   * been received. See also gst_rtsp_auth_set_tls_authentication_mode().
+   *
+   * Returns: %TRUE to accept @peer_cert (which will also
+   * immediately end the signal emission). %FALSE to allow the signal
+   * emission to continue, which will cause the handshake to fail if
+   * no one else overrides it.
+   *
+   * Since: 1.6
+   */
+  signals[SIGNAL_ACCEPT_CERTIFICATE] = g_signal_new ("accept-certificate",
+      G_TYPE_FROM_CLASS (gobject_class),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstRTSPAuthClass, accept_certificate),
+      g_signal_accumulator_true_handled, NULL, g_cclosure_marshal_generic,
+      G_TYPE_BOOLEAN, 3, G_TYPE_TLS_CONNECTION, G_TYPE_TLS_CERTIFICATE,
+      G_TYPE_TLS_CERTIFICATE_FLAGS);
 }
 
 static void
@@ -116,9 +187,14 @@ gst_rtsp_auth_init (GstRTSPAuth * auth)
 
   priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
       (GDestroyNotify) gst_rtsp_token_unref);
+  priv->digest = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+      (GDestroyNotify) gst_rtsp_digest_entry_free);
+  priv->nonces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+      (GDestroyNotify) gst_rtsp_digest_nonce_free);
 
   /* bitwise or of all methods that need authentication */
   priv->methods = 0;
+  priv->auth_methods = GST_RTSP_AUTH_BASIC;
 }
 
 static void
@@ -131,7 +207,11 @@ gst_rtsp_auth_finalize (GObject * obj)
 
   if (priv->certificate)
     g_object_unref (priv->certificate);
+  if (priv->database)
+    g_object_unref (priv->database);
   g_hash_table_unref (priv->basic);
+  g_hash_table_unref (priv->digest);
+  g_hash_table_unref (priv->nonces);
   g_mutex_clear (&priv->lock);
 
   G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
@@ -162,7 +242,7 @@ gst_rtsp_auth_set_property (GObject * object, guint propid,
  *
  * Create a new #GstRTSPAuth instance.
  *
- * Returns: a new #GstRTSPAuth
+ * Returns: (transfer full): a new #GstRTSPAuth
  */
 GstRTSPAuth *
 gst_rtsp_auth_new (void)
@@ -177,7 +257,7 @@ gst_rtsp_auth_new (void)
 /**
  * gst_rtsp_auth_set_tls_certificate:
  * @auth: a #GstRTSPAuth
- * @cert: (allow none): a #GTlsCertificate
+ * @cert: (transfer none) (allow-none): a #GTlsCertificate
  *
  * Set the TLS certificate for the auth. Client connections will only
  * be accepted when TLS is negotiated.
@@ -232,9 +312,121 @@ gst_rtsp_auth_get_tls_certificate (GstRTSPAuth * auth)
 }
 
 /**
+ * gst_rtsp_auth_set_tls_database:
+ * @auth: a #GstRTSPAuth
+ * @database: (transfer none) (allow-none): a #GTlsDatabase
+ *
+ * Sets the certificate database that is used to verify peer certificates.
+ * If set to %NULL (the default), then peer certificate validation will always
+ * set the %G_TLS_CERTIFICATE_UNKNOWN_CA error.
+ *
+ * Since 1.6
+ */
+void
+gst_rtsp_auth_set_tls_database (GstRTSPAuth * auth, GTlsDatabase * database)
+{
+  GstRTSPAuthPrivate *priv;
+  GTlsDatabase *old;
+
+  g_return_if_fail (GST_IS_RTSP_AUTH (auth));
+
+  priv = auth->priv;
+
+  if (database)
+    g_object_ref (database);
+
+  g_mutex_lock (&priv->lock);
+  old = priv->database;
+  priv->database = database;
+  g_mutex_unlock (&priv->lock);
+
+  if (old)
+    g_object_unref (old);
+}
+
+/**
+ * gst_rtsp_auth_get_tls_database:
+ * @auth: a #GstRTSPAuth
+ *
+ * Get the #GTlsDatabase used for verifying client certificate.
+ *
+ * Returns: (transfer full): the #GTlsDatabase of @auth. g_object_unref() after
+ * usage.
+ * Since: 1.6
+ */
+GTlsDatabase *
+gst_rtsp_auth_get_tls_database (GstRTSPAuth * auth)
+{
+  GstRTSPAuthPrivate *priv;
+  GTlsDatabase *result;
+
+  g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), NULL);
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  if ((result = priv->database))
+    g_object_ref (result);
+  g_mutex_unlock (&priv->lock);
+
+  return result;
+}
+
+/**
+ * gst_rtsp_auth_set_tls_authentication_mode:
+ * @auth: a #GstRTSPAuth
+ * @mode: (transfer none) (allow-none): a #GTlsAuthenticationMode
+ *
+ * The #GTlsAuthenticationMode to set on the underlying GTlsServerConnection.
+ * When set to another value than %G_TLS_AUTHENTICATION_NONE,
+ * #GstRTSPAuth::accept-certificate signal will be emitted and must be handled.
+ *
+ * Since: 1.6
+ */
+void
+gst_rtsp_auth_set_tls_authentication_mode (GstRTSPAuth * auth,
+    GTlsAuthenticationMode mode)
+{
+  GstRTSPAuthPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_AUTH (auth));
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  priv->mode = mode;
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_auth_get_tls_authentication_mode:
+ * @auth: a #GstRTSPAuth
+ *
+ * Get the #GTlsAuthenticationMode.
+ *
+ * Returns: (transfer full): the #GTlsAuthenticationMode.
+ */
+GTlsAuthenticationMode
+gst_rtsp_auth_get_tls_authentication_mode (GstRTSPAuth * auth)
+{
+  GstRTSPAuthPrivate *priv;
+  GTlsAuthenticationMode result;
+
+  g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), G_TLS_AUTHENTICATION_NONE);
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  result = priv->mode;
+  g_mutex_unlock (&priv->lock);
+
+  return result;
+}
+
+/**
  * gst_rtsp_auth_set_default_token:
  * @auth: a #GstRTSPAuth
- * @token: (allow none): a #GstRTSPToken
+ * @token: (transfer none) (allow-none): a #GstRTSPToken
  *
  * Set the default #GstRTSPToken to @token in @auth. The default token will
  * be used for unauthenticated users.
@@ -265,7 +457,7 @@ gst_rtsp_auth_set_default_token (GstRTSPAuth * auth, GstRTSPToken * token)
  * gst_rtsp_auth_get_default_token:
  * @auth: a #GstRTSPAuth
  *
- * Get the default token for @auth. This token will be used for unauthorized
+ * Get the default token for @auth. This token will be used for unauthenticated
  * users.
  *
  * Returns: (transfer full): the #GstRTSPToken of @auth. gst_rtsp_token_unref() after
@@ -293,7 +485,7 @@ gst_rtsp_auth_get_default_token (GstRTSPAuth * auth)
  * gst_rtsp_auth_add_basic:
  * @auth: a #GstRTSPAuth
  * @basic: the basic token
- * @token: authorisation token
+ * @token: (transfer none): authorisation token
  *
  * Add a basic token for the default authentication algorithm that
  * enables the client with privileges listed in @token.
@@ -321,8 +513,7 @@ gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
  * @auth: a #GstRTSPAuth
  * @basic: (transfer none): the basic token
  *
- * Add a basic token for the default authentication algorithm that
- * enables the client qith privileges from @authgroup.
+ * Removes @basic authentication token.
  */
 void
 gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
@@ -339,42 +530,257 @@ gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
   g_mutex_unlock (&priv->lock);
 }
 
+/**
+ * gst_rtsp_auth_add_digest:
+ * @auth: a #GstRTSPAuth
+ * @user: the digest user name
+ * @pass: the digest password
+ * @token: (transfer none): authorisation token
+ *
+ * Add a digest @user and @pass for the default authentication algorithm that
+ * enables the client with privileges listed in @token.
+ *
+ * Since: 1.12
+ */
+void
+gst_rtsp_auth_add_digest (GstRTSPAuth * auth, const gchar * user,
+    const gchar * pass, GstRTSPToken * token)
+{
+  GstRTSPAuthPrivate *priv;
+  GstRTSPDigestEntry *entry;
+
+  g_return_if_fail (GST_IS_RTSP_AUTH (auth));
+  g_return_if_fail (user != NULL);
+  g_return_if_fail (pass != NULL);
+  g_return_if_fail (GST_IS_RTSP_TOKEN (token));
+
+  priv = auth->priv;
+
+  entry = g_new0 (GstRTSPDigestEntry, 1);
+  entry->token = gst_rtsp_token_ref (token);
+  entry->pass = g_strdup (pass);
+
+  g_mutex_lock (&priv->lock);
+  g_hash_table_replace (priv->digest, g_strdup (user), entry);
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_auth_remove_digest:
+ * @auth: a #GstRTSPAuth
+ * @user: (transfer none): the digest user name
+ *
+ * Removes a digest user.
+ *
+ * Since: 1.12
+ */
+void
+gst_rtsp_auth_remove_digest (GstRTSPAuth * auth, const gchar * user)
+{
+  GstRTSPAuthPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_AUTH (auth));
+  g_return_if_fail (user != NULL);
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  g_hash_table_remove (priv->digest, user);
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_auth_set_supported_methods:
+ * @auth: a #GstRTSPAuth
+ * @methods: supported methods
+ *
+ * Sets the supported authentication @methods for @auth.
+ *
+ * Since: 1.12
+ */
+void
+gst_rtsp_auth_set_supported_methods (GstRTSPAuth * auth,
+    GstRTSPAuthMethod methods)
+{
+  GstRTSPAuthPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_AUTH (auth));
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  priv->auth_methods = methods;
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_auth_get_supported_methods:
+ * @auth: a #GstRTSPAuth
+ *
+ * Gets the supported authentication methods of @auth.
+ *
+ * Returns: The supported authentication methods
+ *
+ * Since: 1.12
+ */
+GstRTSPAuthMethod
+gst_rtsp_auth_get_supported_methods (GstRTSPAuth * auth)
+{
+  GstRTSPAuthPrivate *priv;
+  GstRTSPAuthMethod methods;
+
+  g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), 0);
+
+  priv = auth->priv;
+
+  g_mutex_lock (&priv->lock);
+  methods = priv->auth_methods;
+  g_mutex_unlock (&priv->lock);
+
+  return methods;
+}
+
+typedef struct
+{
+  GstRTSPAuth *auth;
+  GstRTSPDigestNonce *nonce;
+} RemoveNonceData;
+
+static void
+remove_nonce (gpointer data, GObject * object)
+{
+  RemoveNonceData *remove_nonce_data = data;
+
+  g_mutex_lock (&remove_nonce_data->auth->priv->lock);
+  g_hash_table_remove (remove_nonce_data->auth->priv->nonces,
+      remove_nonce_data->nonce->nonce);
+  g_mutex_unlock (&remove_nonce_data->auth->priv->lock);
+
+  g_object_unref (remove_nonce_data->auth);
+  g_free (remove_nonce_data);
+}
+
 static gboolean
-default_authenticate (GstRTSPAuth * auth, GstRTSPClientState * state)
+default_digest_auth (GstRTSPAuth * auth, GstRTSPContext * ctx,
+    GstRTSPAuthParam ** param)
+{
+  const gchar *realm = NULL, *user = NULL, *nonce = NULL;
+  const gchar *response = NULL, *uri = NULL;
+  GstRTSPDigestNonce *nonce_entry = NULL;
+  GstRTSPDigestEntry *digest_entry;
+  gchar *expected_response = NULL;
+  gboolean ret = FALSE;
+
+  GST_DEBUG_OBJECT (auth, "check Digest auth");
+
+  if (!param)
+    return ret;
+
+  while (*param) {
+    if (!realm && strcmp ((*param)->name, "realm") == 0 && (*param)->value)
+      realm = (*param)->value;
+    else if (!user && strcmp ((*param)->name, "username") == 0
+        && (*param)->value)
+      user = (*param)->value;
+    else if (!nonce && strcmp ((*param)->name, "nonce") == 0 && (*param)->value)
+      nonce = (*param)->value;
+    else if (!response && strcmp ((*param)->name, "response") == 0
+        && (*param)->value)
+      response = (*param)->value;
+    else if (!uri && strcmp ((*param)->name, "uri") == 0 && (*param)->value)
+      uri = (*param)->value;
+
+    param++;
+  }
+
+  if (!realm || !user || !nonce || !response || !uri)
+    return FALSE;
+
+  g_mutex_lock (&auth->priv->lock);
+  digest_entry = g_hash_table_lookup (auth->priv->digest, user);
+  if (!digest_entry)
+    goto out;
+  nonce_entry = g_hash_table_lookup (auth->priv->nonces, nonce);
+  if (!nonce_entry)
+    goto out;
+
+  if (strcmp (nonce_entry->ip, gst_rtsp_connection_get_ip (ctx->conn)) != 0)
+    goto out;
+  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 (!expected_response || strcmp (response, expected_response) != 0)
+    goto out;
+
+  ctx->token = digest_entry->token;
+  ret = TRUE;
+
+out:
+  if (nonce_entry && !nonce_entry->client) {
+    RemoveNonceData *remove_nonce_data = g_new (RemoveNonceData, 1);
+
+    nonce_entry->client = ctx->client;
+    remove_nonce_data->nonce = nonce_entry;
+    remove_nonce_data->auth = g_object_ref (auth);
+    g_object_weak_ref (G_OBJECT (ctx->client), remove_nonce, remove_nonce_data);
+  }
+  g_mutex_unlock (&auth->priv->lock);
+
+  g_free (expected_response);
+
+  return ret;
+}
+
+static gboolean
+default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx)
 {
   GstRTSPAuthPrivate *priv = auth->priv;
-  GstRTSPResult res;
-  gchar *authorization;
+  GstRTSPAuthCredential **credentials, **credential;
 
   GST_DEBUG_OBJECT (auth, "authenticate");
 
   g_mutex_lock (&priv->lock);
-  /* FIXME, need to ref but we have no way to unref when the state is
+  /* FIXME, need to ref but we have no way to unref when the ctx is
    * popped */
-  state->token = priv->default_token;
+  ctx->token = priv->default_token;
   g_mutex_unlock (&priv->lock);
 
-  res =
-      gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_AUTHORIZATION,
-      &authorization, 0);
-  if (res < 0)
+  credentials =
+      gst_rtsp_message_parse_auth_credentials (ctx->request,
+      GST_RTSP_HDR_AUTHORIZATION);
+  if (!credentials)
     goto no_auth;
 
   /* parse type */
-  if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
-    GstRTSPToken *token;
-
-    GST_DEBUG_OBJECT (auth, "check Basic auth");
-    g_mutex_lock (&priv->lock);
-    if ((token = g_hash_table_lookup (priv->basic, &authorization[6]))) {
-      GST_DEBUG_OBJECT (auth, "setting token %p", token);
-      state->token = token;
+  credential = credentials;
+  while (*credential) {
+    if ((*credential)->scheme == GST_RTSP_AUTH_BASIC) {
+      GstRTSPToken *token;
+
+      GST_DEBUG_OBJECT (auth, "check Basic auth");
+      g_mutex_lock (&priv->lock);
+      if ((token =
+              g_hash_table_lookup (priv->basic,
+                  (*credential)->authorization))) {
+        GST_DEBUG_OBJECT (auth, "setting token %p", token);
+        ctx->token = token;
+        g_mutex_unlock (&priv->lock);
+        break;
+      }
+      g_mutex_unlock (&priv->lock);
+    } else if ((*credential)->scheme == GST_RTSP_AUTH_DIGEST) {
+      if (default_digest_auth (auth, ctx, (*credential)->params))
+        break;
     }
-    g_mutex_unlock (&priv->lock);
-  } else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
-    GST_DEBUG_OBJECT (auth, "check Digest auth");
-    /* not implemented yet */
+
+    credential++;
   }
+
+  gst_rtsp_auth_credentials_free (credentials);
   return TRUE;
 
 no_auth:
@@ -385,35 +791,89 @@ no_auth:
 }
 
 static void
-send_response (GstRTSPAuth * auth, GstRTSPStatusCode code,
-    GstRTSPClientState * state)
+default_generate_authenticate_header (GstRTSPAuth * auth, GstRTSPContext * ctx)
+{
+  if (auth->priv->auth_methods & GST_RTSP_AUTH_BASIC) {
+    gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
+        "Basic realm=\"GStreamer RTSP Server\"");
+  }
+
+  if (auth->priv->auth_methods & GST_RTSP_AUTH_DIGEST) {
+    GstRTSPDigestNonce *nonce;
+    gchar *nonce_value, *auth_header;
+
+    nonce_value =
+        g_strdup_printf ("%08x%08x", g_random_int (), g_random_int ());
+
+    auth_header =
+        g_strdup_printf
+        ("Digest realm=\"GStreamer RTSP Server\", nonce=\"%s\"", nonce_value);
+    gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
+        auth_header);
+    g_free (auth_header);
+
+    nonce = g_new0 (GstRTSPDigestNonce, 1);
+    nonce->nonce = g_strdup (nonce_value);
+    nonce->timestamp = g_get_monotonic_time ();
+    nonce->ip = g_strdup (gst_rtsp_connection_get_ip (ctx->conn));
+    g_mutex_lock (&auth->priv->lock);
+    g_hash_table_replace (auth->priv->nonces, nonce_value, nonce);
+
+    if (auth->priv->last_nonce_check == 0)
+      auth->priv->last_nonce_check = nonce->timestamp;
+
+    /* 30 second nonce timeout */
+    if (nonce->timestamp - auth->priv->last_nonce_check >= 30 * G_USEC_PER_SEC) {
+      GHashTableIter iter;
+      gpointer key, value;
+
+      g_hash_table_iter_init (&iter, auth->priv->nonces);
+      while (g_hash_table_iter_next (&iter, &key, &value)) {
+        GstRTSPDigestNonce *tmp = value;
+
+        if (!tmp->client
+            && nonce->timestamp - tmp->timestamp >= 30 * G_USEC_PER_SEC)
+          g_hash_table_iter_remove (&iter);
+      }
+      auth->priv->last_nonce_check = nonce->timestamp;
+    }
+
+    g_mutex_unlock (&auth->priv->lock);
+  }
+}
+
+static void
+send_response (GstRTSPAuth * auth, GstRTSPStatusCode code, GstRTSPContext * ctx)
 {
-  gst_rtsp_message_init_response (state->response, code,
-      gst_rtsp_status_as_text (code), state->request);
+  gst_rtsp_message_init_response (ctx->response, code,
+      gst_rtsp_status_as_text (code), ctx->request);
 
   if (code == GST_RTSP_STS_UNAUTHORIZED) {
-    /* we only have Basic for now */
-    gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
-        "Basic realm=\"GStreamer RTSP Server\"");
+    GstRTSPAuthClass *klass;
+
+    klass = GST_RTSP_AUTH_GET_CLASS (auth);
+
+    if (klass->generate_authenticate_header)
+      klass->generate_authenticate_header (auth, ctx);
   }
-  gst_rtsp_client_send_message (state->client, state->session, state->response);
+  gst_rtsp_client_send_message (ctx->client, ctx->session, ctx->response);
 }
 
 static gboolean
-ensure_authenticated (GstRTSPAuth * auth, GstRTSPClientState * state)
+ensure_authenticated (GstRTSPAuth * auth, GstRTSPContext * ctx)
 {
   GstRTSPAuthClass *klass;
 
   klass = GST_RTSP_AUTH_GET_CLASS (auth);
 
   /* we need a token to check */
-  if (state->token == NULL) {
+  if (ctx->token == NULL) {
     if (klass->authenticate) {
-      if (!klass->authenticate (auth, state))
+      if (!klass->authenticate (auth, ctx))
         goto authenticate_failed;
     }
   }
-  if (state->token == NULL)
+  if (ctx->token == NULL)
     goto no_auth;
 
   return TRUE;
@@ -422,42 +882,62 @@ ensure_authenticated (GstRTSPAuth * auth, GstRTSPClientState * state)
 authenticate_failed:
   {
     GST_DEBUG_OBJECT (auth, "authenticate failed");
-    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
+    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, ctx);
     return FALSE;
   }
 no_auth:
   {
     GST_DEBUG_OBJECT (auth, "no authorization token found");
-    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
+    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, ctx);
     return FALSE;
   }
 }
 
+static gboolean
+accept_certificate_cb (GTlsConnection * conn, GTlsCertificate * peer_cert,
+    GTlsCertificateFlags errors, GstRTSPAuth * auth)
+{
+  gboolean ret = FALSE;
+
+  g_signal_emit (auth, signals[SIGNAL_ACCEPT_CERTIFICATE], 0,
+      conn, peer_cert, errors, &ret);
+
+  return ret;
+}
+
 /* new connection */
 static gboolean
-check_connect (GstRTSPAuth * auth, GstRTSPClientState * state,
-    const gchar * check)
+check_connect (GstRTSPAuth * auth, GstRTSPContext * ctx, const gchar * check)
 {
   GstRTSPAuthPrivate *priv = auth->priv;
+  GTlsConnection *tls;
 
-  if (priv->certificate) {
-    GTlsConnection *tls;
+  /* configure the connection */
 
-    /* configure the connection */
-    tls = gst_rtsp_connection_get_tls (state->conn, NULL);
+  if (priv->certificate) {
+    tls = gst_rtsp_connection_get_tls (ctx->conn, NULL);
     g_tls_connection_set_certificate (tls, priv->certificate);
   }
+
+  if (priv->mode != G_TLS_AUTHENTICATION_NONE) {
+    tls = gst_rtsp_connection_get_tls (ctx->conn, NULL);
+    g_tls_connection_set_database (tls, priv->database);
+    g_object_set (tls, "authentication-mode", priv->mode, NULL);
+    g_signal_connect (tls, "accept-certificate",
+        G_CALLBACK (accept_certificate_cb), auth);
+  }
+
   return TRUE;
 }
 
 /* check url and methods */
 static gboolean
-check_url (GstRTSPAuth * auth, GstRTSPClientState * state, const gchar * check)
+check_url (GstRTSPAuth * auth, GstRTSPContext * ctx, const gchar * check)
 {
   GstRTSPAuthPrivate *priv = auth->priv;
 
-  if ((state->method & priv->methods) != 0)
-    if (!ensure_authenticated (auth, state))
+  if ((ctx->method & priv->methods) != 0)
+    if (!ensure_authenticated (auth, ctx))
       goto not_authenticated;
 
   return TRUE;
@@ -471,19 +951,18 @@ not_authenticated:
 
 /* check access to media factory */
 static gboolean
-check_factory (GstRTSPAuth * auth, GstRTSPClientState * state,
-    const gchar * check)
+check_factory (GstRTSPAuth * auth, GstRTSPContext * ctx, const gchar * check)
 {
   const gchar *role;
   GstRTSPPermissions *perms;
 
-  if (!ensure_authenticated (auth, state))
+  if (!ensure_authenticated (auth, ctx))
     return FALSE;
 
-  if (!(role = gst_rtsp_token_get_string (state->token,
+  if (!(role = gst_rtsp_token_get_string (ctx->token,
               GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE)))
     goto no_media_role;
-  if (!(perms = gst_rtsp_media_factory_get_permissions (state->factory)))
+  if (!(perms = gst_rtsp_media_factory_get_permissions (ctx->factory)))
     goto no_permissions;
 
   if (g_str_equal (check, GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS)) {
@@ -495,61 +974,65 @@ check_factory (GstRTSPAuth * auth, GstRTSPClientState * state,
             GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT))
       goto no_construct;
   }
+
+  gst_rtsp_permissions_unref (perms);
+
   return TRUE;
 
   /* ERRORS */
 no_media_role:
   {
     GST_DEBUG_OBJECT (auth, "no media factory role found");
-    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
+    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, ctx);
     return FALSE;
   }
 no_permissions:
   {
     GST_DEBUG_OBJECT (auth, "no permissions on media factory found");
-    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
+    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, ctx);
     return FALSE;
   }
 no_access:
   {
     GST_DEBUG_OBJECT (auth, "no permissions to access media factory");
-    send_response (auth, GST_RTSP_STS_NOT_FOUND, state);
+    gst_rtsp_permissions_unref (perms);
+    send_response (auth, GST_RTSP_STS_NOT_FOUND, ctx);
     return FALSE;
   }
 no_construct:
   {
     GST_DEBUG_OBJECT (auth, "no permissions to construct media factory");
-    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
+    gst_rtsp_permissions_unref (perms);
+    send_response (auth, GST_RTSP_STS_UNAUTHORIZED, ctx);
     return FALSE;
   }
 }
 
 static gboolean
-check_client_settings (GstRTSPAuth * auth, GstRTSPClientState * state,
+check_client_settings (GstRTSPAuth * auth, GstRTSPContext * ctx,
     const gchar * check)
 {
-  if (!ensure_authenticated (auth, state))
+  if (!ensure_authenticated (auth, ctx))
     return FALSE;
 
-  return gst_rtsp_token_is_allowed (state->token,
+  return gst_rtsp_token_is_allowed (ctx->token,
       GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS);
 }
 
 static gboolean
-default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
-    const gchar * check)
+default_check (GstRTSPAuth * auth, GstRTSPContext * ctx, const gchar * check)
 {
   gboolean res = FALSE;
 
   /* FIXME, use hastable or so */
   if (g_str_equal (check, GST_RTSP_AUTH_CHECK_CONNECT)) {
-    res = check_connect (auth, state, check);
+    res = check_connect (auth, ctx, check);
   } else if (g_str_equal (check, GST_RTSP_AUTH_CHECK_URL)) {
-    res = check_url (auth, state, check);
+    res = check_url (auth, ctx, check);
   } else if (g_str_has_prefix (check, "auth.check.media.factory.")) {
-    res = check_factory (auth, state, check);
+    res = check_factory (auth, ctx, check);
   } else if (g_str_equal (check, GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS)) {
-    res = check_client_settings (auth, state, check);
+    res = check_client_settings (auth, ctx, check);
   }
   return res;
 }
@@ -580,16 +1063,16 @@ gst_rtsp_auth_check (const gchar * check)
 {
   gboolean result = FALSE;
   GstRTSPAuthClass *klass;
-  GstRTSPClientState *state;
+  GstRTSPContext *ctx;
   GstRTSPAuth *auth;
 
   g_return_val_if_fail (check != NULL, FALSE);
 
-  if (!(state = gst_rtsp_client_state_get_current ()))
-    goto no_state;
+  if (!(ctx = gst_rtsp_context_get_current ()))
+    goto no_context;
 
   /* no auth, we don't need to check */
-  if (!(auth = state->auth))
+  if (!(auth = ctx->auth))
     return no_auth_check (check);
 
   klass = GST_RTSP_AUTH_GET_CLASS (auth);
@@ -597,14 +1080,14 @@ gst_rtsp_auth_check (const gchar * check)
   GST_DEBUG_OBJECT (auth, "check authorization '%s'", check);
 
   if (klass->check)
-    result = klass->check (auth, state, check);
+    result = klass->check (auth, ctx, check);
 
   return result;
 
   /* ERRORS */
-no_state:
+no_context:
   {
-    GST_ERROR ("no clientstate found");
+    GST_ERROR ("no context found");
     return FALSE;
   }
 }
@@ -616,8 +1099,8 @@ no_state:
  *
  * Construct a Basic authorisation token from @user and @pass.
  *
- * Returns: the base64 encoding of the string @user:@pass. g_free()
- *    after usage.
+ * Returns: (transfer full): the base64 encoding of the string @user:@pass.
+ * g_free() after usage.
  */
 gchar *
 gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass)