ef5d8f8115fd4be6142ae41c53f15f01b928d8ad
[profile/ivi/gsignond.git] / src / common / gsignond-access-control-manager.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012-2013 Intel Corporation.
7  *
8  * Contact: Jussi Laako <jussi.laako@linux.intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #include <gio/gio.h>
32
33 #include "config.h"
34
35 #include "gsignond/gsignond-log.h"
36 #include "gsignond/gsignond-access-control-manager.h"
37
38 /**
39  * SECTION:gsignond-access-control-manager
40  * @short_description: an object that performs access control checks
41  * @include: gsignond/gsignond-access-control-manager.h
42  *
43  * #GSignondAccessControlManager performs access control checks using
44  * available system services. gSSO can be configured to use a custom extension
45  * that provides a subclassed implementation of #GSignondAccessControlManager
46  * (see #GSignondExtension), otherwise a default implementation is used.
47  */
48 /**
49  * GSignondAccessControlManager:
50  *
51  * Opaque #GSignondAccessControlManager data structure.
52  */
53
54 #define GSIGNOND_ACCESS_CONTROL_MANAGER_GET_PRIVATE(obj) \
55     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
56                                   GSIGNOND_TYPE_ACCESS_CONTROL_MANAGER, \
57                                   GSignondAccessControlManagerPrivate))
58
59 #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
60 #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
61 #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
62 #ifndef GSIGNOND_BUS_TYPE
63 #   define GSIGNOND_BUS_TYPE G_BUS_TYPE_SESSION
64 #endif
65
66 struct _GSignondAccessControlManagerPrivate
67 {
68 };
69
70 enum
71 {
72     PROP_0,
73     PROP_CONFIG,
74     N_PROPERTIES
75 };
76
77 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
78
79 G_DEFINE_TYPE (GSignondAccessControlManager, gsignond_access_control_manager,
80                G_TYPE_OBJECT);
81
82 static void
83 _set_property (GObject *object, guint prop_id, const GValue *value,
84                GParamSpec *pspec)
85 {
86     GSignondAccessControlManager *self =
87         GSIGNOND_ACCESS_CONTROL_MANAGER (object);
88
89     switch (prop_id) {
90         case PROP_CONFIG:
91             g_assert (self->config == NULL);
92             self->config = g_value_dup_object (value);
93             break;
94         default:
95             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96     }
97 }
98
99 static void
100 _get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
101 {
102     GSignondAccessControlManager *self =
103         GSIGNOND_ACCESS_CONTROL_MANAGER (object);
104
105     switch (prop_id) {
106         case PROP_CONFIG:
107             g_value_set_object (value, self->config);
108             break;
109         default:
110             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111     }
112 }
113
114 static void
115 _dispose (GObject *object)
116 {
117     GSignondAccessControlManager *self =
118         GSIGNOND_ACCESS_CONTROL_MANAGER (object);
119
120     if (self->config) {
121         g_object_unref (self->config);
122         self->config = NULL;
123     }
124
125     G_OBJECT_CLASS (gsignond_access_control_manager_parent_class)->dispose (object);
126 }
127
128 static void
129 _security_context_of_peer (GSignondAccessControlManager *self,
130                            GSignondSecurityContext *peer_ctx,
131                            int peer_fd, const gchar *peer_service,
132                            const gchar *peer_app_ctx)
133 {
134     pid_t remote_pid = 0;
135     gchar *procfname;
136     char *peerpath;
137     ssize_t res;
138
139     (void) self;
140
141     gsignond_security_context_set_system_context (peer_ctx, "");
142     gsignond_security_context_set_application_context (peer_ctx,
143                                                        peer_app_ctx);
144
145     if (peer_fd >= 0) {
146         struct ucred peer_cred;
147         socklen_t cred_size = sizeof(peer_cred);
148
149         if (getsockopt (peer_fd, SOL_SOCKET, SO_PEERCRED,
150                         &peer_cred, &cred_size) != 0) {
151             WARN ("getsockopt() for SO_PEERCRED failed");
152             return;
153         }
154         DBG ("remote peer pid=%d uid=%d gid=%d",
155              peer_cred.pid, peer_cred.uid, peer_cred.gid);
156         remote_pid = peer_cred.pid;
157     } else if (peer_service) {
158         GError *error = NULL;
159         GDBusConnection *connection;
160         GVariant *response = NULL;
161         guint32 upid;
162
163         connection = g_bus_get_sync (GSIGNOND_BUS_TYPE, NULL, &error);
164         if (!connection) {
165             WARN ("failed to open connection to session bus: %s",
166                   error->message);
167             g_error_free (error);
168             return;
169         }
170
171         error = NULL;
172         response = g_dbus_connection_call_sync (connection,
173                                                 DBUS_SERVICE_DBUS,
174                                                 DBUS_PATH_DBUS,
175                                                 DBUS_INTERFACE_DBUS,
176                                                 "GetConnectionUnixProcessID",
177                                                 g_variant_new ("(s)", peer_service),
178                                                 ((const GVariantType *) "(u)"),
179                                                 G_DBUS_CALL_FLAGS_NONE,
180                                                 -1,
181                                                 NULL,
182                                                 &error);
183
184         g_object_unref (connection);
185
186         if (!response) {
187             WARN ("request for peer pid failed: %s",
188                   error->message);
189             g_error_free (error);
190             return;
191         }
192         
193         g_variant_get (response, "(u)", &upid);
194         DBG ("remote peer service=%s pid=%u", peer_service, upid);
195         remote_pid = (pid_t) upid;
196
197         g_variant_unref (response);
198     } else return;
199
200     if (!remote_pid)
201         return;
202
203     procfname = g_strdup_printf ("/proc/%d/exe", remote_pid);
204     peerpath = g_malloc0 (PATH_MAX + 1);
205     res = readlink (procfname, peerpath, PATH_MAX);
206     g_free (procfname);
207     if (res <= 0) {
208         WARN ("failed to follow link for pid %d", remote_pid);
209         g_free (peerpath);
210         return;
211     }
212
213     DBG ("identity of pid %d is [%s:%s]", remote_pid, peerpath, peer_app_ctx);
214     gsignond_security_context_set_system_context (peer_ctx, peerpath);
215
216     g_free (peerpath);
217 }
218
219 static gboolean
220 _peer_is_allowed_to_use_identity (GSignondAccessControlManager *self,
221                                 const GSignondSecurityContext *peer_ctx,
222                                 const GSignondSecurityContext *owner_ctx,
223                                 const GSignondSecurityContextList *identity_acl)
224 {
225     GSignondSecurityContext *acl_ctx;
226
227     (void) self;
228     (void) owner_ctx;
229
230     for ( ; identity_acl != NULL; identity_acl = g_list_next (identity_acl)) {
231         acl_ctx = (GSignondSecurityContext *) identity_acl->data;
232         DBG ("ACL check [%p=(%s:%s)] vs [%p=(%s:%s)]",
233              acl_ctx,
234              gsignond_security_context_get_system_context (acl_ctx),
235              gsignond_security_context_get_application_context (acl_ctx),
236              peer_ctx,
237              gsignond_security_context_get_system_context (peer_ctx),
238              gsignond_security_context_get_application_context (peer_ctx));
239         if (gsignond_security_context_check (acl_ctx, peer_ctx)) {
240             DBG (" - ACL check passed");
241             return TRUE;
242         }
243     }
244     DBG (" - ACL check failed");
245     return FALSE;
246 }
247
248 static gboolean
249 _peer_is_owner_of_identity (GSignondAccessControlManager *self,
250                             const GSignondSecurityContext *peer_ctx,
251                             const GSignondSecurityContext *owner_ctx)
252 {
253     (void) self;
254
255     DBG ("Owner check [%p=(%s:%s)] vs [%p=(%s:%s)]",
256          owner_ctx,
257          gsignond_security_context_get_system_context (owner_ctx),
258          gsignond_security_context_get_application_context (owner_ctx),
259          peer_ctx,
260          gsignond_security_context_get_system_context (peer_ctx),
261          gsignond_security_context_get_application_context (peer_ctx));
262     return gsignond_security_context_check (owner_ctx, peer_ctx);
263 }
264
265 static gboolean
266 _acl_is_valid (GSignondAccessControlManager *self,
267                const GSignondSecurityContext *peer_ctx,
268                const GSignondSecurityContextList *identity_acl)
269 {
270     (void) self;
271     (void) peer_ctx;
272     (void) identity_acl;
273
274     return TRUE;
275 }
276
277 GSignondSecurityContext *
278 _security_context_of_keychain (GSignondAccessControlManager *self)
279 {
280     const gchar *keychain_sysctx = NULL;
281
282     (void) self;
283
284 #   if defined(ENABLE_DEBUG)
285     keychain_sysctx = g_getenv ("SSO_KEYCHAIN_SYSCTX");
286 #   elif defined(KEYCHAIN_SYSCTX)
287     keychain_sysctx = KEYCHAIN_SYSCTX;
288 #   endif
289     if (!keychain_sysctx)
290         keychain_sysctx = "";
291     return gsignond_security_context_new_from_values (keychain_sysctx, "");
292 }
293
294 /**
295  * GSignondAccessControlManagerClass:
296  * @parent_class: parent class.
297  * @security_context_of_peer: an implementation of gsignond_access_control_manager_security_context_of_peer()
298  * @peer_is_allowed_to_use_identity: an implementation of gsignond_access_control_manager_peer_is_allowed_to_use_identity()
299  * @peer_is_owner_of_identity: an implementation of gsignond_access_control_manager_peer_is_owner_of_identity()
300  * @acl_is_valid: an implementation of gsignond_access_control_manager_acl_is_valid()
301  * @security_context_of_keychain: an implementation of gsignond_access_control_manager_security_context_of_keychain()
302  * 
303  * #GSignondAccessControlManagerClass class containing pointers to class methods.
304  */
305 static void
306 gsignond_access_control_manager_class_init (
307                                        GSignondAccessControlManagerClass *klass)
308 {
309     GObjectClass *base = G_OBJECT_CLASS (klass);
310
311     base->set_property = _set_property;
312     base->get_property = _get_property;
313     base->dispose = _dispose;
314     properties[PROP_CONFIG] = g_param_spec_object ("config",
315                                                    "config",
316                                                    "Configuration object",
317                                                    GSIGNOND_TYPE_CONFIG,
318                                                    G_PARAM_CONSTRUCT_ONLY|
319                                                    G_PARAM_READWRITE |
320                                                    G_PARAM_STATIC_STRINGS);
321     g_object_class_install_properties (base, N_PROPERTIES, properties);
322
323     /*g_type_class_add_private (klass,
324                               sizeof(GSignondAccessControlManagerPrivate));*/
325
326     klass->security_context_of_peer = _security_context_of_peer;
327     klass->peer_is_allowed_to_use_identity = _peer_is_allowed_to_use_identity;
328     klass->peer_is_owner_of_identity = _peer_is_owner_of_identity;
329     klass->acl_is_valid = _acl_is_valid;
330     klass->security_context_of_keychain = _security_context_of_keychain;
331 }
332
333 static void
334 gsignond_access_control_manager_init (GSignondAccessControlManager *self)
335 {
336     /*self->priv = GSIGNOND_ACCESS_CONTROL_MANAGER_GET_PRIVATE (self);*/
337
338     self->config = NULL;
339 }
340
341 /**
342  * gsignond_access_control_manager_security_context_of_peer:
343  * @self: object instance.
344  * @peer_ctx: instance of security context to be set.
345  * @peer_fd: file descriptor of the peer connection if using peer-to-peer dbus, -1 otherwise.
346  * @peer_service: g_dbus_method_invocation_get_sender() of the peer connection, if not using peer-to-peer dbus, NULL otherwise
347  * @peer_app_ctx: application context of the peer connection.
348  *
349  * Retrieves and sets #GSignondSecurityContext of the specified peer.
350  * 
351  * The default implementation sets the app context as it was passed, and sets 
352  * the system context to the binary path of the process that is determined from
353  * @peer_fd and @peer_service parameters.
354  */
355 void
356 gsignond_access_control_manager_security_context_of_peer (
357                             GSignondAccessControlManager *self,
358                             GSignondSecurityContext *peer_ctx,
359                             int peer_fd, const gchar *peer_service,
360                             const gchar *peer_app_ctx)
361 {
362     GSIGNOND_ACCESS_CONTROL_MANAGER_GET_CLASS (self)->
363         security_context_of_peer (self, peer_ctx, peer_fd,
364                                   peer_service, peer_app_ctx);
365 }
366
367 /**
368  * gsignond_access_control_manager_peer_is_allowed_to_use_identity:
369  * @self: object instance.
370  * @peer_ctx: security context of the peer connection.
371  * @owner_ctx: security context of the identity owner.
372  * @identity_acl: access control list for the identity in question. Includes the @owner_ctx as well.
373  *
374  * Checks if specified peer is allowed to access the specified identity.
375  * 
376  * The default implementation goes over items in @identity_acl, using 
377  * gsignond_security_context_check() to check them against @peer_ctx.
378  *
379  * Returns: access is allowed?
380  */
381 gboolean
382 gsignond_access_control_manager_peer_is_allowed_to_use_identity (
383                             GSignondAccessControlManager *self,
384                             const GSignondSecurityContext *peer_ctx,
385                             const GSignondSecurityContext *owner_ctx,
386                             const GSignondSecurityContextList *identity_acl)
387 {
388     return GSIGNOND_ACCESS_CONTROL_MANAGER_GET_CLASS (self)->
389         peer_is_allowed_to_use_identity (self, peer_ctx, owner_ctx, identity_acl);
390 }
391
392 /**
393  * gsignond_access_control_manager_peer_is_owner_of_identity:
394  * @self: object instance.
395  * @peer_ctx: security context of the peer connection.
396  * @owner_ctx: security context of the identity owner.
397  *
398  * Checks if the peer specified in @peer_ctx is the owner of the identity.
399  * 
400  * The default implementation is using gsignond_security_context_check() 
401  * to check @peer_ctx against @owner_ctx directly.
402  *
403  * Returns: is owner?
404  */
405 gboolean
406 gsignond_access_control_manager_peer_is_owner_of_identity (
407                             GSignondAccessControlManager *self,
408                             const GSignondSecurityContext *peer_ctx,
409                             const GSignondSecurityContext *owner_ctx)
410 {
411     return GSIGNOND_ACCESS_CONTROL_MANAGER_GET_CLASS (self)->
412         peer_is_owner_of_identity (self, peer_ctx, owner_ctx);
413 }
414
415 /**
416  * gsignond_access_control_manager_acl_is_valid:
417  * @self: object instance.
418  * @peer_ctx: security context of the peer connection.
419  * @identity_acl: access control list for the identity.
420  *
421  * Checks if the specified peer is allowed to set the specified access
422  * control list. gsignond_access_control_manager_peer_is_owner_of_identity()
423  * is used before calling this method to verify identity ownership.
424  * 
425  * The default implementation always returns TRUE.
426  *
427  * Returns: access control list is OK?
428  */
429 gboolean
430 gsignond_access_control_manager_acl_is_valid (
431                             GSignondAccessControlManager *self,
432                             const GSignondSecurityContext *peer_ctx,
433                             const GSignondSecurityContextList *identity_acl)
434 {
435     return GSIGNOND_ACCESS_CONTROL_MANAGER_GET_CLASS (self)->
436         acl_is_valid (self, peer_ctx, identity_acl);
437 }
438
439 /**
440  * gsignond_access_control_manager_security_context_of_keychain:
441  * @self: object instance.
442  *
443  * Retrieves security context of the keychain application. Keychain application
444  * has a special management access to all stored identities and is able to
445  * perform deletion of all identities from storage.
446  * 
447  * The default implementation returns an empty context. If gSSO was compiled
448  * with --enable-debug and SSO_KEYCHAIN_SYSCTX environment variable is set, then
449  * the value of that variable is used to set the returned system context instead.
450  *
451  * Returns: security context of the keychain application.
452  */
453 GSignondSecurityContext *
454 gsignond_access_control_manager_security_context_of_keychain (
455                             GSignondAccessControlManager *self)
456 {
457     return GSIGNOND_ACCESS_CONTROL_MANAGER_GET_CLASS (self)->
458         security_context_of_keychain (self);
459 }
460