1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus
3 *
4 * Copyright (C) 2002, 2003, 2004, 2005  Red Hat, Inc.
5 * Copyright (C) 2003 CodeFactory AB
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#include <config.h>
26#include "dbus-sysdeps.h"
27#include "dbus-sysdeps-unix.h"
28#include "dbus-internals.h"
29#include "dbus-pipe.h"
30#include "dbus-protocol.h"
31#include "dbus-string.h"
32#define DBUS_USERDB_INCLUDES_PRIVATE 1
33#include "dbus-userdb.h"
34#include "dbus-test.h"
35
36#include <sys/types.h>
37#include <stdlib.h>
38#include <string.h>
39#include <signal.h>
40#include <unistd.h>
41#include <stdio.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <sys/stat.h>
45#include <grp.h>
46#include <sys/socket.h>
47#include <dirent.h>
48#include <sys/un.h>
49#include <syslog.h>
50
51#ifdef HAVE_SYS_SYSLIMITS_H
52#include <sys/syslimits.h>
53#endif
54
55#ifndef O_BINARY
56#define O_BINARY 0
57#endif
58
59/**
60 * @addtogroup DBusInternalsUtils
61 * @{
62 */
63
64
65/**
66 * Does the chdir, fork, setsid, etc. to become a daemon process.
67 *
68 * @param pidfile #NULL, or pidfile to create
69 * @param print_pid_pipe pipe to print daemon's pid to, or -1 for none
70 * @param error return location for errors
71 * @param keep_umask #TRUE to keep the original umask
72 * @returns #FALSE on failure
73 */
74dbus_bool_t
75_dbus_become_daemon (const DBusString *pidfile,
76                     DBusPipe         *print_pid_pipe,
77                     DBusError        *error,
78                     dbus_bool_t       keep_umask)
79{
80  const char *s;
81  pid_t child_pid;
82  int dev_null_fd;
83
84  _dbus_verbose ("Becoming a daemon...\n");
85
86  _dbus_verbose ("chdir to /\n");
87  if (chdir ("/") < 0)
88    {
89      dbus_set_error (error, DBUS_ERROR_FAILED,
90                      "Could not chdir() to root directory");
91      return FALSE;
92    }
93
94  _dbus_verbose ("forking...\n");
95  switch ((child_pid = fork ()))
96    {
97    case -1:
98      _dbus_verbose ("fork failed\n");
99      dbus_set_error (error, _dbus_error_from_errno (errno),
100                      "Failed to fork daemon: %s", _dbus_strerror (errno));
101      return FALSE;
102      break;
103
104    case 0:
105      _dbus_verbose ("in child, closing std file descriptors\n");
106
107      /* silently ignore failures here, if someone
108       * doesn't have /dev/null we may as well try
109       * to continue anyhow
110       */
111
112      dev_null_fd = open ("/dev/null", O_RDWR);
113      if (dev_null_fd >= 0)
114        {
115          dup2 (dev_null_fd, 0);
116          dup2 (dev_null_fd, 1);
117
118          s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
119          if (s == NULL || *s == '\0')
120            dup2 (dev_null_fd, 2);
121          else
122            _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
123        }
124
125      if (!keep_umask)
126        {
127          /* Get a predictable umask */
128          _dbus_verbose ("setting umask\n");
129          umask (022);
130        }
131
132      _dbus_verbose ("calling setsid()\n");
133      if (setsid () == -1)
134        _dbus_assert_not_reached ("setsid() failed");
135
136      break;
137
138    default:
139      if (!_dbus_write_pid_to_file_and_pipe (pidfile, print_pid_pipe,
140                                             child_pid, error))
141        {
142          _dbus_verbose ("pid file or pipe write failed: %s\n",
143                         error->message);
144          kill (child_pid, SIGTERM);
145          return FALSE;
146        }
147
148      _dbus_verbose ("parent exiting\n");
149      _exit (0);
150      break;
151    }
152
153  return TRUE;
154}
155
156
157/**
158 * Creates a file containing the process ID.
159 *
160 * @param filename the filename to write to
161 * @param pid our process ID
162 * @param error return location for errors
163 * @returns #FALSE on failure
164 */
165static dbus_bool_t
166_dbus_write_pid_file (const DBusString *filename,
167                      unsigned long     pid,
168		      DBusError        *error)
169{
170  const char *cfilename;
171  int fd;
172  FILE *f;
173
174  cfilename = _dbus_string_get_const_data (filename);
175
176  fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
177
178  if (fd < 0)
179    {
180      dbus_set_error (error, _dbus_error_from_errno (errno),
181                      "Failed to open \"%s\": %s", cfilename,
182                      _dbus_strerror (errno));
183      return FALSE;
184    }
185
186  if ((f = fdopen (fd, "w")) == NULL)
187    {
188      dbus_set_error (error, _dbus_error_from_errno (errno),
189                      "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
190      _dbus_close (fd, NULL);
191      return FALSE;
192    }
193
194  if (fprintf (f, "%lu\n", pid) < 0)
195    {
196      dbus_set_error (error, _dbus_error_from_errno (errno),
197                      "Failed to write to \"%s\": %s", cfilename,
198                      _dbus_strerror (errno));
199
200      fclose (f);
201      return FALSE;
202    }
203
204  if (fclose (f) == EOF)
205    {
206      dbus_set_error (error, _dbus_error_from_errno (errno),
207                      "Failed to close \"%s\": %s", cfilename,
208                      _dbus_strerror (errno));
209      return FALSE;
210    }
211
212  return TRUE;
213}
214
215/**
216 * Writes the given pid_to_write to a pidfile (if non-NULL) and/or to a
217 * pipe (if non-NULL). Does nothing if pidfile and print_pid_pipe are both
218 * NULL.
219 *
220 * @param pidfile the file to write to or #NULL
221 * @param print_pid_pipe the pipe to write to or #NULL
222 * @param pid_to_write the pid to write out
223 * @param error error on failure
224 * @returns FALSE if error is set
225 */
226dbus_bool_t
227_dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
228                                  DBusPipe         *print_pid_pipe,
229                                  dbus_pid_t        pid_to_write,
230                                  DBusError        *error)
231{
232  if (pidfile)
233    {
234      _dbus_verbose ("writing pid file %s\n", _dbus_string_get_const_data (pidfile));
235      if (!_dbus_write_pid_file (pidfile,
236                                 pid_to_write,
237                                 error))
238        {
239          _dbus_verbose ("pid file write failed\n");
240          _DBUS_ASSERT_ERROR_IS_SET(error);
241          return FALSE;
242        }
243    }
244  else
245    {
246      _dbus_verbose ("No pid file requested\n");
247    }
248
249  if (print_pid_pipe != NULL && _dbus_pipe_is_valid (print_pid_pipe))
250    {
251      DBusString pid;
252      int bytes;
253
254      _dbus_verbose ("writing our pid to pipe %"PRIuPTR"\n",
255                     print_pid_pipe->fd_or_handle);
256
257      if (!_dbus_string_init (&pid))
258        {
259          _DBUS_SET_OOM (error);
260          return FALSE;
261        }
262
263      if (!_dbus_string_append_int (&pid, pid_to_write) ||
264          !_dbus_string_append (&pid, "\n"))
265        {
266          _dbus_string_free (&pid);
267          _DBUS_SET_OOM (error);
268          return FALSE;
269        }
270
271      bytes = _dbus_string_get_length (&pid);
272      if (_dbus_pipe_write (print_pid_pipe, &pid, 0, bytes, error) != bytes)
273        {
274          /* _dbus_pipe_write sets error only on failure, not short write */
275          if (error != NULL && !dbus_error_is_set(error))
276            {
277              dbus_set_error (error, DBUS_ERROR_FAILED,
278                              "Printing message bus PID: did not write enough bytes\n");
279            }
280          _dbus_string_free (&pid);
281          return FALSE;
282        }
283
284      _dbus_string_free (&pid);
285    }
286  else
287    {
288      _dbus_verbose ("No pid pipe to write to\n");
289    }
290
291  return TRUE;
292}
293
294/**
295 * Verify that after the fork we can successfully change to this user.
296 *
297 * @param user the username given in the daemon configuration
298 * @returns #TRUE if username is valid
299 */
300dbus_bool_t
301_dbus_verify_daemon_user (const char *user)
302{
303  DBusString u;
304
305  _dbus_string_init_const (&u, user);
306
307  return _dbus_get_user_id_and_primary_group (&u, NULL, NULL);
308}
309
310
311/* The HAVE_LIBAUDIT case lives in selinux.c */
312#ifndef HAVE_LIBAUDIT
313/**
314 * Changes the user and group the bus is running as.
315 *
316 * @param user the user to become
317 * @param error return location for errors
318 * @returns #FALSE on failure
319 */
320dbus_bool_t
321_dbus_change_to_daemon_user  (const char    *user,
322                              DBusError     *error)
323{
324  dbus_uid_t uid;
325  dbus_gid_t gid;
326  DBusString u;
327
328  _dbus_string_init_const (&u, user);
329
330  if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
331    {
332      dbus_set_error (error, DBUS_ERROR_FAILED,
333                      "User '%s' does not appear to exist?",
334                      user);
335      return FALSE;
336    }
337
338  /* setgroups() only works if we are a privileged process,
339   * so we don't return error on failure; the only possible
340   * failure is that we don't have perms to do it.
341   *
342   * not sure this is right, maybe if setuid()
343   * is going to work then setgroups() should also work.
344   */
345  if (setgroups (0, NULL) < 0)
346    _dbus_warn ("Failed to drop supplementary groups: %s\n",
347                _dbus_strerror (errno));
348
349  /* Set GID first, or the setuid may remove our permission
350   * to change the GID
351   */
352  if (setgid (gid) < 0)
353    {
354      dbus_set_error (error, _dbus_error_from_errno (errno),
355                      "Failed to set GID to %lu: %s", gid,
356                      _dbus_strerror (errno));
357      return FALSE;
358    }
359
360  if (setuid (uid) < 0)
361    {
362      dbus_set_error (error, _dbus_error_from_errno (errno),
363                      "Failed to set UID to %lu: %s", uid,
364                      _dbus_strerror (errno));
365      return FALSE;
366    }
367
368  return TRUE;
369}
370#endif /* !HAVE_LIBAUDIT */
371
372void
373_dbus_init_system_log (void)
374{
375  openlog ("dbus", LOG_PID, LOG_DAEMON);
376}
377/**
378 * Log a message to the system log file (e.g. syslog on Unix).
379 *
380 * @param severity a severity value
381 * @param msg a printf-style format string
382 * @param args arguments for the format string
383 *
384 */
385void
386_dbus_system_log (DBusSystemLogSeverity severity, const char *msg, ...)
387{
388  va_list args;
389
390  va_start (args, msg);
391
392  _dbus_system_logv (severity, msg, args);
393
394  va_end (args);
395}
396
397/**
398 * Log a message to the system log file (e.g. syslog on Unix).
399 *
400 * @param severity a severity value
401 * @param msg a printf-style format string
402 * @param args arguments for the format string
403 *
404 * If the FATAL severity is given, this function will terminate the program
405 * with an error code.
406 */
407void
408_dbus_system_logv (DBusSystemLogSeverity severity, const char *msg, va_list args)
409{
410  int flags;
411  switch (severity)
412    {
413      case DBUS_SYSTEM_LOG_INFO:
414        flags =  LOG_DAEMON | LOG_NOTICE;
415        break;
416      case DBUS_SYSTEM_LOG_SECURITY:
417        flags = LOG_AUTH | LOG_NOTICE;
418        break;
419      case DBUS_SYSTEM_LOG_FATAL:
420        flags = LOG_DAEMON|LOG_CRIT;
421      default:
422        return;
423    }
424
425  vsyslog (flags, msg, args);
426
427  if (severity == DBUS_SYSTEM_LOG_FATAL)
428    exit (1);
429}
430
431/** Installs a UNIX signal handler
432 *
433 * @param sig the signal to handle
434 * @param handler the handler
435 */
436void
437_dbus_set_signal_handler (int               sig,
438                          DBusSignalHandler handler)
439{
440  struct sigaction act;
441  sigset_t empty_mask;
442
443  sigemptyset (&empty_mask);
444  act.sa_handler = handler;
445  act.sa_mask    = empty_mask;
446  act.sa_flags   = 0;
447  sigaction (sig,  &act, NULL);
448}
449
450/** Checks if a file exists
451*
452* @param file full path to the file
453* @returns #TRUE if file exists
454*/
455dbus_bool_t
456_dbus_file_exists (const char *file)
457{
458  return (access (file, F_OK) == 0);
459}
460
461/** Checks if user is at the console
462*
463* @param username user to check
464* @param error return location for errors
465* @returns #TRUE is the user is at the consolei and there are no errors
466*/
467dbus_bool_t
468_dbus_user_at_console (const char *username,
469                       DBusError  *error)
470{
471
472  DBusString f;
473  dbus_bool_t result;
474
475  result = FALSE;
476  if (!_dbus_string_init (&f))
477    {
478      _DBUS_SET_OOM (error);
479      return FALSE;
480    }
481
482  if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
483    {
484      _DBUS_SET_OOM (error);
485      goto out;
486    }
487
488
489  if (!_dbus_string_append (&f, username))
490    {
491      _DBUS_SET_OOM (error);
492      goto out;
493    }
494
495  result = _dbus_file_exists (_dbus_string_get_const_data (&f));
496
497 out:
498  _dbus_string_free (&f);
499
500  return result;
501}
502
503
504/**
505 * Checks whether the filename is an absolute path
506 *
507 * @param filename the filename
508 * @returns #TRUE if an absolute path
509 */
510dbus_bool_t
511_dbus_path_is_absolute (const DBusString *filename)
512{
513  if (_dbus_string_get_length (filename) > 0)
514    return _dbus_string_get_byte (filename, 0) == '/';
515  else
516    return FALSE;
517}
518
519/**
520 * stat() wrapper.
521 *
522 * @param filename the filename to stat
523 * @param statbuf the stat info to fill in
524 * @param error return location for error
525 * @returns #FALSE if error was set
526 */
527dbus_bool_t
528_dbus_stat (const DBusString *filename,
529            DBusStat         *statbuf,
530            DBusError        *error)
531{
532  const char *filename_c;
533  struct stat sb;
534
535  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
536
537  filename_c = _dbus_string_get_const_data (filename);
538
539  if (stat (filename_c, &sb) < 0)
540    {
541      dbus_set_error (error, _dbus_error_from_errno (errno),
542                      "%s", _dbus_strerror (errno));
543      return FALSE;
544    }
545
546  statbuf->mode = sb.st_mode;
547  statbuf->nlink = sb.st_nlink;
548  statbuf->uid = sb.st_uid;
549  statbuf->gid = sb.st_gid;
550  statbuf->size = sb.st_size;
551  statbuf->atime = sb.st_atime;
552  statbuf->mtime = sb.st_mtime;
553  statbuf->ctime = sb.st_ctime;
554
555  return TRUE;
556}
557
558
559/**
560 * Internals of directory iterator
561 */
562struct DBusDirIter
563{
564  DIR *d; /**< The DIR* from opendir() */
565
566};
567
568/**
569 * Open a directory to iterate over.
570 *
571 * @param filename the directory name
572 * @param error exception return object or #NULL
573 * @returns new iterator, or #NULL on error
574 */
575DBusDirIter*
576_dbus_directory_open (const DBusString *filename,
577                      DBusError        *error)
578{
579  DIR *d;
580  DBusDirIter *iter;
581  const char *filename_c;
582
583  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
584
585  filename_c = _dbus_string_get_const_data (filename);
586
587  d = opendir (filename_c);
588  if (d == NULL)
589    {
590      dbus_set_error (error, _dbus_error_from_errno (errno),
591                      "Failed to read directory \"%s\": %s",
592                      filename_c,
593                      _dbus_strerror (errno));
594      return NULL;
595    }
596  iter = dbus_new0 (DBusDirIter, 1);
597  if (iter == NULL)
598    {
599      closedir (d);
600      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
601                      "Could not allocate memory for directory iterator");
602      return NULL;
603    }
604
605  iter->d = d;
606
607  return iter;
608}
609
610/* it is never safe to retrun a size smaller than sizeof(struct dirent)
611 * because the libc *could* try to access the  whole structure
612 * (for instance it could try to memset it).
613 * it is also incorrect to return a size bigger than that, because
614 * the libc would never use it.
615 * The only correct and safe value this function can ever return is
616 * sizeof(struct dirent).
617 */
618static dbus_bool_t
619dirent_buf_size(DIR * dirp, size_t *size)
620{
621  *size = sizeof(struct dirent);
622  return TRUE;
623}
624
625/**
626 * Get next file in the directory. Will not return "." or ".."  on
627 * UNIX. If an error occurs, the contents of "filename" are
628 * undefined. The error is never set if the function succeeds.
629 *
630 * @param iter the iterator
631 * @param filename string to be set to the next file in the dir
632 * @param error return location for error
633 * @returns #TRUE if filename was filled in with a new filename
634 */
635dbus_bool_t
636_dbus_directory_get_next_file (DBusDirIter      *iter,
637                               DBusString       *filename,
638                               DBusError        *error)
639{
640  struct dirent *d, *ent;
641  size_t buf_size;
642  int err;
643
644  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
645
646  if (!dirent_buf_size (iter->d, &buf_size))
647    {
648      dbus_set_error (error, DBUS_ERROR_FAILED,
649                      "Can't calculate buffer size when reading directory");
650      return FALSE;
651    }
652
653  d = (struct dirent *)dbus_malloc (buf_size);
654  if (!d)
655    {
656      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
657                      "No memory to read directory entry");
658      return FALSE;
659    }
660
661 again:
662  err = readdir_r (iter->d, d, &ent);
663  if (err || !ent)
664    {
665      if (err != 0)
666        dbus_set_error (error,
667                        _dbus_error_from_errno (err),
668                        "%s", _dbus_strerror (err));
669
670      dbus_free (d);
671      return FALSE;
672    }
673  else if (ent->d_name[0] == '.' &&
674           (ent->d_name[1] == '\0' ||
675            (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
676    goto again;
677  else
678    {
679      _dbus_string_set_length (filename, 0);
680      if (!_dbus_string_append (filename, ent->d_name))
681        {
682          dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
683                          "No memory to read directory entry");
684          dbus_free (d);
685          return FALSE;
686        }
687      else
688        {
689          dbus_free (d);
690          return TRUE;
691        }
692    }
693}
694
695/**
696 * Closes a directory iteration.
697 */
698void
699_dbus_directory_close (DBusDirIter *iter)
700{
701  closedir (iter->d);
702  dbus_free (iter);
703}
704
705static dbus_bool_t
706fill_user_info_from_group (struct group  *g,
707                           DBusGroupInfo *info,
708                           DBusError     *error)
709{
710  _dbus_assert (g->gr_name != NULL);
711
712  info->gid = g->gr_gid;
713  info->groupname = _dbus_strdup (g->gr_name);
714
715  /* info->members = dbus_strdupv (g->gr_mem) */
716
717  if (info->groupname == NULL)
718    {
719      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
720      return FALSE;
721    }
722
723  return TRUE;
724}
725
726static dbus_bool_t
727fill_group_info (DBusGroupInfo    *info,
728                 dbus_gid_t        gid,
729                 const DBusString *groupname,
730                 DBusError        *error)
731{
732  const char *group_c_str;
733
734  _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
735  _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
736
737  if (groupname)
738    group_c_str = _dbus_string_get_const_data (groupname);
739  else
740    group_c_str = NULL;
741
742  /* For now assuming that the getgrnam() and getgrgid() flavors
743   * always correspond to the pwnam flavors, if not we have
744   * to add more configure checks.
745   */
746
747#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
748  {
749    struct group *g;
750    int result;
751    size_t buflen;
752    char *buf;
753    struct group g_str;
754    dbus_bool_t b;
755
756    /* retrieve maximum needed size for buf */
757    buflen = sysconf (_SC_GETGR_R_SIZE_MAX);
758
759    /* sysconf actually returns a long, but everything else expects size_t,
760     * so just recast here.
761     * https://bugs.freedesktop.org/show_bug.cgi?id=17061
762     */
763    if ((long) buflen <= 0)
764      buflen = 1024;
765
766    result = -1;
767    while (1)
768      {
769        buf = dbus_malloc (buflen);
770        if (buf == NULL)
771          {
772            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
773            return FALSE;
774          }
775
776        g = NULL;
777#ifdef HAVE_POSIX_GETPWNAM_R
778        if (group_c_str)
779          result = getgrnam_r (group_c_str, &g_str, buf, buflen,
780                               &g);
781        else
782          result = getgrgid_r (gid, &g_str, buf, buflen,
783                               &g);
784#else
785        g = getgrnam_r (group_c_str, &g_str, buf, buflen);
786        result = 0;
787#endif /* !HAVE_POSIX_GETPWNAM_R */
788        /* Try a bigger buffer if ERANGE was returned:
789           https://bugs.freedesktop.org/show_bug.cgi?id=16727
790        */
791        if (result == ERANGE && buflen < 512 * 1024)
792          {
793            dbus_free (buf);
794            buflen *= 2;
795          }
796        else
797          {
798            break;
799          }
800      }
801
802    if (result == 0 && g == &g_str)
803      {
804        b = fill_user_info_from_group (g, info, error);
805        dbus_free (buf);
806        return b;
807      }
808    else
809      {
810        dbus_set_error (error, _dbus_error_from_errno (errno),
811                        "Group %s unknown or failed to look it up\n",
812                        group_c_str ? group_c_str : "???");
813        dbus_free (buf);
814        return FALSE;
815      }
816  }
817#else /* ! HAVE_GETPWNAM_R */
818  {
819    /* I guess we're screwed on thread safety here */
820    struct group *g;
821
822    g = getgrnam (group_c_str);
823
824    if (g != NULL)
825      {
826        return fill_user_info_from_group (g, info, error);
827      }
828    else
829      {
830        dbus_set_error (error, _dbus_error_from_errno (errno),
831                        "Group %s unknown or failed to look it up\n",
832                        group_c_str ? group_c_str : "???");
833        return FALSE;
834      }
835  }
836#endif  /* ! HAVE_GETPWNAM_R */
837}
838
839/**
840 * Initializes the given DBusGroupInfo struct
841 * with information about the given group name.
842 *
843 * @param info the group info struct
844 * @param groupname name of group
845 * @param error the error return
846 * @returns #FALSE if error is set
847 */
848dbus_bool_t
849_dbus_group_info_fill (DBusGroupInfo    *info,
850                       const DBusString *groupname,
851                       DBusError        *error)
852{
853  return fill_group_info (info, DBUS_GID_UNSET,
854                          groupname, error);
855
856}
857
858/**
859 * Initializes the given DBusGroupInfo struct
860 * with information about the given group ID.
861 *
862 * @param info the group info struct
863 * @param gid group ID
864 * @param error the error return
865 * @returns #FALSE if error is set
866 */
867dbus_bool_t
868_dbus_group_info_fill_gid (DBusGroupInfo *info,
869                           dbus_gid_t     gid,
870                           DBusError     *error)
871{
872  return fill_group_info (info, gid, NULL, error);
873}
874
875/**
876 * Parse a UNIX user from the bus config file. On Windows, this should
877 * simply always fail (just return #FALSE).
878 *
879 * @param username the username text
880 * @param uid_p place to return the uid
881 * @returns #TRUE on success
882 */
883dbus_bool_t
884_dbus_parse_unix_user_from_config (const DBusString  *username,
885                                   dbus_uid_t        *uid_p)
886{
887  return _dbus_get_user_id (username, uid_p);
888
889}
890
891/**
892 * Parse a UNIX group from the bus config file. On Windows, this should
893 * simply always fail (just return #FALSE).
894 *
895 * @param groupname the groupname text
896 * @param gid_p place to return the gid
897 * @returns #TRUE on success
898 */
899dbus_bool_t
900_dbus_parse_unix_group_from_config (const DBusString  *groupname,
901                                    dbus_gid_t        *gid_p)
902{
903  return _dbus_get_group_id (groupname, gid_p);
904}
905
906/**
907 * Gets all groups corresponding to the given UNIX user ID. On UNIX,
908 * just calls _dbus_groups_from_uid(). On Windows, should always
909 * fail since we don't know any UNIX groups.
910 *
911 * @param uid the UID
912 * @param group_ids return location for array of group IDs
913 * @param n_group_ids return location for length of returned array
914 * @returns #TRUE if the UID existed and we got some credentials
915 */
916dbus_bool_t
917_dbus_unix_groups_from_uid (dbus_uid_t            uid,
918                            dbus_gid_t          **group_ids,
919                            int                  *n_group_ids)
920{
921  return _dbus_groups_from_uid (uid, group_ids, n_group_ids);
922}
923
924/**
925 * Checks to see if the UNIX user ID is at the console.
926 * Should always fail on Windows (set the error to
927 * #DBUS_ERROR_NOT_SUPPORTED).
928 *
929 * @param uid UID of person to check
930 * @param error return location for errors
931 * @returns #TRUE if the UID is the same as the console user and there are no errors
932 */
933dbus_bool_t
934_dbus_unix_user_is_at_console (dbus_uid_t         uid,
935                               DBusError         *error)
936{
937  return _dbus_is_console_user (uid, error);
938
939}
940
941/**
942 * Checks to see if the UNIX user ID matches the UID of
943 * the process. Should always return #FALSE on Windows.
944 *
945 * @param uid the UNIX user ID
946 * @returns #TRUE if this uid owns the process.
947 */
948dbus_bool_t
949_dbus_unix_user_is_process_owner (dbus_uid_t uid)
950{
951  return uid == _dbus_geteuid ();
952}
953
954/**
955 * Checks to see if the Windows user SID matches the owner of
956 * the process. Should always return #FALSE on UNIX.
957 *
958 * @param windows_sid the Windows user SID
959 * @returns #TRUE if this user owns the process.
960 */
961dbus_bool_t
962_dbus_windows_user_is_process_owner (const char *windows_sid)
963{
964  return FALSE;
965}
966
967/** @} */ /* End of DBusInternalsUtils functions */
968
969/**
970 * @addtogroup DBusString
971 *
972 * @{
973 */
974/**
975 * Get the directory name from a complete filename
976 * @param filename the filename
977 * @param dirname string to append directory name to
978 * @returns #FALSE if no memory
979 */
980dbus_bool_t
981_dbus_string_get_dirname  (const DBusString *filename,
982                           DBusString       *dirname)
983{
984  int sep;
985
986  _dbus_assert (filename != dirname);
987  _dbus_assert (filename != NULL);
988  _dbus_assert (dirname != NULL);
989
990  /* Ignore any separators on the end */
991  sep = _dbus_string_get_length (filename);
992  if (sep == 0)
993    return _dbus_string_append (dirname, "."); /* empty string passed in */
994
995  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
996    --sep;
997
998  _dbus_assert (sep >= 0);
999
1000  if (sep == 0)
1001    return _dbus_string_append (dirname, "/");
1002
1003  /* Now find the previous separator */
1004  _dbus_string_find_byte_backward (filename, sep, '/', &sep);
1005  if (sep < 0)
1006    return _dbus_string_append (dirname, ".");
1007
1008  /* skip multiple separators */
1009  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
1010    --sep;
1011
1012  _dbus_assert (sep >= 0);
1013
1014  if (sep == 0 &&
1015      _dbus_string_get_byte (filename, 0) == '/')
1016    return _dbus_string_append (dirname, "/");
1017  else
1018    return _dbus_string_copy_len (filename, 0, sep - 0,
1019                                  dirname, _dbus_string_get_length (dirname));
1020}
1021/** @} */ /* DBusString stuff */
1022
1023static void
1024string_squash_nonprintable (DBusString *str)
1025{
1026  unsigned char *buf;
1027  int i, len;
1028
1029  buf = _dbus_string_get_data (str);
1030  len = _dbus_string_get_length (str);
1031
1032  for (i = 0; i < len; i++)
1033    {
1034	  unsigned char c = (unsigned char) buf[i];
1035      if (c == '\0')
1036        c = ' ';
1037      else if (c < 0x20 || c > 127)
1038        c = '?';
1039    }
1040}
1041
1042/**
1043 * Get a printable string describing the command used to execute
1044 * the process with pid.  This string should only be used for
1045 * informative purposes such as logging; it may not be trusted.
1046 *
1047 * The command is guaranteed to be printable ASCII and no longer
1048 * than max_len.
1049 *
1050 * @param pid Process id
1051 * @param str Append command to this string
1052 * @param max_len Maximum length of returned command
1053 * @param error return location for errors
1054 * @returns #FALSE on error
1055 */
1056dbus_bool_t
1057_dbus_command_for_pid (unsigned long  pid,
1058                       DBusString    *str,
1059                       int            max_len,
1060                       DBusError     *error)
1061{
1062  /* This is all Linux-specific for now */
1063  DBusString path;
1064  DBusString cmdline;
1065  int fd;
1066
1067  if (!_dbus_string_init (&path))
1068    {
1069      _DBUS_SET_OOM (error);
1070      return FALSE;
1071    }
1072
1073  if (!_dbus_string_init (&cmdline))
1074    {
1075      _DBUS_SET_OOM (error);
1076      _dbus_string_free (&path);
1077      return FALSE;
1078    }
1079
1080  if (!_dbus_string_append_printf (&path, "/proc/%ld/cmdline", pid))
1081    goto oom;
1082
1083  fd = open (_dbus_string_get_const_data (&path), O_RDONLY);
1084  if (fd < 0)
1085    {
1086      dbus_set_error (error,
1087                      _dbus_error_from_errno (errno),
1088                      "Failed to open \"%s\": %s",
1089                      _dbus_string_get_const_data (&path),
1090                      _dbus_strerror (errno));
1091      goto fail;
1092    }
1093
1094  if (!_dbus_read (fd, &cmdline, max_len))
1095    {
1096      dbus_set_error (error,
1097                      _dbus_error_from_errno (errno),
1098                      "Failed to read from \"%s\": %s",
1099                      _dbus_string_get_const_data (&path),
1100                      _dbus_strerror (errno));
1101      goto fail;
1102    }
1103
1104  if (!_dbus_close (fd, error))
1105    goto fail;
1106
1107  string_squash_nonprintable (&cmdline);
1108
1109  if (!_dbus_string_copy (&cmdline, 0, str, _dbus_string_get_length (str)))
1110    goto oom;
1111
1112  _dbus_string_free (&cmdline);
1113  _dbus_string_free (&path);
1114  return TRUE;
1115oom:
1116  _DBUS_SET_OOM (error);
1117fail:
1118  _dbus_string_free (&cmdline);
1119  _dbus_string_free (&path);
1120  return FALSE;
1121}
1122