1 /*
2  * Copyright (C) 2008 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: David Zeuthen <davidz@redhat.com>
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25 
26 #include <string.h>
27 
28 #include "polkitidentity.h"
29 #include "polkitunixuser.h"
30 #include "polkitunixgroup.h"
31 #include "polkitunixnetgroup.h"
32 #include "polkiterror.h"
33 #include "polkitprivate.h"
34 
35 /**
36  * SECTION:polkitidentity
37  * @title: PolkitIdentity
38  * @short_description: Type for representing identities
39  *
40  * #PolkitIdentity is an abstract type for representing one or more
41  * identities.
42  */
43 
44 static void
base_init(gpointer g_iface)45 base_init (gpointer g_iface)
46 {
47 }
48 
49 GType
polkit_identity_get_type(void)50 polkit_identity_get_type (void)
51 {
52   static volatile gsize g_define_type_id__volatile = 0;
53 
54   if (g_once_init_enter (&g_define_type_id__volatile))
55     {
56       static const GTypeInfo info =
57       {
58         sizeof (PolkitIdentityIface),
59         base_init,              /* base_init      */
60         NULL,                   /* base_finalize  */
61         NULL,                   /* class_init     */
62         NULL,                   /* class_finalize */
63         NULL,                   /* class_data     */
64         0,                      /* instance_size  */
65         0,                      /* n_preallocs    */
66         NULL,                   /* instance_init  */
67         NULL                    /* value_table    */
68       };
69 
70       GType iface_type =
71         g_type_register_static (G_TYPE_INTERFACE, "PolkitIdentity", &info, 0);
72 
73       g_type_interface_add_prerequisite (iface_type, G_TYPE_OBJECT);
74       g_once_init_leave (&g_define_type_id__volatile, iface_type);
75     }
76 
77   return g_define_type_id__volatile;
78 }
79 
80 /**
81  * polkit_identity_hash:
82  * @identity: A #PolkitIdentity.
83  *
84  * Gets a hash code for @identity that can be used with e.g. g_hash_table_new().
85  *
86  * Returns: A hash code.
87  */
88 guint
polkit_identity_hash(PolkitIdentity * identity)89 polkit_identity_hash (PolkitIdentity *identity)
90 {
91   g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), 0);
92   return POLKIT_IDENTITY_GET_IFACE (identity)->hash (identity);
93 }
94 
95 /**
96  * polkit_identity_equal:
97  * @a: A #PolkitIdentity.
98  * @b: A #PolkitIdentity.
99  *
100  * Checks if @a and @b are equal, ie. represent the same identity.
101  *
102  * This function can be used in e.g. g_hash_table_new().
103  *
104  * Returns: %TRUE if @a and @b are equal, %FALSE otherwise.
105  */
106 gboolean
polkit_identity_equal(PolkitIdentity * a,PolkitIdentity * b)107 polkit_identity_equal (PolkitIdentity *a,
108                       PolkitIdentity *b)
109 {
110   g_return_val_if_fail (POLKIT_IS_IDENTITY (a), FALSE);
111   g_return_val_if_fail (POLKIT_IS_IDENTITY (b), FALSE);
112 
113   if (!g_type_is_a (G_TYPE_FROM_INSTANCE (a), G_TYPE_FROM_INSTANCE (b)))
114     return FALSE;
115 
116   return POLKIT_IDENTITY_GET_IFACE (a)->equal (a, b);
117 }
118 
119 /**
120  * polkit_identity_to_string:
121  * @identity: A #PolkitIdentity.
122  *
123  * Serializes @identity to a string that can be used in
124  * polkit_identity_from_string().
125  *
126  * Returns: A string representing @identity. Free with g_free().
127  */
128 gchar *
polkit_identity_to_string(PolkitIdentity * identity)129 polkit_identity_to_string (PolkitIdentity *identity)
130 {
131   g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), NULL);
132   return POLKIT_IDENTITY_GET_IFACE (identity)->to_string (identity);
133 }
134 
135 /**
136  * polkit_identity_from_string:
137  * @str: A string obtained from polkit_identity_to_string().
138  * @error: Return location for error.
139  *
140  * Creates an object from @str that implements the #PolkitIdentity
141  * interface.
142  *
143  * Returns: (allow-none) (transfer full): A #PolkitIdentity or %NULL
144  * if @error is set. Free with g_object_unref().
145  */
146 PolkitIdentity *
polkit_identity_from_string(const gchar * str,GError ** error)147 polkit_identity_from_string  (const gchar   *str,
148                              GError       **error)
149 {
150   PolkitIdentity *identity;
151   guint64 val;
152   gchar *endptr;
153 
154   g_return_val_if_fail (str != NULL, NULL);
155   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
156 
157   /* TODO: we could do something with VFuncs like in g_icon_from_string() */
158 
159   identity = NULL;
160 
161   if (g_str_has_prefix (str, "unix-user:"))
162     {
163       val = g_ascii_strtoull (str + sizeof "unix-user:" - 1,
164                               &endptr,
165                               10);
166       if (*endptr == '\0')
167         identity = polkit_unix_user_new ((gint) val);
168       else
169         identity = polkit_unix_user_new_for_name (str + sizeof "unix-user:" - 1,
170                                                  error);
171     }
172   else if (g_str_has_prefix (str, "unix-group:"))
173     {
174       val = g_ascii_strtoull (str + sizeof "unix-group:" - 1,
175                               &endptr,
176                               10);
177       if (*endptr == '\0')
178         identity = polkit_unix_group_new ((gint) val);
179       else
180         identity = polkit_unix_group_new_for_name (str + sizeof "unix-group:" - 1,
181                                                   error);
182     }
183   else if (g_str_has_prefix (str, "unix-netgroup:"))
184     {
185       identity = polkit_unix_netgroup_new (str + sizeof "unix-netgroup:" - 1);
186     }
187 
188   if (identity == NULL && (error != NULL && *error == NULL))
189     {
190       g_set_error (error,
191                    POLKIT_ERROR,
192                    POLKIT_ERROR_FAILED,
193                    "Malformed identity string '%s'",
194                    str);
195     }
196 
197 
198   return identity;
199 }
200 
201 /* Note that this returns a floating value. */
202 GVariant *
polkit_identity_to_gvariant(PolkitIdentity * identity)203 polkit_identity_to_gvariant (PolkitIdentity *identity)
204 {
205   GVariantBuilder builder;
206   GVariant *dict;
207   const gchar *kind;
208 
209   kind = "";
210 
211   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
212   if (POLKIT_IS_UNIX_USER (identity))
213     {
214       kind = "unix-user";
215       g_variant_builder_add (&builder, "{sv}", "uid",
216                              g_variant_new_uint32 (polkit_unix_user_get_uid (POLKIT_UNIX_USER (identity))));
217     }
218   else if (POLKIT_IS_UNIX_GROUP (identity))
219     {
220       kind = "unix-group";
221       g_variant_builder_add (&builder, "{sv}", "gid",
222                              g_variant_new_uint32 (polkit_unix_group_get_gid (POLKIT_UNIX_GROUP (identity))));
223     }
224   else if (POLKIT_IS_UNIX_NETGROUP (identity))
225     {
226       kind = "unix-netgroup";
227       g_variant_builder_add (&builder, "{sv}", "name",
228                              g_variant_new_string (polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (identity))));
229     }
230   else
231     {
232       g_warning ("Unknown class %s implementing PolkitIdentity", g_type_name (G_TYPE_FROM_INSTANCE (identity)));
233     }
234 
235   dict = g_variant_builder_end (&builder);
236   return g_variant_new ("(s@a{sv})", kind, dict);
237 }
238 
239 static GVariant *
lookup_asv(GVariant * dict,const gchar * given_key,const GVariantType * given_type,GError ** error)240 lookup_asv (GVariant            *dict,
241             const gchar         *given_key,
242             const GVariantType  *given_type,
243             GError             **error)
244 {
245   GVariantIter iter;
246   const gchar *key;
247   GVariant *value;
248   GVariant *ret;
249 
250   ret = NULL;
251 
252   g_variant_iter_init (&iter, dict);
253   while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
254     {
255       if (g_strcmp0 (key, given_key) == 0)
256         {
257           if (!g_variant_is_of_type (value, given_type))
258             {
259               gchar *type_string;
260               type_string = g_variant_type_dup_string (given_type);
261               g_set_error (error,
262                            POLKIT_ERROR,
263                            POLKIT_ERROR_FAILED,
264                            "Value for key `%s' found but is of type %s and type %s was expected",
265                            given_key,
266                            g_variant_get_type_string (value),
267                            type_string);
268               g_free (type_string);
269               g_variant_unref (value);
270               goto out;
271             }
272           ret = value;
273           goto out;
274         }
275       g_variant_unref (value);
276     }
277 
278  out:
279   if (ret == NULL)
280     {
281       gchar *type_string;
282       type_string = g_variant_type_dup_string (given_type);
283       g_set_error (error,
284                    POLKIT_ERROR,
285                    POLKIT_ERROR_FAILED,
286                    "Didn't find value for key `%s' of type %s",
287                    given_key,
288                    type_string);
289       g_free (type_string);
290     }
291 
292   return ret;
293 }
294 
295 PolkitIdentity *
polkit_identity_new_for_gvariant(GVariant * variant,GError ** error)296 polkit_identity_new_for_gvariant (GVariant  *variant,
297                                   GError    **error)
298 {
299   PolkitIdentity *ret;
300   const gchar *kind;
301   GVariant *details_gvariant;
302 
303   ret = NULL;
304 
305   g_variant_get (variant,
306                  "(&s@a{sv})",
307                  &kind,
308                  &details_gvariant);
309 
310   if (g_strcmp0 (kind, "unix-user") == 0)
311     {
312       GVariant *v;
313       guint32 uid;
314 
315       v = lookup_asv (details_gvariant, "uid", G_VARIANT_TYPE_UINT32, error);
316       if (v == NULL)
317         {
318           g_prefix_error (error, "Error parsing unix-user identity: ");
319           goto out;
320         }
321       uid = g_variant_get_uint32 (v);
322       g_variant_unref (v);
323 
324       ret = polkit_unix_user_new (uid);
325     }
326   else if (g_strcmp0 (kind, "unix-group") == 0)
327     {
328       GVariant *v;
329       guint32 gid;
330 
331       v = lookup_asv (details_gvariant, "gid", G_VARIANT_TYPE_UINT32, error);
332       if (v == NULL)
333         {
334           g_prefix_error (error, "Error parsing unix-user identity: ");
335           goto out;
336         }
337       gid = g_variant_get_uint32 (v);
338       g_variant_unref (v);
339 
340       ret = polkit_unix_group_new (gid);
341     }
342   else if (g_strcmp0 (kind, "unix-netgroup") == 0)
343     {
344       GVariant *v;
345       const char *name;
346 
347       v = lookup_asv (details_gvariant, "name", G_VARIANT_TYPE_STRING, error);
348       if (v == NULL)
349         {
350           g_prefix_error (error, "Error parsing net identity: ");
351           goto out;
352         }
353       name = g_variant_get_string (v, NULL);
354       ret = polkit_unix_netgroup_new (name);
355       g_variant_unref (v);
356     }
357   else
358     {
359       g_set_error (error,
360                    POLKIT_ERROR,
361                    POLKIT_ERROR_FAILED,
362                    "Unknown identity of kind `%s'",
363                    kind);
364     }
365 
366  out:
367   g_variant_unref (details_gvariant);
368   return ret;
369 }
370