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