1 /* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: David Zeuthen <davidz@redhat.com>
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <gobject/gvaluecollector.h>
27
28 #include "gcredentials.h"
29 #include "gcredentialsprivate.h"
30 #include "gnetworking.h"
31 #include "gioerror.h"
32 #include "gioenumtypes.h"
33
34 #include "glibintl.h"
35
36 /**
37 * SECTION:gcredentials
38 * @short_description: An object containing credentials
39 * @include: gio/gio.h
40 *
41 * The #GCredentials type is a reference-counted wrapper for native
42 * credentials. This information is typically used for identifying,
43 * authenticating and authorizing other processes.
44 *
45 * Some operating systems supports looking up the credentials of the
46 * remote peer of a communication endpoint - see e.g.
47 * g_socket_get_credentials().
48 *
49 * Some operating systems supports securely sending and receiving
50 * credentials over a Unix Domain Socket, see
51 * #GUnixCredentialsMessage, g_unix_connection_send_credentials() and
52 * g_unix_connection_receive_credentials() for details.
53 *
54 * On Linux, the native credential type is a struct ucred - see the
55 * unix(7) man page for details. This corresponds to
56 * %G_CREDENTIALS_TYPE_LINUX_UCRED.
57 *
58 * On FreeBSD, Debian GNU/kFreeBSD, and GNU/Hurd, the native
59 * credential type is a struct cmsgcred. This corresponds
60 * to %G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED.
61 *
62 * On NetBSD, the native credential type is a struct unpcbid.
63 * This corresponds to %G_CREDENTIALS_TYPE_NETBSD_UNPCBID.
64 *
65 * On OpenBSD, the native credential type is a struct sockpeercred.
66 * This corresponds to %G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED.
67 *
68 * On Solaris (including OpenSolaris and its derivatives), the native
69 * credential type is a ucred_t. This corresponds to
70 * %G_CREDENTIALS_TYPE_SOLARIS_UCRED.
71 */
72
73 /**
74 * GCredentials:
75 *
76 * The #GCredentials structure contains only private data and
77 * should only be accessed using the provided API.
78 *
79 * Since: 2.26
80 */
81 struct _GCredentials
82 {
83 /*< private >*/
84 GObject parent_instance;
85
86 #if G_CREDENTIALS_USE_LINUX_UCRED
87 struct ucred native;
88 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
89 struct cmsgcred native;
90 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
91 struct unpcbid native;
92 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
93 struct sockpeercred native;
94 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
95 ucred_t *native;
96 #else
97 #ifdef __GNUC__
98 #pragma GCC diagnostic push
99 #pragma GCC diagnostic warning "-Wcpp"
100 #warning Please add GCredentials support for your OS
101 #pragma GCC diagnostic pop
102 #endif
103 #endif
104 };
105
106 /**
107 * GCredentialsClass:
108 *
109 * Class structure for #GCredentials.
110 *
111 * Since: 2.26
112 */
113 struct _GCredentialsClass
114 {
115 /*< private >*/
116 GObjectClass parent_class;
117 };
118
G_DEFINE_TYPE(GCredentials,g_credentials,G_TYPE_OBJECT)119 G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT)
120
121 static void
122 g_credentials_finalize (GObject *object)
123 {
124 #if G_CREDENTIALS_USE_SOLARIS_UCRED
125 GCredentials *credentials = G_CREDENTIALS (object);
126
127 ucred_free (credentials->native);
128 #endif
129
130 if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL)
131 G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object);
132 }
133
134
135 static void
g_credentials_class_init(GCredentialsClass * klass)136 g_credentials_class_init (GCredentialsClass *klass)
137 {
138 GObjectClass *gobject_class;
139
140 gobject_class = G_OBJECT_CLASS (klass);
141 gobject_class->finalize = g_credentials_finalize;
142 }
143
144 static void
g_credentials_init(GCredentials * credentials)145 g_credentials_init (GCredentials *credentials)
146 {
147 #if G_CREDENTIALS_USE_LINUX_UCRED
148 credentials->native.pid = getpid ();
149 credentials->native.uid = geteuid ();
150 credentials->native.gid = getegid ();
151 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
152 memset (&credentials->native, 0, sizeof (struct cmsgcred));
153 credentials->native.cmcred_pid = getpid ();
154 credentials->native.cmcred_euid = geteuid ();
155 credentials->native.cmcred_gid = getegid ();
156 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
157 credentials->native.unp_pid = getpid ();
158 credentials->native.unp_euid = geteuid ();
159 credentials->native.unp_egid = getegid ();
160 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
161 credentials->native.pid = getpid ();
162 credentials->native.uid = geteuid ();
163 credentials->native.gid = getegid ();
164 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
165 credentials->native = ucred_get (P_MYID);
166 #endif
167 }
168
169 /* ---------------------------------------------------------------------------------------------------- */
170
171 /**
172 * g_credentials_new:
173 *
174 * Creates a new #GCredentials object with credentials matching the
175 * the current process.
176 *
177 * Returns: A #GCredentials. Free with g_object_unref().
178 *
179 * Since: 2.26
180 */
181 GCredentials *
g_credentials_new(void)182 g_credentials_new (void)
183 {
184 return g_object_new (G_TYPE_CREDENTIALS, NULL);
185 }
186
187 /* ---------------------------------------------------------------------------------------------------- */
188
189 /**
190 * g_credentials_to_string:
191 * @credentials: A #GCredentials object.
192 *
193 * Creates a human-readable textual representation of @credentials
194 * that can be used in logging and debug messages. The format of the
195 * returned string may change in future GLib release.
196 *
197 * Returns: A string that should be freed with g_free().
198 *
199 * Since: 2.26
200 */
201 gchar *
g_credentials_to_string(GCredentials * credentials)202 g_credentials_to_string (GCredentials *credentials)
203 {
204 GString *ret;
205
206 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
207
208 ret = g_string_new ("GCredentials:");
209 #if G_CREDENTIALS_USE_LINUX_UCRED
210 g_string_append (ret, "linux-ucred:");
211 if (credentials->native.pid != -1)
212 g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
213 if (credentials->native.uid != -1)
214 g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
215 if (credentials->native.gid != -1)
216 g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
217 if (ret->str[ret->len - 1] == ',')
218 ret->str[ret->len - 1] = '\0';
219 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
220 g_string_append (ret, "freebsd-cmsgcred:");
221 if (credentials->native.cmcred_pid != -1)
222 g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_pid);
223 if (credentials->native.cmcred_euid != -1)
224 g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_euid);
225 if (credentials->native.cmcred_gid != -1)
226 g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_gid);
227 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
228 g_string_append (ret, "netbsd-unpcbid:");
229 if (credentials->native.unp_pid != -1)
230 g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_pid);
231 if (credentials->native.unp_euid != -1)
232 g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_euid);
233 if (credentials->native.unp_egid != -1)
234 g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_egid);
235 ret->str[ret->len - 1] = '\0';
236 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
237 g_string_append (ret, "openbsd-sockpeercred:");
238 if (credentials->native.pid != -1)
239 g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
240 if (credentials->native.uid != -1)
241 g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
242 if (credentials->native.gid != -1)
243 g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
244 if (ret->str[ret->len - 1] == ',')
245 ret->str[ret->len - 1] = '\0';
246 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
247 g_string_append (ret, "solaris-ucred:");
248 {
249 id_t id;
250 if ((id = ucred_getpid (credentials->native)) != -1)
251 g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) id);
252 if ((id = ucred_geteuid (credentials->native)) != -1)
253 g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) id);
254 if ((id = ucred_getegid (credentials->native)) != -1)
255 g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) id);
256 if (ret->str[ret->len - 1] == ',')
257 ret->str[ret->len - 1] = '\0';
258 }
259 #else
260 g_string_append (ret, "unknown");
261 #endif
262
263 return g_string_free (ret, FALSE);
264 }
265
266 /* ---------------------------------------------------------------------------------------------------- */
267
268 #if G_CREDENTIALS_USE_LINUX_UCRED
269 /*
270 * Check whether @native contains invalid data. If getsockopt SO_PEERCRED
271 * is used on a TCP socket, it succeeds but yields a credentials structure
272 * with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
273 * receiving Unix socket when the sending socket did not also enable
274 * SO_PASSCRED, it can succeed but yield a credentials structure with
275 * pid 0, uid /proc/sys/kernel/overflowuid and gid
276 * /proc/sys/kernel/overflowgid.
277 */
278 static gboolean
linux_ucred_check_valid(struct ucred * native,GError ** error)279 linux_ucred_check_valid (struct ucred *native,
280 GError **error)
281 {
282 if (native->pid == 0
283 || native->uid == -1
284 || native->gid == -1)
285 {
286 g_set_error_literal (error,
287 G_IO_ERROR,
288 G_IO_ERROR_INVALID_DATA,
289 "GCredentials contains invalid data");
290 return FALSE;
291 }
292
293 return TRUE;
294 }
295 #endif
296
297 /**
298 * g_credentials_is_same_user:
299 * @credentials: A #GCredentials.
300 * @other_credentials: A #GCredentials.
301 * @error: Return location for error or %NULL.
302 *
303 * Checks if @credentials and @other_credentials is the same user.
304 *
305 * This operation can fail if #GCredentials is not supported on the
306 * the OS.
307 *
308 * Returns: %TRUE if @credentials and @other_credentials has the same
309 * user, %FALSE otherwise or if @error is set.
310 *
311 * Since: 2.26
312 */
313 gboolean
g_credentials_is_same_user(GCredentials * credentials,GCredentials * other_credentials,GError ** error)314 g_credentials_is_same_user (GCredentials *credentials,
315 GCredentials *other_credentials,
316 GError **error)
317 {
318 gboolean ret;
319
320 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
321 g_return_val_if_fail (G_IS_CREDENTIALS (other_credentials), FALSE);
322 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
323
324 ret = FALSE;
325 #if G_CREDENTIALS_USE_LINUX_UCRED
326 if (linux_ucred_check_valid (&credentials->native, NULL)
327 && credentials->native.uid == other_credentials->native.uid)
328 ret = TRUE;
329 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
330 if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
331 ret = TRUE;
332 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
333 if (credentials->native.unp_euid == other_credentials->native.unp_euid)
334 ret = TRUE;
335 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
336 if (credentials->native.uid == other_credentials->native.uid)
337 ret = TRUE;
338 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
339 if (ucred_geteuid (credentials->native) == ucred_geteuid (other_credentials->native))
340 ret = TRUE;
341 #else
342 g_set_error_literal (error,
343 G_IO_ERROR,
344 G_IO_ERROR_NOT_SUPPORTED,
345 _("GCredentials is not implemented on this OS"));
346 #endif
347
348 return ret;
349 }
350
351 static gboolean
credentials_native_type_check(GCredentialsType requested_type,const char * op)352 credentials_native_type_check (GCredentialsType requested_type,
353 const char *op)
354 {
355 GEnumClass *enum_class;
356 GEnumValue *requested;
357 #if G_CREDENTIALS_SUPPORTED
358 GEnumValue *supported;
359 #endif
360
361 #if G_CREDENTIALS_SUPPORTED
362 if (requested_type == G_CREDENTIALS_NATIVE_TYPE)
363 return TRUE;
364 #endif
365
366 enum_class = g_type_class_ref (g_credentials_type_get_type ());
367 requested = g_enum_get_value (enum_class, requested_type);
368
369 #if G_CREDENTIALS_SUPPORTED
370 supported = g_enum_get_value (enum_class, G_CREDENTIALS_NATIVE_TYPE);
371 g_assert (supported);
372 g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
373 "but only %s is supported on this platform.",
374 op, op,
375 requested ? requested->value_name : "(unknown)",
376 supported->value_name);
377 #else
378 g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
379 "but there is no support for GCredentials on this platform.",
380 op, op,
381 requested ? requested->value_name : "(unknown)");
382 #endif
383
384 g_type_class_unref (enum_class);
385 return FALSE;
386 }
387
388 /**
389 * g_credentials_get_native: (skip)
390 * @credentials: A #GCredentials.
391 * @native_type: The type of native credentials to get.
392 *
393 * Gets a pointer to native credentials of type @native_type from
394 * @credentials.
395 *
396 * It is a programming error (which will cause a warning to be
397 * logged) to use this method if there is no #GCredentials support for
398 * the OS or if @native_type isn't supported by the OS.
399 *
400 * Returns: The pointer to native credentials or %NULL if the
401 * operation there is no #GCredentials support for the OS or if
402 * @native_type isn't supported by the OS. Do not free the returned
403 * data, it is owned by @credentials.
404 *
405 * Since: 2.26
406 */
407 gpointer
g_credentials_get_native(GCredentials * credentials,GCredentialsType native_type)408 g_credentials_get_native (GCredentials *credentials,
409 GCredentialsType native_type)
410 {
411 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
412
413 if (!credentials_native_type_check (native_type, "get"))
414 return NULL;
415
416 #if G_CREDENTIALS_USE_SOLARIS_UCRED
417 return credentials->native;
418 #elif G_CREDENTIALS_SUPPORTED
419 return &credentials->native;
420 #else
421 g_assert_not_reached ();
422 #endif
423 }
424
425 /**
426 * g_credentials_set_native:
427 * @credentials: A #GCredentials.
428 * @native_type: The type of native credentials to set.
429 * @native: (not nullable): A pointer to native credentials.
430 *
431 * Copies the native credentials of type @native_type from @native
432 * into @credentials.
433 *
434 * It is a programming error (which will cause a warning to be
435 * logged) to use this method if there is no #GCredentials support for
436 * the OS or if @native_type isn't supported by the OS.
437 *
438 * Since: 2.26
439 */
440 void
g_credentials_set_native(GCredentials * credentials,GCredentialsType native_type,gpointer native)441 g_credentials_set_native (GCredentials *credentials,
442 GCredentialsType native_type,
443 gpointer native)
444 {
445 if (!credentials_native_type_check (native_type, "set"))
446 return;
447
448 #if G_CREDENTIALS_USE_SOLARIS_UCRED
449 memcpy (credentials->native, native, ucred_size ());
450 #elif G_CREDENTIALS_SUPPORTED
451 memcpy (&credentials->native, native, sizeof (credentials->native));
452 #else
453 g_assert_not_reached ();
454 #endif
455 }
456
457 /* ---------------------------------------------------------------------------------------------------- */
458
459 #ifdef G_OS_UNIX
460 /**
461 * g_credentials_get_unix_user:
462 * @credentials: A #GCredentials
463 * @error: Return location for error or %NULL.
464 *
465 * Tries to get the UNIX user identifier from @credentials. This
466 * method is only available on UNIX platforms.
467 *
468 * This operation can fail if #GCredentials is not supported on the
469 * OS or if the native credentials type does not contain information
470 * about the UNIX user.
471 *
472 * Returns: The UNIX user identifier or -1 if @error is set.
473 *
474 * Since: 2.26
475 */
476 uid_t
g_credentials_get_unix_user(GCredentials * credentials,GError ** error)477 g_credentials_get_unix_user (GCredentials *credentials,
478 GError **error)
479 {
480 uid_t ret;
481
482 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
483 g_return_val_if_fail (error == NULL || *error == NULL, -1);
484
485 #if G_CREDENTIALS_USE_LINUX_UCRED
486 if (linux_ucred_check_valid (&credentials->native, error))
487 ret = credentials->native.uid;
488 else
489 ret = -1;
490 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
491 ret = credentials->native.cmcred_euid;
492 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
493 ret = credentials->native.unp_euid;
494 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
495 ret = credentials->native.uid;
496 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
497 ret = ucred_geteuid (credentials->native);
498 #else
499 ret = -1;
500 g_set_error_literal (error,
501 G_IO_ERROR,
502 G_IO_ERROR_NOT_SUPPORTED,
503 _("There is no GCredentials support for your platform"));
504 #endif
505
506 return ret;
507 }
508
509 /**
510 * g_credentials_get_unix_pid:
511 * @credentials: A #GCredentials
512 * @error: Return location for error or %NULL.
513 *
514 * Tries to get the UNIX process identifier from @credentials. This
515 * method is only available on UNIX platforms.
516 *
517 * This operation can fail if #GCredentials is not supported on the
518 * OS or if the native credentials type does not contain information
519 * about the UNIX process ID.
520 *
521 * Returns: The UNIX process ID, or -1 if @error is set.
522 *
523 * Since: 2.36
524 */
525 pid_t
g_credentials_get_unix_pid(GCredentials * credentials,GError ** error)526 g_credentials_get_unix_pid (GCredentials *credentials,
527 GError **error)
528 {
529 pid_t ret;
530
531 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
532 g_return_val_if_fail (error == NULL || *error == NULL, -1);
533
534 #if G_CREDENTIALS_USE_LINUX_UCRED
535 if (linux_ucred_check_valid (&credentials->native, error))
536 ret = credentials->native.pid;
537 else
538 ret = -1;
539 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
540 ret = credentials->native.cmcred_pid;
541 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
542 ret = credentials->native.unp_pid;
543 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
544 ret = credentials->native.pid;
545 #elif G_CREDENTIALS_USE_SOLARIS_UCRED
546 ret = ucred_getpid (credentials->native);
547 #else
548 ret = -1;
549 g_set_error_literal (error,
550 G_IO_ERROR,
551 G_IO_ERROR_NOT_SUPPORTED,
552 _("GCredentials does not contain a process ID on this OS"));
553 #endif
554
555 return ret;
556 }
557
558 /**
559 * g_credentials_set_unix_user:
560 * @credentials: A #GCredentials.
561 * @uid: The UNIX user identifier to set.
562 * @error: Return location for error or %NULL.
563 *
564 * Tries to set the UNIX user identifier on @credentials. This method
565 * is only available on UNIX platforms.
566 *
567 * This operation can fail if #GCredentials is not supported on the
568 * OS or if the native credentials type does not contain information
569 * about the UNIX user. It can also fail if the OS does not allow the
570 * use of "spoofed" credentials.
571 *
572 * Returns: %TRUE if @uid was set, %FALSE if error is set.
573 *
574 * Since: 2.26
575 */
576 gboolean
g_credentials_set_unix_user(GCredentials * credentials,uid_t uid,GError ** error)577 g_credentials_set_unix_user (GCredentials *credentials,
578 uid_t uid,
579 GError **error)
580 {
581 gboolean ret;
582
583 g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
584 g_return_val_if_fail (uid != -1, FALSE);
585 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
586
587 ret = FALSE;
588 #if G_CREDENTIALS_USE_LINUX_UCRED
589 credentials->native.uid = uid;
590 ret = TRUE;
591 #elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
592 credentials->native.cmcred_euid = uid;
593 ret = TRUE;
594 #elif G_CREDENTIALS_USE_NETBSD_UNPCBID
595 credentials->native.unp_euid = uid;
596 ret = TRUE;
597 #elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
598 credentials->native.uid = uid;
599 ret = TRUE;
600 #elif !G_CREDENTIALS_SPOOFING_SUPPORTED
601 g_set_error_literal (error,
602 G_IO_ERROR,
603 G_IO_ERROR_PERMISSION_DENIED,
604 _("Credentials spoofing is not possible on this OS"));
605 ret = FALSE;
606 #else
607 g_set_error_literal (error,
608 G_IO_ERROR,
609 G_IO_ERROR_NOT_SUPPORTED,
610 _("GCredentials is not implemented on this OS"));
611 ret = FALSE;
612 #endif
613
614 return ret;
615 }
616
617 #endif /* G_OS_UNIX */
618