[gupnp/wip/acl: 12/15] WIP: Initial ACL implementation



commit f8f352158d5c32fc2a25e6b7f21c8f08e7ebb9a6
Author: Jens Georg <mail jensge org>
Date:   Sun Nov 24 01:25:05 2013 +0100

    WIP: Initial ACL implementation

 libgupnp/Makefile.am     |    4 +-
 libgupnp/gupnp-acl.c     |  124 ++++++++++++++++++++++++++++++++++++++++++++++
 libgupnp/gupnp-acl.h     |   98 ++++++++++++++++++++++++++++++++++++
 libgupnp/gupnp-context.c |   59 +++++++++++++++++++++-
 libgupnp/gupnp-context.h |    8 +++
 libgupnp/gupnp-service.c |   32 ++++++++++++
 6 files changed, 323 insertions(+), 2 deletions(-)
---
diff --git a/libgupnp/Makefile.am b/libgupnp/Makefile.am
index 6d30acb..9a16668 100644
--- a/libgupnp/Makefile.am
+++ b/libgupnp/Makefile.am
@@ -44,7 +44,8 @@ libgupnpincdir = $(includedir)/gupnp-1.0/libgupnp
 
 lib_LTLIBRARIES = libgupnp-1.0.la
 
-libgupnpinc_HEADERS = gupnp-context.h                  \
+libgupnpinc_HEADERS = gupnp-acl.h                       \
+                      gupnp-context.h                  \
                      gupnp-context-manager.h           \
                      gupnp-control-point.h             \
                      gupnp-device.h                    \
@@ -63,6 +64,7 @@ libgupnpinc_HEADERS = gupnp-context.h                 \
                      gupnp.h
 
 introspection_sources = $(libgupnpinc_HEADERS) \
+                        gupnp-acl.c                     \
                        gupnp-context.c                 \
                        gupnp-context-manager.c         \
                        gupnp-control-point.c           \
diff --git a/libgupnp/gupnp-acl.c b/libgupnp/gupnp-acl.c
new file mode 100644
index 0000000..57433fc
--- /dev/null
+++ b/libgupnp/gupnp-acl.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 Jens Georg <mail jensge org>
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "gupnp-acl.h"
+
+G_DEFINE_INTERFACE(GUPnPAcl, gupnp_acl, 0)
+
+static void
+gupnp_acl_default_init (GUPnPAclInterface *klass)
+{
+}
+
+/**
+ * gupnp_acl_is_allowed:
+ *
+ * Check whether an IP address is allowed to access this resource.
+ *
+ * @self: an instance of #GUPnPAcl
+ * @address: IP address of the peer.
+ * @returns: %TRUE if the peer is allowed, %FALSE otherwise
+ *
+ * Since: 0.21.0
+ */
+gboolean
+gupnp_acl_is_allowed (GUPnPAcl *self, const char *address)
+{
+        g_return_val_if_fail (GUPNP_IS_ACL (self), FALSE);
+
+        return GUPNP_ACL_GET_INTERFACE (self)->is_allowed (self, address);
+}
+
+/**
+ * gupnp_acl_is_allowed_async:
+ *
+ * Optional. Check asynchronously whether an IP address is allowed to access
+ * this resource. Use this function if the process of verifying the access right
+ * is expected to take some time, for example when using D-Bus etc.
+ *
+ * If this function is supported, gupnp_acl_can_sync() should return %TRUE.
+ *
+ * Use gupnp_acl_is_allowed_finish() to retrieve the result.
+ *
+ * @self: a #GUPnPAcl
+ * @address: IP address of the peer
+ * @cancellable: (allow-none): A #GCancellable which can be used to cancel the
+ * operation.
+ * @callback: Callback to call after the function is done.
+ * @user_data: Some user data.
+ *
+ * Since: 0.21.0
+ */
+void
+gupnp_acl_is_allowed_async (GUPnPAcl            *self,
+                            const char          *address,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback *callback,
+                            gpointer             user_data)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        GUPNP_ACL_GET_INTERFACE (self)->is_allowed_async (self,
+                                                          address,
+                                                          cancellable,
+                                                          callback,
+                                                          user_data);
+}
+
+/**
+ * gupnp_acl_is_allowed_finish:
+ * @self: An instance of #GUPnPAcl
+ * @res: %GAsyncResult obtained from the callback in gupnp_acl_is_allowed_async()
+ * @error: (allow-none): A return location for a #GError describing the failure
+ * @returns: %TRUE if the authentication was successful, %FALSE otherwise and on
+ * error. Check @error for details.
+ *
+ * Since: 0.21.0
+ */
+gboolean
+gupnp_acl_is_allowed_finish (GUPnPAcl      *self,
+                             GAsyncResult  *res,
+                             GError       **error)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        return GUPNP_ACL_GET_INTERFACE (self)->is_allowed_finish (self,
+                                                                  res,
+                                                                  error);
+}
+
+/**
+ * gupnp_acl_can_sync:
+ * Check whether gupnp_acl_is_allowed_async() is supported.
+ *
+ * @self: A #GUPnPAcl
+ * @returns: %TRUE, if gupnp_acl_is_allowed_async() is supported, %FALSE
+ * otherwise.
+ *
+ * Since: 0.21.0
+ */
+gboolean
+gupnp_acl_can_sync (GUPnPAcl *self)
+{
+        g_return_if_fail (GUPNP_IS_ACL (self));
+
+        return GUPNP_ACL_GET_INTERFACE (self)->can_sync (self);
+}
diff --git a/libgupnp/gupnp-acl.h b/libgupnp/gupnp-acl.h
new file mode 100644
index 0000000..42bfc05
--- /dev/null
+++ b/libgupnp/gupnp-acl.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Jens Georg <mail jensge org>
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GUPNP_ACL_H__
+#define __GUPNP_ACL_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libsoup/soup-session.h>
+
+G_BEGIN_DECLS
+
+GType
+gupnp_acl_get_type (void) G_GNUC_CONST;
+
+#define GUPNP_TYPE_ACL (gupnp_acl_get_type())
+
+#define GUPNP_ACL(obj)                  \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+     GUPNP_TYPE_ACL,                    \
+     GUPnPAcl))
+
+#define GUPNP_IS_ACL(obj)               \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj),  \
+     GUPNP_TYPE_ACL))
+
+#define GUPNP_ACL_GET_INTERFACE(obj)      \
+    (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \
+     GUPNP_TYPE_ACL, GUPnPAclInterface))
+
+typedef struct _GUPnPAcl GUPnPAcl;
+typedef struct _GUPnPAclInterface GUPnPAclInterface;
+
+struct _GUPnPAclInterface {
+    GTypeInterface parent;
+
+    gboolean (*is_allowed) (GUPnPAcl   *self,
+                            const char *address);
+
+    void     (*is_allowed_async) (GUPnPAcl            *self,
+                                  const char          *address,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback *callback,
+                                  gpointer             user_data);
+
+    gboolean (*is_allowed_finish) (GUPnPAcl      *self,
+                                   GAsyncResult  *res,
+                                   GError       **error);
+
+    gboolean (*can_sync)          (GUPnPAcl *self);
+
+    /* future padding */
+    void (* _gupnp_reserved1) (void);
+    void (* _gupnp_reserved2) (void);
+    void (* _gupnp_reserved3) (void);
+    void (* _gupnp_reserved4) (void);
+};
+
+gboolean
+gupnp_acl_is_allowed (GUPnPAcl   *self,
+                      const char *address);
+
+void
+gupnp_acl_is_allowed_async (GUPnPAcl            *self,
+                            const char          *address,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback *callback,
+                            gpointer             user_data);
+
+gboolean
+gupnp_acl_is_allowed_finish (GUPnPAcl      *self,
+                             GAsyncResult  *res,
+                             GError       **error);
+
+gboolean
+gupnp_acl_can_sync (GUPnPAcl *self);
+
+G_END_DECLS
+
+#endif
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index 7949880..3046c84 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -49,6 +49,7 @@
 #include <libsoup/soup-address.h>
 #include <glib/gstdio.h>
 
+#include "gupnp-acl.h"
 #include "gupnp-context.h"
 #include "gupnp-context-private.h"
 #include "gupnp-error.h"
@@ -83,6 +84,8 @@ struct _GUPnPContextPrivate {
         char        *default_language;
 
         GList       *host_path_datas;
+
+        GUPnPAcl    *acl;
 };
 
 enum {
@@ -91,7 +94,8 @@ enum {
         PROP_SERVER,
         PROP_SESSION,
         PROP_SUBSCRIPTION_TIMEOUT,
-        PROP_DEFAULT_LANGUAGE
+        PROP_DEFAULT_LANGUAGE,
+        PROP_ACL
 };
 
 typedef struct {
@@ -251,6 +255,10 @@ gupnp_context_set_property (GObject      *object,
                 gupnp_context_set_default_language (context,
                                                     g_value_get_string (value));
                 break;
+        case PROP_ACL:
+                gupnp_context_set_acl (context, g_value_get_object (value));
+
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break;
@@ -290,6 +298,11 @@ gupnp_context_get_property (GObject    *object,
                                     gupnp_context_get_default_language
                                                                    (context));
                 break;
+        case PROP_ACL:
+                g_value_set_object (value,
+                                    gupnp_context_get_acl (context));
+
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break;
@@ -491,6 +504,16 @@ gupnp_context_class_init (GUPnPContextClass *klass)
                                       G_PARAM_STATIC_NAME |
                                       G_PARAM_STATIC_NICK |
                                       G_PARAM_STATIC_BLURB));
+
+        g_object_class_install_property
+                (object_class,
+                 PROP_ACL,
+                 g_param_spec_object ("acl",
+                                      "Access control list",
+                                      "Access control list",
+                                      GUPNP_TYPE_ACL,
+                                      G_PARAM_CONSTRUCT |
+                                      G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -1279,3 +1302,37 @@ gupnp_context_unhost_path (GUPnPContext *context,
         soup_server_remove_handler (server, server_path);
         host_path_data_free (path_data);
 }
+
+/**
+ * gupnp_context_get_acl:
+ * @context: A #GUPnPContext
+ *
+ * Returns:(transfer none): The access control list associated with this context or %NULL
+ * if no acl is set.
+ **/
+GUPnPAcl *
+gupnp_context_get_acl (GUPnPContext *context)
+{
+        g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
+
+        return context->priv->acl;
+}
+
+/**
+ * gupnp_context_set_acl:
+ * @context: A #GUPnPContext
+ * @acl:(allow-none): The new access control list or %NULL to remove the
+ * current list.
+ **/
+void
+gupnp_context_set_acl (GUPnPContext *context, GUPnPAcl *acl)
+{
+        g_return_if_fail (GUPNP_IS_CONTEXT (context));
+
+        if (context->priv->acl != NULL)
+                g_object_unref (context->priv->acl);
+
+        context->priv->acl = g_object_ref (acl);
+
+        g_object_notify (G_OBJECT (context), "acl");
+}
diff --git a/libgupnp/gupnp-context.h b/libgupnp/gupnp-context.h
index a2e2f9e..d0556e9 100644
--- a/libgupnp/gupnp-context.h
+++ b/libgupnp/gupnp-context.h
@@ -26,6 +26,8 @@
 #include <libsoup/soup-server.h>
 #include <libsoup/soup-session.h>
 
+#include "gupnp-acl.h"
+
 G_BEGIN_DECLS
 
 GType
@@ -129,6 +131,12 @@ void
 gupnp_context_unhost_path              (GUPnPContext *context,
                                         const char   *server_path);
 
+GUPnPAcl *
+gupnp_context_get_acl                  (GUPnPContext *context);
+
+void
+gupnp_context_set_acl                  (GUPnPContext *context,
+                                        GUPnPAcl     *acl);
 G_END_DECLS
 
 #endif /* __GUPNP_CONTEXT_H__ */
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 684dbd1..faacc37 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -36,6 +36,7 @@
 #include "gupnp-context-private.h"
 #include "gupnp-marshal.h"
 #include "gupnp-error.h"
+#include "gupnp-acl.h"
 #include "http-headers.h"
 #include "gena-protocol.h"
 #include "xml-util.h"
@@ -914,6 +915,7 @@ control_server_handler (SoupServer                      *server,
         char *action_name;
         char *end;
         GUPnPServiceAction *action;
+        GUPnPAcl *acl;
 
         service = GUPNP_SERVICE (user_data);
 
@@ -939,6 +941,20 @@ control_server_handler (SoupServer                      *server,
 
         context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
 
+        acl = gupnp_context_get_acl (context);
+
+        if (acl) {
+                if (gupnp_acl_can_sync (acl)) {
+                        if (!gupnp_acl_is_allowed (acl, "127.0.0.1")) {
+                                soup_message_set_status (msg, SOUP_STATUS_PROXY_UNAUTHORIZED);
+
+                                return;
+                        } else {
+                                g_warning ("TBD: Handle async ACL");
+                        }
+                }
+        }
+
         /* Get action name */
         soap_action = soup_message_headers_get_one (msg->request_headers,
                                                     "SOAPAction");
@@ -1302,10 +1318,26 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
                              gpointer                         user_data)
 {
         GUPnPService *service;
+        GUPnPContext *context;
+        GUPnPAcl *acl;
         const char *callback, *nt, *sid;
 
         service = GUPNP_SERVICE (user_data);
 
+        context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
+        acl = gupnp_context_get_acl (context);
+        if (acl) {
+                if (gupnp_acl_can_sync (acl)) {
+                        if (!gupnp_acl_is_allowed (acl, "127.0.0.1")) {
+                                soup_message_set_status (msg, SOUP_STATUS_PROXY_UNAUTHORIZED);
+
+                                return;
+                        } else {
+                                g_warning ("TBD: Handle async ACL");
+                        }
+                }
+        }
+
         callback = soup_message_headers_get_one (msg->request_headers,
                                                  "Callback");
         nt       = soup_message_headers_get_one (msg->request_headers, "NT");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]