~psusi/ubuntu/utopic/udisks2/fix-standby

« back to all changes in this revision

Viewing changes to .pc/00git_no_polkit_fallback.patch/src/udisksdaemonutil.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2012-06-13 17:01:30 UTC
  • mfrom: (1.1.1) (2.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20120613170130-9jggrd76bkv1vd0b
Tags: 1.98.0-1
* New upstream release.
* debian/control: Drop ntfsprogs Recommends. It is a transitional package
  for ntfs-3g now, which we already recommend.
* Add 00git_no_polkit_fallback.patch: Fix crash if polkit is not available.
  Patch backported from current upstream git head.
* Add debian/local/integration-test: Latest integration test suite from
  upstream git. 1.99 and later will ship that in the source tarball.
* Add debian/tests/control and debian/tests/upstream-system: DEP-8
  autopkgtest (adapted from udisks package).
* debian/control: Change suggestion of cryptsetup to cryptsetup-bin, as that
  is sufficient for udisks' needs.
* debian/copyright: Fix duplicate copyright line, thanks lintian.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 
2
 *
 
3
 * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program 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
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
#include <glib/gi18n-lib.h>
 
23
 
 
24
#include <stdio.h>
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/stat.h>
 
28
#include <fcntl.h>
 
29
#include <errno.h>
 
30
#include <pwd.h>
 
31
 
 
32
#include <limits.h>
 
33
#include <stdlib.h>
 
34
 
 
35
#include "udisksdaemon.h"
 
36
#include "udisksdaemonutil.h"
 
37
#include "udiskscleanup.h"
 
38
#include "udiskslogging.h"
 
39
#include "udiskslinuxblockobject.h"
 
40
#include "udiskslinuxdriveobject.h"
 
41
 
 
42
#if defined(HAVE_LIBSYSTEMD_LOGIN)
 
43
#include <systemd/sd-login.h>
 
44
#endif
 
45
 
 
46
/**
 
47
 * SECTION:udisksdaemonutil
 
48
 * @title: Utilities
 
49
 * @short_description: Various utility routines
 
50
 *
 
51
 * Various utility routines.
 
52
 */
 
53
 
 
54
/**
 
55
 * udisks_decode_udev_string:
 
56
 * @str: An udev-encoded string or %NULL.
 
57
 *
 
58
 * Unescapes sequences like \x20 to " " and ensures the returned string is valid UTF-8.
 
59
 *
 
60
 * If the string is not valid UTF-8, try as hard as possible to convert to UTF-8.
 
61
 *
 
62
 * If %NULL is passed, then %NULL is returned.
 
63
 *
 
64
 * See udev_util_encode_string() in libudev/libudev-util.c in the udev
 
65
 * tree for what kinds of strings can be used.
 
66
 *
 
67
 * Returns: A valid UTF-8 string that must be freed with g_free().
 
68
 */
 
69
gchar *
 
70
udisks_decode_udev_string (const gchar *str)
 
71
{
 
72
  GString *s;
 
73
  gchar *ret;
 
74
  const gchar *end_valid;
 
75
  guint n;
 
76
 
 
77
  if (str == NULL)
 
78
    {
 
79
      ret = NULL;
 
80
      goto out;
 
81
    }
 
82
 
 
83
  s = g_string_new (NULL);
 
84
  for (n = 0; str[n] != '\0'; n++)
 
85
    {
 
86
      if (str[n] == '\\')
 
87
        {
 
88
          gint val;
 
89
 
 
90
          if (str[n + 1] != 'x' || str[n + 2] == '\0' || str[n + 3] == '\0')
 
91
            {
 
92
              udisks_warning ("**** NOTE: malformed encoded string `%s'", str);
 
93
              break;
 
94
            }
 
95
 
 
96
          val = (g_ascii_xdigit_value (str[n + 2]) << 4) | g_ascii_xdigit_value (str[n + 3]);
 
97
 
 
98
          g_string_append_c (s, val);
 
99
 
 
100
          n += 3;
 
101
        }
 
102
      else
 
103
        {
 
104
          g_string_append_c (s, str[n]);
 
105
        }
 
106
    }
 
107
 
 
108
  if (!g_utf8_validate (s->str, -1, &end_valid))
 
109
    {
 
110
      udisks_warning ("The string `%s' is not valid UTF-8. Invalid characters begins at `%s'", s->str, end_valid);
 
111
      ret = g_strndup (s->str, end_valid - s->str);
 
112
      g_string_free (s, TRUE);
 
113
    }
 
114
  else
 
115
    {
 
116
      ret = g_string_free (s, FALSE);
 
117
    }
 
118
 
 
119
 out:
 
120
  return ret;
 
121
}
 
122
 
 
123
/**
 
124
 * udisks_safe_append_to_object_path:
 
125
 * @str: A #GString to append to.
 
126
 * @s: A UTF-8 string.
 
127
 *
 
128
 * Appends @s to @str in a way such that only characters that can be
 
129
 * used in a D-Bus object path will be used. E.g. a character not in
 
130
 * <literal>[A-Z][a-z][0-9]_</literal> will be escaped as _HEX where
 
131
 * HEX is a two-digit hexadecimal number.
 
132
 *
 
133
 * Note that his mapping is not bijective - e.g. you cannot go back
 
134
 * to the original string.
 
135
 */
 
136
void
 
137
udisks_safe_append_to_object_path (GString      *str,
 
138
                                   const gchar  *s)
 
139
{
 
140
  guint n;
 
141
  for (n = 0; s[n] != '\0'; n++)
 
142
    {
 
143
      gint c = s[n];
 
144
      /* D-Bus spec sez:
 
145
       *
 
146
       * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
 
147
       */
 
148
      if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
 
149
        {
 
150
          g_string_append_c (str, c);
 
151
        }
 
152
      else
 
153
        {
 
154
          /* Escape bytes not in [A-Z][a-z][0-9] as _<hex-with-two-digits> */
 
155
          g_string_append_printf (str, "_%02x", c);
 
156
        }
 
157
    }
 
158
}
 
159
 
 
160
/**
 
161
 * udisks_daemon_util_block_get_size:
 
162
 * @device: A #GUdevDevice for a top-level block device.
 
163
 * @out_media_available: (out): Return location for whether media is available or %NULL.
 
164
 * @out_media_change_detected: (out): Return location for whether media change is detected or %NULL.
 
165
 *
 
166
 * Gets the size of the @device top-level block device, checking for media in the process
 
167
 *
 
168
 * Returns: The size of @device or 0 if no media is available or if unknown.
 
169
 */
 
170
guint64
 
171
udisks_daemon_util_block_get_size (GUdevDevice *device,
 
172
                                   gboolean    *out_media_available,
 
173
                                   gboolean    *out_media_change_detected)
 
174
{
 
175
  gboolean media_available = FALSE;
 
176
  gboolean media_change_detected = TRUE;
 
177
  guint64 size = 0;
 
178
 
 
179
  /* figuring out if media is available is a bit tricky */
 
180
  if (g_udev_device_get_sysfs_attr_as_boolean (device, "removable"))
 
181
    {
 
182
      /* never try to open optical drives (might cause the door to close) or
 
183
       * floppy drives (makes noise)
 
184
       */
 
185
      if (g_udev_device_get_property_as_boolean (device, "ID_DRIVE_FLOPPY"))
 
186
        {
 
187
          /* assume media available */
 
188
          media_available = TRUE;
 
189
          media_change_detected = FALSE;
 
190
        }
 
191
      else if (g_udev_device_get_property_as_boolean (device, "ID_CDROM"))
 
192
        {
 
193
          /* Rely on (careful) work already done by udev's cdrom_id prober */
 
194
          if (g_udev_device_get_property_as_boolean (device, "ID_CDROM_MEDIA"))
 
195
            media_available = TRUE;
 
196
        }
 
197
      else
 
198
        {
 
199
          gint fd;
 
200
          /* For the general case, just rely on open(2) failing with
 
201
           * ENOMEDIUM if no medium is inserted
 
202
           */
 
203
          fd = open (g_udev_device_get_device_file (device), O_RDONLY);
 
204
          if (fd >= 0)
 
205
            {
 
206
              media_available = TRUE;
 
207
              close (fd);
 
208
            }
 
209
        }
 
210
    }
 
211
  else
 
212
    {
 
213
      /* not removable, so media is implicitly available */
 
214
      media_available = TRUE;
 
215
    }
 
216
 
 
217
  if (media_available && size == 0 && media_change_detected)
 
218
    size = g_udev_device_get_sysfs_attr_as_uint64 (device, "size") * 512;
 
219
 
 
220
  if (out_media_available != NULL)
 
221
    *out_media_available = media_available;
 
222
 
 
223
  if (out_media_change_detected != NULL)
 
224
    *out_media_change_detected = media_change_detected;
 
225
 
 
226
  return size;
 
227
}
 
228
 
 
229
 
 
230
/**
 
231
 * udisks_daemon_util_resolve_link:
 
232
 * @path: A path
 
233
 * @name: Name of a symlink in @path.
 
234
 *
 
235
 * Resolves the symlink @path/@name.
 
236
 *
 
237
 * Returns: A canonicalized absolute pathname or %NULL if the symlink
 
238
 * could not be resolved. Free with g_free().
 
239
 */
 
240
gchar *
 
241
udisks_daemon_util_resolve_link (const gchar *path,
 
242
                                 const gchar *name)
 
243
{
 
244
  gchar *full_path;
 
245
  gchar link_path[PATH_MAX];
 
246
  gchar resolved_path[PATH_MAX];
 
247
  gssize num;
 
248
  gboolean found_it;
 
249
 
 
250
  found_it = FALSE;
 
251
 
 
252
  full_path = g_build_filename (path, name, NULL);
 
253
 
 
254
  num = readlink (full_path, link_path, sizeof(link_path) - 1);
 
255
  if (num != -1)
 
256
    {
 
257
      char *absolute_path;
 
258
 
 
259
      link_path[num] = '\0';
 
260
 
 
261
      absolute_path = g_build_filename (path, link_path, NULL);
 
262
      if (realpath (absolute_path, resolved_path) != NULL)
 
263
        {
 
264
          found_it = TRUE;
 
265
        }
 
266
      g_free (absolute_path);
 
267
    }
 
268
  g_free (full_path);
 
269
 
 
270
  if (found_it)
 
271
    return g_strdup (resolved_path);
 
272
  else
 
273
    return NULL;
 
274
}
 
275
 
 
276
/**
 
277
 * udisks_daemon_util_resolve_links:
 
278
 * @path: A path
 
279
 * @dir_name: Name of a directory in @path holding symlinks.
 
280
 *
 
281
 * Resolves all symlinks in @path/@dir_name. This can be used to
 
282
 * easily walk e.g. holders or slaves of block devices.
 
283
 *
 
284
 * Returns: An array of canonicalized absolute pathnames. Free with g_strfreev().
 
285
 */
 
286
gchar **
 
287
udisks_daemon_util_resolve_links (const gchar *path,
 
288
                                  const gchar *dir_name)
 
289
{
 
290
  gchar *s;
 
291
  GDir *dir;
 
292
  const gchar *name;
 
293
  GPtrArray *p;
 
294
 
 
295
  p = g_ptr_array_new ();
 
296
 
 
297
  s = g_build_filename (path, dir_name, NULL);
 
298
  dir = g_dir_open (s, 0, NULL);
 
299
  if (dir == NULL)
 
300
    goto out;
 
301
  while ((name = g_dir_read_name (dir)) != NULL)
 
302
    {
 
303
      gchar *resolved;
 
304
      resolved = udisks_daemon_util_resolve_link (s, name);
 
305
      if (resolved != NULL)
 
306
        g_ptr_array_add (p, resolved);
 
307
    }
 
308
  g_ptr_array_add (p, NULL);
 
309
 
 
310
 out:
 
311
  if (dir != NULL)
 
312
    g_dir_close (dir);
 
313
  g_free (s);
 
314
 
 
315
  return (gchar **) g_ptr_array_free (p, FALSE);
 
316
}
 
317
 
 
318
 
 
319
/**
 
320
 * udisks_daemon_util_setup_by_user:
 
321
 * @daemon: A #UDisksDaemon.
 
322
 * @object: The #GDBusObject that the call is on or %NULL.
 
323
 * @user: The user in question.
 
324
 *
 
325
 * Checks whether the device represented by @object (if any) has been
 
326
 * setup by @user.
 
327
 *
 
328
 * Returns: %TRUE if @object has been set-up by @user, %FALSE if not.
 
329
 */
 
330
gboolean
 
331
udisks_daemon_util_setup_by_user (UDisksDaemon *daemon,
 
332
                                  UDisksObject *object,
 
333
                                  uid_t         user)
 
334
{
 
335
  gboolean ret;
 
336
  UDisksBlock *block = NULL;
 
337
  UDisksPartition *partition = NULL;
 
338
  UDisksCleanup *cleanup;
 
339
  uid_t setup_by_user;
 
340
  UDisksObject *crypto_object;
 
341
 
 
342
  ret = FALSE;
 
343
 
 
344
  cleanup = udisks_daemon_get_cleanup (daemon);
 
345
  block = udisks_object_get_block (object);
 
346
  if (block == NULL)
 
347
    goto out;
 
348
  partition = udisks_object_get_partition (object);
 
349
 
 
350
  /* loop devices */
 
351
  if (udisks_cleanup_has_loop (cleanup, udisks_block_get_device (block), &setup_by_user))
 
352
    {
 
353
      if (setup_by_user == user)
 
354
        {
 
355
          ret = TRUE;
 
356
          goto out;
 
357
        }
 
358
    }
 
359
 
 
360
  /* partition of a loop device */
 
361
  if (partition != NULL)
 
362
    {
 
363
      UDisksObject *partition_object = NULL;
 
364
      partition_object = udisks_daemon_find_object (daemon, udisks_partition_get_table (partition));
 
365
      if (partition_object != NULL)
 
366
        {
 
367
          if (udisks_daemon_util_setup_by_user (daemon, partition_object, user))
 
368
            {
 
369
              ret = TRUE;
 
370
              g_object_unref (partition_object);
 
371
              goto out;
 
372
            }
 
373
          g_object_unref (partition_object);
 
374
        }
 
375
    }
 
376
 
 
377
  /* LUKS devices */
 
378
  crypto_object = udisks_daemon_find_object (daemon, udisks_block_get_crypto_backing_device (block));
 
379
  if (crypto_object != NULL)
 
380
    {
 
381
      UDisksBlock *crypto_block;
 
382
      crypto_block = udisks_object_peek_block (crypto_object);
 
383
      if (udisks_cleanup_find_unlocked_luks (cleanup,
 
384
                                             udisks_block_get_device_number (crypto_block),
 
385
                                             &setup_by_user))
 
386
        {
 
387
          if (setup_by_user == user)
 
388
            {
 
389
              ret = TRUE;
 
390
              g_object_unref (crypto_object);
 
391
              goto out;
 
392
            }
 
393
        }
 
394
      g_object_unref (crypto_object);
 
395
    }
 
396
 
 
397
 out:
 
398
  g_clear_object (&partition);
 
399
  g_clear_object (&block);
 
400
  return ret;
 
401
}
 
402
 
 
403
/* Need this until we can depend on a libpolkit with this bugfix
 
404
 *
 
405
 * http://cgit.freedesktop.org/polkit/commit/?h=wip/js-rule-files&id=224f7b892478302dccbe7e567b013d3c73d376fd
 
406
 */
 
407
static void
 
408
_safe_polkit_details_insert (PolkitDetails *details, const gchar *key, const gchar *value)
 
409
{
 
410
  if (value != NULL && strlen (value) > 0)
 
411
    polkit_details_insert (details, key, value);
 
412
}
 
413
 
 
414
static void
 
415
_safe_polkit_details_insert_int (PolkitDetails *details, const gchar *key, gint value)
 
416
{
 
417
  gchar buf[32];
 
418
  snprintf (buf, sizeof buf, "%d", value);
 
419
  polkit_details_insert (details, key, buf);
 
420
}
 
421
 
 
422
static void
 
423
_safe_polkit_details_insert_uint64 (PolkitDetails *details, const gchar *key, guint64 value)
 
424
{
 
425
  gchar buf[32];
 
426
  snprintf (buf, sizeof buf, "0x%08llx", (unsigned long long int) value);
 
427
  polkit_details_insert (details, key, buf);
 
428
}
 
429
 
 
430
static gboolean
 
431
check_authorization_no_polkit (UDisksDaemon          *daemon,
 
432
                               UDisksObject          *object,
 
433
                               const gchar           *action_id,
 
434
                               GVariant              *options,
 
435
                               const gchar           *message,
 
436
                               GDBusMethodInvocation *invocation)
 
437
{
 
438
  gboolean ret = FALSE;
 
439
  uid_t caller_uid = -1;
 
440
  GError *error = NULL;
 
441
 
 
442
  if (!udisks_daemon_util_get_caller_uid_sync (daemon,
 
443
                                               invocation,
 
444
                                               NULL,         /* GCancellable* */
 
445
                                               &caller_uid,
 
446
                                               NULL,         /* gid_t *out_gid */
 
447
                                               NULL,         /* gchar **out_user_name */
 
448
                                               &error))
 
449
    {
 
450
      g_dbus_method_invocation_return_error (invocation,
 
451
                                             UDISKS_ERROR,
 
452
                                             UDISKS_ERROR_FAILED,
 
453
                                             "Error getting uid for caller with bus name %s: %s (%s, %d)",
 
454
                                             g_dbus_method_invocation_get_sender (invocation),
 
455
                                             error->message, g_quark_to_string (error->domain), error->code);
 
456
      g_clear_error (&error);
 
457
      goto out;
 
458
    }
 
459
 
 
460
  /* only allow root */
 
461
  if (caller_uid == 0)
 
462
    {
 
463
      ret = TRUE;
 
464
    }
 
465
  else
 
466
    {
 
467
      g_dbus_method_invocation_return_error_literal (invocation,
 
468
                                                     UDISKS_ERROR,
 
469
                                                     UDISKS_ERROR_NOT_AUTHORIZED,
 
470
                                                     "Not authorized to perform operation (polkit authority not available and caller is not uid 0)");
 
471
    }
 
472
 
 
473
 out:
 
474
  return ret;
 
475
}
 
476
 
 
477
/**
 
478
 * udisks_daemon_util_check_authorization_sync:
 
479
 * @daemon: A #UDisksDaemon.
 
480
 * @object: (allow-none): The #GDBusObject that the call is on or %NULL.
 
481
 * @action_id: The action id to check for.
 
482
 * @options: (allow-none): A #GVariant to check for the <quote>auth.no_user_interaction</quote> option or %NULL.
 
483
 * @message: The message to convey (use N_).
 
484
 * @invocation: The invocation to check for.
 
485
 *
 
486
 * Checks if the caller represented by @invocation is authorized for
 
487
 * the action identified by @action_id, optionally displaying @message
 
488
 * if authentication is needed. Additionally, if the caller is not
 
489
 * authorized, the appropriate error is already returned to the caller
 
490
 * via @invocation.
 
491
 *
 
492
 * The calling thread is blocked for the duration of the authorization
 
493
 * check which could be a very long time since it may involve
 
494
 * presenting an authentication dialog and having a human user use
 
495
 * it. If <quote>auth.no_user_interaction</quote> in @options is %TRUE
 
496
 * no authentication dialog will be presented and the check is not
 
497
 * expected to take a long time.
 
498
 *
 
499
 * See <xref linkend="udisks-polkit-details"/> for the variables that
 
500
 * can be used in @message but note that not all variables can be used
 
501
 * in all checks. For example, any check involving a #UDisksDrive or a
 
502
 * #UDisksBlock object can safely include the fragment
 
503
 * <quote>$(drive)</quote> since it will always expand to the name of
 
504
 * the drive, e.g. <quote>INTEL SSDSA2MH080G1GC (/dev/sda1)</quote> or
 
505
 * the block device file e.g. <quote>/dev/vg_lucifer/lv_root</quote>
 
506
 * or <quote>/dev/sda1</quote>. However this won't work for operations
 
507
 * that isn't on a drive or block device, for example calls on the
 
508
 * <link linkend="gdbus-interface-org-freedesktop-UDisks2-Manager.top_of_page">Manager</link>
 
509
 * object.
 
510
 *
 
511
 * Returns: %TRUE if caller is authorized, %FALSE if not.
 
512
 */
 
513
gboolean
 
514
udisks_daemon_util_check_authorization_sync (UDisksDaemon          *daemon,
 
515
                                             UDisksObject          *object,
 
516
                                             const gchar           *action_id,
 
517
                                             GVariant              *options,
 
518
                                             const gchar           *message,
 
519
                                             GDBusMethodInvocation *invocation)
 
520
{
 
521
  PolkitAuthority *authority = NULL;
 
522
  PolkitSubject *subject = NULL;
 
523
  PolkitDetails *details = NULL;
 
524
  PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
 
525
  PolkitAuthorizationResult *result = NULL;
 
526
  GError *error = NULL;
 
527
  gboolean ret = FALSE;
 
528
  UDisksBlock *block = NULL;
 
529
  UDisksDrive *drive = NULL;
 
530
  UDisksPartition *partition = NULL;
 
531
  UDisksObject *block_object = NULL;
 
532
  UDisksObject *drive_object = NULL;
 
533
  gboolean auth_no_user_interaction = FALSE;
 
534
  const gchar *details_device = NULL;
 
535
  gchar *details_drive = NULL;
 
536
 
 
537
  authority = udisks_daemon_get_authority (daemon);
 
538
  if (authority == NULL)
 
539
    {
 
540
      ret = check_authorization_no_polkit (daemon, object, action_id, options, message, invocation);
 
541
      goto out;
 
542
    }
 
543
 
 
544
  subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
 
545
  if (options != NULL)
 
546
    {
 
547
      g_variant_lookup (options,
 
548
                        "auth.no_user_interaction",
 
549
                        "b",
 
550
                        &auth_no_user_interaction);
 
551
    }
 
552
  if (!auth_no_user_interaction)
 
553
    flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
 
554
 
 
555
  details = polkit_details_new ();
 
556
  polkit_details_insert (details, "polkit.message", message);
 
557
  polkit_details_insert (details, "polkit.gettext_domain", "udisks2");
 
558
 
 
559
  /* Find drive associated with the block device, if any */
 
560
  if (object != NULL)
 
561
    {
 
562
      block = udisks_object_get_block (object);
 
563
      if (block != NULL)
 
564
        {
 
565
          block_object = g_object_ref (object);
 
566
          drive_object = udisks_daemon_find_object (daemon, udisks_block_get_drive (block));
 
567
          if (drive_object != NULL)
 
568
            drive = udisks_object_get_drive (drive_object);
 
569
        }
 
570
 
 
571
      partition = udisks_object_get_partition (object);
 
572
    }
 
573
 
 
574
  if (block != NULL)
 
575
    details_device = udisks_block_get_preferred_device (block);
 
576
 
 
577
  /* If we have a drive, use vendor/model in the message (in addition to Block:preferred-device) */
 
578
  if (drive != NULL)
 
579
    {
 
580
      gchar *s;
 
581
      const gchar *vendor;
 
582
      const gchar *model;
 
583
 
 
584
      vendor = udisks_drive_get_vendor (drive);
 
585
      model = udisks_drive_get_model (drive);
 
586
      if (vendor == NULL)
 
587
        vendor = "";
 
588
      if (model == NULL)
 
589
        model = "";
 
590
 
 
591
      if (strlen (vendor) > 0 && strlen (model) > 0)
 
592
        s = g_strdup_printf ("%s %s", vendor, model);
 
593
      else if (strlen (vendor) > 0)
 
594
        s = g_strdup (vendor);
 
595
      else
 
596
        s = g_strdup (model);
 
597
 
 
598
      if (block != NULL)
 
599
        {
 
600
          details_drive = g_strdup_printf ("%s (%s)", s, udisks_block_get_preferred_device (block));
 
601
        }
 
602
      else
 
603
        {
 
604
          details_drive = s;
 
605
          s = NULL;
 
606
        }
 
607
      g_free (s);
 
608
 
 
609
      _safe_polkit_details_insert (details, "drive.wwn", udisks_drive_get_wwn (drive));
 
610
      _safe_polkit_details_insert (details, "drive.serial", udisks_drive_get_serial (drive));
 
611
      _safe_polkit_details_insert (details, "drive.vendor", udisks_drive_get_vendor (drive));
 
612
      _safe_polkit_details_insert (details, "drive.model", udisks_drive_get_model (drive));
 
613
      _safe_polkit_details_insert (details, "drive.revision", udisks_drive_get_revision (drive));
 
614
      if (udisks_drive_get_removable (drive))
 
615
        polkit_details_insert (details, "drive.removable", "true");
 
616
    }
 
617
 
 
618
  if (block != NULL)
 
619
    {
 
620
      _safe_polkit_details_insert (details, "id.type",    udisks_block_get_id_type (block));
 
621
      _safe_polkit_details_insert (details, "id.usage",   udisks_block_get_id_usage (block));
 
622
      _safe_polkit_details_insert (details, "id.version", udisks_block_get_id_version (block));
 
623
      _safe_polkit_details_insert (details, "id.label",   udisks_block_get_id_label (block));
 
624
      _safe_polkit_details_insert (details, "id.uuid",    udisks_block_get_id_uuid (block));
 
625
    }
 
626
 
 
627
  if (partition != NULL)
 
628
    {
 
629
      _safe_polkit_details_insert_int    (details, "partition.number", udisks_partition_get_number (partition));
 
630
      _safe_polkit_details_insert        (details, "partition.type",   udisks_partition_get_type_ (partition));
 
631
      _safe_polkit_details_insert_uint64 (details, "partition.flags",  udisks_partition_get_flags (partition));
 
632
      _safe_polkit_details_insert        (details, "partition.name",   udisks_partition_get_name (partition));
 
633
      _safe_polkit_details_insert        (details, "partition.uuid",   udisks_partition_get_uuid (partition));
 
634
    }
 
635
 
 
636
  /* Fall back to Block:preferred-device */
 
637
  if (details_drive == NULL && block != NULL)
 
638
    details_drive = udisks_block_dup_preferred_device (block);
 
639
 
 
640
  if (details_device != NULL)
 
641
    polkit_details_insert (details, "device", details_device);
 
642
  if (details_drive != NULL)
 
643
    polkit_details_insert (details, "drive", details_drive);
 
644
 
 
645
  error = NULL;
 
646
  result = polkit_authority_check_authorization_sync (authority,
 
647
                                                      subject,
 
648
                                                      action_id,
 
649
                                                      details,
 
650
                                                      flags,
 
651
                                                      NULL, /* GCancellable* */
 
652
                                                      &error);
 
653
  if (result == NULL)
 
654
    {
 
655
      g_dbus_method_invocation_return_error (invocation,
 
656
                                             UDISKS_ERROR,
 
657
                                             UDISKS_ERROR_FAILED,
 
658
                                             "Error checking authorization: %s (%s, %d)",
 
659
                                             error->message,
 
660
                                             g_quark_to_string (error->domain),
 
661
                                             error->code);
 
662
      g_error_free (error);
 
663
      goto out;
 
664
    }
 
665
  if (!polkit_authorization_result_get_is_authorized (result))
 
666
    {
 
667
      if (polkit_authorization_result_get_dismissed (result))
 
668
        g_dbus_method_invocation_return_error_literal (invocation,
 
669
                                                       UDISKS_ERROR,
 
670
                                                       UDISKS_ERROR_NOT_AUTHORIZED_DISMISSED,
 
671
                                                       "The authentication dialog was dismissed");
 
672
      else
 
673
        g_dbus_method_invocation_return_error_literal (invocation,
 
674
                                                       UDISKS_ERROR,
 
675
                                                       polkit_authorization_result_get_is_challenge (result) ?
 
676
                                                       UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN :
 
677
                                                       UDISKS_ERROR_NOT_AUTHORIZED,
 
678
                                                       "Not authorized to perform operation");
 
679
      goto out;
 
680
    }
 
681
 
 
682
  ret = TRUE;
 
683
 
 
684
 out:
 
685
  g_free (details_drive);
 
686
  g_clear_object (&block_object);
 
687
  g_clear_object (&drive_object);
 
688
  g_clear_object (&block);
 
689
  g_clear_object (&partition);
 
690
  g_clear_object (&drive);
 
691
  g_clear_object (&subject);
 
692
  g_clear_object (&details);
 
693
  g_clear_object (&result);
 
694
  return ret;
 
695
}
 
696
 
 
697
/* ---------------------------------------------------------------------------------------------------- */
 
698
 
 
699
/**
 
700
 * udisks_daemon_util_get_caller_uid_sync:
 
701
 * @daemon: A #UDisksDaemon.
 
702
 * @invocation: A #GDBusMethodInvocation.
 
703
 * @cancellable: (allow-none): A #GCancellable or %NULL.
 
704
 * @out_uid: (out): Return location for resolved uid or %NULL.
 
705
 * @out_gid: (out) (allow-none): Return location for resolved gid or %NULL.
 
706
 * @out_user_name: (out) (allow-none): Return location for resolved user name or %NULL.
 
707
 * @error: Return location for error.
 
708
 *
 
709
 * Gets the UNIX user id (and possibly group id and user name) of the
 
710
 * peer represented by @invocation.
 
711
 *
 
712
 * Returns: %TRUE if the user id (and possibly group id) was obtained, %FALSE otherwise
 
713
 */
 
714
gboolean
 
715
udisks_daemon_util_get_caller_uid_sync (UDisksDaemon            *daemon,
 
716
                                        GDBusMethodInvocation   *invocation,
 
717
                                        GCancellable            *cancellable,
 
718
                                        uid_t                   *out_uid,
 
719
                                        gid_t                   *out_gid,
 
720
                                        gchar                  **out_user_name,
 
721
                                        GError                 **error)
 
722
{
 
723
  gboolean ret;
 
724
  const gchar *caller;
 
725
  GVariant *value;
 
726
  GError *local_error;
 
727
  uid_t uid;
 
728
 
 
729
  /* TODO: cache this on @daemon */
 
730
 
 
731
  ret = FALSE;
 
732
 
 
733
  caller = g_dbus_method_invocation_get_sender (invocation);
 
734
 
 
735
  local_error = NULL;
 
736
  value = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation),
 
737
                                       "org.freedesktop.DBus",  /* bus name */
 
738
                                       "/org/freedesktop/DBus", /* object path */
 
739
                                       "org.freedesktop.DBus",  /* interface */
 
740
                                       "GetConnectionUnixUser", /* method */
 
741
                                       g_variant_new ("(s)", caller),
 
742
                                       G_VARIANT_TYPE ("(u)"),
 
743
                                       G_DBUS_CALL_FLAGS_NONE,
 
744
                                       -1, /* timeout_msec */
 
745
                                       cancellable,
 
746
                                       &local_error);
 
747
  if (value == NULL)
 
748
    {
 
749
      g_set_error (error,
 
750
                   UDISKS_ERROR,
 
751
                   UDISKS_ERROR_FAILED,
 
752
                   "Error determining uid of caller %s: %s (%s, %d)",
 
753
                   caller,
 
754
                   local_error->message,
 
755
                   g_quark_to_string (local_error->domain),
 
756
                   local_error->code);
 
757
      g_error_free (local_error);
 
758
      goto out;
 
759
    }
 
760
 
 
761
  G_STATIC_ASSERT (sizeof (uid_t) == sizeof (guint32));
 
762
  g_variant_get (value, "(u)", &uid);
 
763
  if (out_uid != NULL)
 
764
    *out_uid = uid;
 
765
 
 
766
  if (out_gid != NULL || out_user_name != NULL)
 
767
    {
 
768
      struct passwd pwstruct;
 
769
      gchar pwbuf[8192];
 
770
      static struct passwd *pw;
 
771
      int rc;
 
772
 
 
773
      rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
 
774
      if (rc == 0 && pw == NULL)
 
775
        {
 
776
          g_set_error (error,
 
777
                       UDISKS_ERROR,
 
778
                       UDISKS_ERROR_FAILED,
 
779
                       "User with uid %d does not exist", (gint) uid);
 
780
        }
 
781
      else if (pw == NULL)
 
782
        {
 
783
          g_set_error (error,
 
784
                       UDISKS_ERROR,
 
785
                       UDISKS_ERROR_FAILED,
 
786
                       "Error looking up passwd struct for uid %d: %m", (gint) uid);
 
787
          goto out;
 
788
        }
 
789
      if (out_gid != NULL)
 
790
        *out_gid = pw->pw_gid;
 
791
      if (out_user_name != NULL)
 
792
        *out_user_name = g_strdup (pwstruct.pw_name);
 
793
    }
 
794
 
 
795
  ret = TRUE;
 
796
 
 
797
 out:
 
798
  return ret;
 
799
}
 
800
 
 
801
/* ---------------------------------------------------------------------------------------------------- */
 
802
 
 
803
/**
 
804
 * udisks_daemon_util_get_caller_pid_sync:
 
805
 * @daemon: A #UDisksDaemon.
 
806
 * @invocation: A #GDBusMethodInvocation.
 
807
 * @cancellable: (allow-none): A #GCancellable or %NULL.
 
808
 * @out_pid: (out): Return location for resolved pid or %NULL.
 
809
 * @error: Return location for error.
 
810
 *
 
811
 * Gets the UNIX process id of the peer represented by @invocation.
 
812
 *
 
813
 * Returns: %TRUE if the process id was obtained, %FALSE otherwise
 
814
 */
 
815
gboolean
 
816
udisks_daemon_util_get_caller_pid_sync (UDisksDaemon            *daemon,
 
817
                                        GDBusMethodInvocation   *invocation,
 
818
                                        GCancellable            *cancellable,
 
819
                                        pid_t                   *out_pid,
 
820
                                        GError                 **error)
 
821
{
 
822
  gboolean ret;
 
823
  const gchar *caller;
 
824
  GVariant *value;
 
825
  GError *local_error;
 
826
  pid_t pid;
 
827
 
 
828
  /* TODO: cache this on @daemon */
 
829
 
 
830
  ret = FALSE;
 
831
 
 
832
  caller = g_dbus_method_invocation_get_sender (invocation);
 
833
 
 
834
  local_error = NULL;
 
835
  value = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation),
 
836
                                       "org.freedesktop.DBus",  /* bus name */
 
837
                                       "/org/freedesktop/DBus", /* object path */
 
838
                                       "org.freedesktop.DBus",  /* interface */
 
839
                                       "GetConnectionUnixProcessID", /* method */
 
840
                                       g_variant_new ("(s)", caller),
 
841
                                       G_VARIANT_TYPE ("(u)"),
 
842
                                       G_DBUS_CALL_FLAGS_NONE,
 
843
                                       -1, /* timeout_msec */
 
844
                                       cancellable,
 
845
                                       &local_error);
 
846
  if (value == NULL)
 
847
    {
 
848
      g_set_error (error,
 
849
                   UDISKS_ERROR,
 
850
                   UDISKS_ERROR_FAILED,
 
851
                   "Error determining uid of caller %s: %s (%s, %d)",
 
852
                   caller,
 
853
                   local_error->message,
 
854
                   g_quark_to_string (local_error->domain),
 
855
                   local_error->code);
 
856
      g_error_free (local_error);
 
857
      goto out;
 
858
    }
 
859
 
 
860
  G_STATIC_ASSERT (sizeof (uid_t) == sizeof (guint32));
 
861
  g_variant_get (value, "(u)", &pid);
 
862
  if (out_pid != NULL)
 
863
    *out_pid = pid;
 
864
 
 
865
  ret = TRUE;
 
866
 
 
867
 out:
 
868
  return ret;
 
869
}
 
870
 
 
871
/* ---------------------------------------------------------------------------------------------------- */
 
872
 
 
873
/**
 
874
 * udisks_daemon_util_dup_object:
 
875
 * @interface_: (type GDBusInterface): A #GDBusInterface<!-- -->-derived instance.
 
876
 * @error: %NULL, or an unset #GError to set if the return value is %NULL.
 
877
 *
 
878
 * Gets the enclosing #UDisksObject for @interface, if any.
 
879
 *
 
880
 * Returns: (transfer full) (type UDisksObject): Either %NULL or a
 
881
 * #UDisksObject<!-- -->-derived instance that must be released with
 
882
 * g_object_unref().
 
883
 */
 
884
gpointer
 
885
udisks_daemon_util_dup_object (gpointer   interface_,
 
886
                               GError   **error)
 
887
{
 
888
  gpointer ret;
 
889
 
 
890
  g_return_val_if_fail (G_IS_DBUS_INTERFACE (interface_), NULL);
 
891
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
892
 
 
893
  ret = g_dbus_interface_dup_object (interface_);
 
894
  if (ret == NULL)
 
895
    {
 
896
      g_set_error (error,
 
897
                   UDISKS_ERROR,
 
898
                   UDISKS_ERROR_FAILED,
 
899
                   "No enclosing object for interface");
 
900
    }
 
901
 
 
902
  return ret;
 
903
}
 
904
 
 
905
static void
 
906
escaper (GString *s, const gchar *str)
 
907
{
 
908
  const gchar *p;
 
909
  for (p = str; *p != '\0'; p++)
 
910
    {
 
911
      gint c = *p;
 
912
      switch (c)
 
913
        {
 
914
        case '"':
 
915
          g_string_append (s, "\\\"");
 
916
          break;
 
917
 
 
918
        case '\\':
 
919
          g_string_append (s, "\\\\");
 
920
          break;
 
921
 
 
922
        default:
 
923
          g_string_append_c (s, c);
 
924
          break;
 
925
        }
 
926
    }
 
927
}
 
928
 
 
929
/**
 
930
 * udisks_daemon_util_escape_and_quote:
 
931
 * @str: The string to escape.
 
932
 *
 
933
 * Like udisks_daemon_util_escape() but also wraps the result in
 
934
 * double-quotes.
 
935
 *
 
936
 * Returns: The double-quoted and escaped string. Free with g_free().
 
937
 */
 
938
gchar *
 
939
udisks_daemon_util_escape_and_quote (const gchar *str)
 
940
{
 
941
  GString *s;
 
942
 
 
943
  g_return_val_if_fail (str != NULL, NULL);
 
944
 
 
945
  s = g_string_new ("\"");
 
946
  escaper (s, str);
 
947
  g_string_append_c (s, '"');
 
948
 
 
949
  return g_string_free (s, FALSE);
 
950
}
 
951
 
 
952
/**
 
953
 * udisks_daemon_util_escape:
 
954
 * @str: The string to escape.
 
955
 *
 
956
 * Escapes double-quotes (&quot;) and back-slashes (\) in a string
 
957
 * using back-slash (\).
 
958
 *
 
959
 * Returns: The escaped string. Free with g_free().
 
960
 */
 
961
gchar *
 
962
udisks_daemon_util_escape (const gchar *str)
 
963
{
 
964
  GString *s;
 
965
 
 
966
  g_return_val_if_fail (str != NULL, NULL);
 
967
 
 
968
  s = g_string_new (NULL);
 
969
  escaper (s, str);
 
970
 
 
971
  return g_string_free (s, FALSE);
 
972
}
 
973
 
 
974
/**
 
975
 * udisks_daemon_util_on_other_seat:
 
976
 * @daemon: A #UDisksDaemon.
 
977
 * @object: The #GDBusObject that the call is on or %NULL.
 
978
 * @process: The process to check for.
 
979
 *
 
980
 * Checks whether the device represented by @object (if any) is plugged into
 
981
 * a seat where the caller represented by @process is logged in.
 
982
 *
 
983
 * This works if @object is a drive or a block object.
 
984
 *
 
985
 * Returns: %TRUE if @object and @process is on the same seat, %FALSE otherwise.
 
986
 */
 
987
gboolean
 
988
udisks_daemon_util_on_same_seat (UDisksDaemon          *daemon,
 
989
                                 UDisksObject          *object,
 
990
                                 pid_t                  process)
 
991
{
 
992
#if !defined(HAVE_LIBSYSTEMD_LOGIN)
 
993
  /* if we don't have systemd, assume it's always the same seat */
 
994
  return TRUE;
 
995
#else
 
996
  gboolean ret = FALSE;
 
997
  char *session = NULL;
 
998
  char *seat = NULL;
 
999
  const gchar *drive_seat;
 
1000
  UDisksObject *drive_object = NULL;
 
1001
  UDisksDrive *drive = NULL;
 
1002
 
 
1003
  if (UDISKS_IS_LINUX_BLOCK_OBJECT (object))
 
1004
    {
 
1005
      UDisksLinuxBlockObject *linux_block_object;
 
1006
      UDisksBlock *block;
 
1007
      linux_block_object = UDISKS_LINUX_BLOCK_OBJECT (object);
 
1008
      block = udisks_object_get_block (UDISKS_OBJECT (linux_block_object));
 
1009
      if (block != NULL)
 
1010
        {
 
1011
          drive_object = udisks_daemon_find_object (daemon, udisks_block_get_drive (block));
 
1012
          g_object_unref (block);
 
1013
        }
 
1014
    }
 
1015
  else if (UDISKS_IS_LINUX_DRIVE_OBJECT (object))
 
1016
    {
 
1017
      drive_object = g_object_ref (object);
 
1018
    }
 
1019
 
 
1020
  if (drive_object == NULL)
 
1021
    goto out;
 
1022
 
 
1023
  drive = udisks_object_get_drive (UDISKS_OBJECT (drive_object));
 
1024
  if (drive == NULL)
 
1025
    goto out;
 
1026
 
 
1027
  /* It's not unexpected to not find a session, nor a seat associated with @process */
 
1028
  if (sd_pid_get_session (process, &session) == 0)
 
1029
    sd_session_get_seat (session, &seat);
 
1030
 
 
1031
  /* If we don't know the seat of the caller, we assume the device is always on another seat */
 
1032
  if (seat == NULL)
 
1033
    goto out;
 
1034
 
 
1035
  drive_seat = udisks_drive_get_seat (drive);
 
1036
  if (g_strcmp0 (seat, drive_seat) == 0)
 
1037
    {
 
1038
      ret = TRUE;
 
1039
      goto out;
 
1040
    }
 
1041
 
 
1042
 out:
 
1043
  free (seat);
 
1044
  free (session);
 
1045
  g_clear_object (&drive_object);
 
1046
  g_clear_object (&drive);
 
1047
  return ret;
 
1048
#endif /* HAVE_LIBSYSTEMD_LOGIN */
 
1049
}