chiark / gitweb /
88b0ef9e4503448565a40012cef1cb80e70ce89e
[elogind.git] / src / login / pam-module.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sys/file.h>
25 #include <pwd.h>
26 #include <endian.h>
27 #include <sys/capability.h>
28
29 #include <security/pam_modules.h>
30 #include <security/_pam_macros.h>
31 #include <security/pam_modutil.h>
32 #include <security/pam_ext.h>
33 #include <security/pam_misc.h>
34
35 #include <systemd/sd-daemon.h>
36
37 #include "util.h"
38 #include "audit.h"
39 #include "macro.h"
40 #include "strv.h"
41 #include "dbus-common.h"
42 #include "def.h"
43 #include "socket-util.h"
44
45 static int parse_argv(pam_handle_t *handle,
46                       int argc, const char **argv,
47                       char ***controllers,
48                       char ***reset_controllers,
49                       bool *kill_processes,
50                       char ***kill_only_users,
51                       char ***kill_exclude_users,
52                       const char **class,
53                       bool *debug) {
54
55         unsigned i;
56
57         assert(argc >= 0);
58         assert(argc == 0 || argv);
59
60         for (i = 0; i < (unsigned) argc; i++) {
61                 int k;
62
63                 if (startswith(argv[i], "kill-session-processes=")) {
64                         if ((k = parse_boolean(argv[i] + 23)) < 0) {
65                                 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session-processes= argument.");
66                                 return k;
67                         }
68
69                         if (kill_processes)
70                                 *kill_processes = k;
71
72                 } else if (startswith(argv[i], "kill-session=")) {
73                         /* As compatibility for old versions */
74
75                         if ((k = parse_boolean(argv[i] + 13)) < 0) {
76                                 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
77                                 return k;
78                         }
79
80                         if (kill_processes)
81                                 *kill_processes = k;
82
83                 } else if (startswith(argv[i], "controllers=")) {
84
85                         if (controllers) {
86                                 char **l;
87
88                                 if (!(l = strv_split(argv[i] + 12, ","))) {
89                                         pam_syslog(handle, LOG_ERR, "Out of memory.");
90                                         return -ENOMEM;
91                                 }
92
93                                 strv_free(*controllers);
94                                 *controllers = l;
95                         }
96
97                 } else if (startswith(argv[i], "reset-controllers=")) {
98
99                         if (reset_controllers) {
100                                 char **l;
101
102                                 if (!(l = strv_split(argv[i] + 18, ","))) {
103                                         pam_syslog(handle, LOG_ERR, "Out of memory.");
104                                         return -ENOMEM;
105                                 }
106
107                                 strv_free(*reset_controllers);
108                                 *reset_controllers = l;
109                         }
110
111                 } else if (startswith(argv[i], "kill-only-users=")) {
112
113                         if (kill_only_users) {
114                                 char **l;
115
116                                 if (!(l = strv_split(argv[i] + 16, ","))) {
117                                         pam_syslog(handle, LOG_ERR, "Out of memory.");
118                                         return -ENOMEM;
119                                 }
120
121                                 strv_free(*kill_only_users);
122                                 *kill_only_users = l;
123                         }
124
125                 } else if (startswith(argv[i], "kill-exclude-users=")) {
126
127                         if (kill_exclude_users) {
128                                 char **l;
129
130                                 if (!(l = strv_split(argv[i] + 19, ","))) {
131                                         pam_syslog(handle, LOG_ERR, "Out of memory.");
132                                         return -ENOMEM;
133                                 }
134
135                                 strv_free(*kill_exclude_users);
136                                 *kill_exclude_users = l;
137                         }
138
139                 } else if (startswith(argv[i], "class=")) {
140
141                         if (class)
142                                 *class = argv[i] + 6;
143
144                 } else if (startswith(argv[i], "debug=")) {
145                         if ((k = parse_boolean(argv[i] + 6)) < 0) {
146                                 pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument.");
147                                 return k;
148                         }
149
150                         if (debug)
151                                 *debug = k;
152
153                 } else if (startswith(argv[i], "create-session=") ||
154                            startswith(argv[i], "kill-user=")) {
155
156                         pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
157
158                 } else {
159                         pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
160                         return -EINVAL;
161                 }
162         }
163
164         return 0;
165 }
166
167 static int get_user_data(
168                 pam_handle_t *handle,
169                 const char **ret_username,
170                 struct passwd **ret_pw) {
171
172         const char *username = NULL;
173         struct passwd *pw = NULL;
174         uid_t uid;
175         int r;
176
177         assert(handle);
178         assert(ret_username);
179         assert(ret_pw);
180
181         r = audit_loginuid_from_pid(0, &uid);
182         if (r >= 0)
183                 pw = pam_modutil_getpwuid(handle, uid);
184         else {
185                 r = pam_get_user(handle, &username, NULL);
186                 if (r != PAM_SUCCESS) {
187                         pam_syslog(handle, LOG_ERR, "Failed to get user name.");
188                         return r;
189                 }
190
191                 if (isempty(username)) {
192                         pam_syslog(handle, LOG_ERR, "User name not valid.");
193                         return PAM_AUTH_ERR;
194                 }
195
196                 pw = pam_modutil_getpwnam(handle, username);
197         }
198
199         if (!pw) {
200                 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
201                 return PAM_USER_UNKNOWN;
202         }
203
204         *ret_pw = pw;
205         *ret_username = username ? username : pw->pw_name;
206
207         return PAM_SUCCESS;
208 }
209
210 static bool check_user_lists(
211                 pam_handle_t *handle,
212                 uid_t uid,
213                 char **kill_only_users,
214                 char **kill_exclude_users) {
215
216         const char *name = NULL;
217         char **l;
218
219         assert(handle);
220
221         if (uid == 0)
222                 name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
223         else {
224                 struct passwd *pw;
225
226                 pw = pam_modutil_getpwuid(handle, uid);
227                 if (pw)
228                         name = pw->pw_name;
229         }
230
231         STRV_FOREACH(l, kill_exclude_users) {
232                 uid_t u;
233
234                 if (parse_uid(*l, &u) >= 0)
235                         if (u == uid)
236                                 return false;
237
238                 if (name && streq(name, *l))
239                         return false;
240         }
241
242         if (strv_isempty(kill_only_users))
243                 return true;
244
245         STRV_FOREACH(l, kill_only_users) {
246                 uid_t u;
247
248                 if (parse_uid(*l, &u) >= 0)
249                         if (u == uid)
250                                 return true;
251
252                 if (name && streq(name, *l))
253                         return true;
254         }
255
256         return false;
257 }
258
259 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
260         char *p = NULL;
261         int r;
262         int fd;
263         union sockaddr_union sa;
264         struct ucred ucred;
265         socklen_t l;
266         char *tty;
267         int v;
268
269         assert(display);
270         assert(vtnr);
271
272         /* We deduce the X11 socket from the display name, then use
273          * SO_PEERCRED to determine the X11 server process, ask for
274          * the controlling tty of that and if it's a VC then we know
275          * the seat and the virtual terminal. Sounds ugly, is only
276          * semi-ugly. */
277
278         r = socket_from_display(display, &p);
279         if (r < 0)
280                 return r;
281
282         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
283         if (fd < 0) {
284                 free(p);
285                 return -errno;
286         }
287
288         zero(sa);
289         sa.un.sun_family = AF_UNIX;
290         strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
291         free(p);
292
293         if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
294                 close_nointr_nofail(fd);
295                 return -errno;
296         }
297
298         l = sizeof(ucred);
299         r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
300         close_nointr_nofail(fd);
301
302         if (r < 0)
303                 return -errno;
304
305         r = get_ctty(ucred.pid, NULL, &tty);
306         if (r < 0)
307                 return r;
308
309         v = vtnr_from_tty(tty);
310         free(tty);
311
312         if (v < 0)
313                 return v;
314         else if (v == 0)
315                 return -ENOENT;
316
317         if (seat)
318                 *seat = "seat0";
319         *vtnr = (uint32_t) v;
320
321         return 0;
322 }
323
324 _public_ PAM_EXTERN int pam_sm_open_session(
325                 pam_handle_t *handle,
326                 int flags,
327                 int argc, const char **argv) {
328
329         struct passwd *pw;
330         bool kill_processes = false, debug = false;
331         const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, *class_pam = NULL, *cvtnr = NULL;
332         char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
333         DBusError error;
334         uint32_t uid, pid;
335         DBusMessageIter iter;
336         dbus_bool_t kp;
337         int session_fd = -1;
338         DBusConnection *bus = NULL;
339         DBusMessage *m = NULL, *reply = NULL;
340         dbus_bool_t remote, existing;
341         int r;
342         uint32_t vtnr = 0;
343
344         assert(handle);
345
346         dbus_error_init(&error);
347
348         /* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
349
350         /* Make this a NOP on non-systemd systems */
351         if (sd_booted() <= 0)
352                 return PAM_SUCCESS;
353
354         if (parse_argv(handle,
355                        argc, argv,
356                        &controllers, &reset_controllers,
357                        &kill_processes, &kill_only_users, &kill_exclude_users,
358                        &class_pam, &debug) < 0) {
359                 r = PAM_SESSION_ERR;
360                 goto finish;
361         }
362
363         r = get_user_data(handle, &username, &pw);
364         if (r != PAM_SUCCESS)
365                 goto finish;
366
367         /* Make sure we don't enter a loop by talking to
368          * systemd-logind when it is actually waiting for the
369          * background to finish start-up. If the service is
370          * "systemd-shared" we simply set XDG_RUNTIME_DIR and
371          * leave. */
372
373         pam_get_item(handle, PAM_SERVICE, (const void**) &service);
374         if (streq_ptr(service, "systemd-shared")) {
375                 char *p, *rt = NULL;
376
377                 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) {
378                         r = PAM_BUF_ERR;
379                         goto finish;
380                 }
381
382                 r = parse_env_file(p, NEWLINE,
383                                    "RUNTIME", &rt,
384                                    NULL);
385                 free(p);
386
387                 if (r < 0 && r != -ENOENT) {
388                         r = PAM_SESSION_ERR;
389                         free(rt);
390                         goto finish;
391                 }
392
393                 if (rt)  {
394                         r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
395                         free(rt);
396
397                         if (r != PAM_SUCCESS) {
398                                 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
399                                 goto finish;
400                         }
401                 }
402
403                 r = PAM_SUCCESS;
404                 goto finish;
405         }
406
407         if (kill_processes)
408                 kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
409
410         dbus_connection_set_change_sigpipe(FALSE);
411
412         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
413         if (!bus) {
414                 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
415                 r = PAM_SESSION_ERR;
416                 goto finish;
417         }
418
419         m = dbus_message_new_method_call(
420                         "org.freedesktop.login1",
421                         "/org/freedesktop/login1",
422                         "org.freedesktop.login1.Manager",
423                         "CreateSession");
424         if (!m) {
425                 pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
426                 r = PAM_BUF_ERR;
427                 goto finish;
428         }
429
430         uid = pw->pw_uid;
431         pid = getpid();
432
433         pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
434         pam_get_item(handle, PAM_TTY, (const void**) &tty);
435         pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
436         pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
437
438         seat = pam_getenv(handle, "XDG_SEAT");
439         if (isempty(seat))
440                 seat = getenv("XDG_SEAT");
441
442         cvtnr = pam_getenv(handle, "XDG_VTNR");
443         if (isempty(cvtnr))
444                 cvtnr = getenv("XDG_VTNR");
445
446         service = strempty(service);
447         tty = strempty(tty);
448         display = strempty(display);
449         remote_user = strempty(remote_user);
450         remote_host = strempty(remote_host);
451         seat = strempty(seat);
452
453         if (strchr(tty, ':')) {
454                 /* A tty with a colon is usually an X11 display, place
455                  * there to show up in utmp. We rearrange things and
456                  * don't pretend that an X display was a tty */
457
458                 if (isempty(display))
459                         display = tty;
460                 tty = "";
461         } else if (streq(tty, "cron")) {
462                 /* cron has been setting PAM_TTY to "cron" for a very
463                  * long time and it probably shouldn't stop doing that
464                  * for compatibility reasons. */
465                 tty = "";
466                 type = "unspecified";
467         } else if (streq(tty, "ssh")) {
468                 /* ssh has been setting PAM_TTY to "ssh" for a very
469                  * long time and probably shouldn't stop doing that
470                  * for compatibility reasons. */
471                 tty = "";
472                 type ="tty";
473         }
474
475         /* If this fails vtnr will be 0, that's intended */
476         if (!isempty(cvtnr))
477                 safe_atou32(cvtnr, &vtnr);
478
479         if (!isempty(display) && vtnr <= 0) {
480                 if (isempty(seat))
481                         get_seat_from_display(display, &seat, &vtnr);
482                 else if (streq(seat, "seat0"))
483                         get_seat_from_display(display, NULL, &vtnr);
484         }
485
486         if (!type)
487                 type = !isempty(display) ? "x11" :
488                         !isempty(tty) ? "tty" : "unspecified";
489
490         class = pam_getenv(handle, "XDG_SESSION_CLASS");
491         if (isempty(class))
492                 class = getenv("XDG_SESSION_CLASS");
493         if (isempty(class))
494                 class = class_pam;
495         if (isempty(class))
496                 class = "user";
497
498         remote = !isempty(remote_host) &&
499                 !streq(remote_host, "localhost") &&
500                 !streq(remote_host, "localhost.localdomain");
501
502         if (!dbus_message_append_args(m,
503                                       DBUS_TYPE_UINT32, &uid,
504                                       DBUS_TYPE_UINT32, &pid,
505                                       DBUS_TYPE_STRING, &service,
506                                       DBUS_TYPE_STRING, &type,
507                                       DBUS_TYPE_STRING, &class,
508                                       DBUS_TYPE_STRING, &seat,
509                                       DBUS_TYPE_UINT32, &vtnr,
510                                       DBUS_TYPE_STRING, &tty,
511                                       DBUS_TYPE_STRING, &display,
512                                       DBUS_TYPE_BOOLEAN, &remote,
513                                       DBUS_TYPE_STRING, &remote_user,
514                                       DBUS_TYPE_STRING, &remote_host,
515                                       DBUS_TYPE_INVALID)) {
516                 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
517                 r = PAM_BUF_ERR;
518                 goto finish;
519         }
520
521         dbus_message_iter_init_append(m, &iter);
522
523         r = bus_append_strv_iter(&iter, controllers);
524         if (r < 0) {
525                 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
526                 r = PAM_BUF_ERR;
527                 goto finish;
528         }
529
530         r = bus_append_strv_iter(&iter, reset_controllers);
531         if (r < 0) {
532                 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
533                 r = PAM_BUF_ERR;
534                 goto finish;
535         }
536
537         kp = kill_processes;
538         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
539                 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
540                 r = PAM_BUF_ERR;
541                 goto finish;
542         }
543
544         if (debug)
545                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
546                            "uid=%u pid=%u service=%s type=%s class=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
547                            uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
548
549         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
550         if (!reply) {
551                 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
552                 r = PAM_SESSION_ERR;
553                 goto finish;
554         }
555
556         if (!dbus_message_get_args(reply, &error,
557                                    DBUS_TYPE_STRING, &id,
558                                    DBUS_TYPE_OBJECT_PATH, &object_path,
559                                    DBUS_TYPE_STRING, &runtime_path,
560                                    DBUS_TYPE_UNIX_FD, &session_fd,
561                                    DBUS_TYPE_STRING, &seat,
562                                    DBUS_TYPE_UINT32, &vtnr,
563                                    DBUS_TYPE_BOOLEAN, &existing,
564                                    DBUS_TYPE_INVALID)) {
565                 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
566                 r = PAM_SESSION_ERR;
567                 goto finish;
568         }
569
570         if (debug)
571                 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
572                            "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
573                            id, object_path, runtime_path, session_fd, seat, vtnr);
574
575         r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
576         if (r != PAM_SUCCESS) {
577                 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
578                 goto finish;
579         }
580
581         r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
582         if (r != PAM_SUCCESS) {
583                 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
584                 goto finish;
585         }
586
587         if (!isempty(seat)) {
588                 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
589                 if (r != PAM_SUCCESS) {
590                         pam_syslog(handle, LOG_ERR, "Failed to set seat.");
591                         goto finish;
592                 }
593         }
594
595         if (vtnr > 0) {
596                 char buf[11];
597                 snprintf(buf, sizeof(buf), "%u", vtnr);
598                 char_array_0(buf);
599
600                 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
601                 if (r != PAM_SUCCESS) {
602                         pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
603                         goto finish;
604                 }
605         }
606
607         r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
608         if (r != PAM_SUCCESS) {
609                 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
610                 return r;
611         }
612
613         if (session_fd >= 0) {
614                 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
615                 if (r != PAM_SUCCESS) {
616                         pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
617                         return r;
618                 }
619         }
620
621         session_fd = -1;
622
623         r = PAM_SUCCESS;
624
625 finish:
626         strv_free(controllers);
627         strv_free(reset_controllers);
628         strv_free(kill_only_users);
629         strv_free(kill_exclude_users);
630
631         dbus_error_free(&error);
632
633         if (bus) {
634                 dbus_connection_close(bus);
635                 dbus_connection_unref(bus);
636         }
637
638         if (m)
639                 dbus_message_unref(m);
640
641         if (reply)
642                 dbus_message_unref(reply);
643
644         if (session_fd >= 0)
645                 close_nointr_nofail(session_fd);
646
647         return r;
648 }
649
650 _public_ PAM_EXTERN int pam_sm_close_session(
651                 pam_handle_t *handle,
652                 int flags,
653                 int argc, const char **argv) {
654
655         const void *p = NULL, *existing = NULL;
656         const char *id;
657         DBusConnection *bus = NULL;
658         DBusMessage *m = NULL, *reply = NULL;
659         DBusError error;
660         int r;
661
662         assert(handle);
663
664         dbus_error_init(&error);
665
666         /* Only release session if it wasn't pre-existing when we
667          * tried to create it */
668         pam_get_data(handle, "systemd.existing", &existing);
669
670         id = pam_getenv(handle, "XDG_SESSION_ID");
671         if (id && !existing) {
672
673                 /* Before we go and close the FIFO we need to tell
674                  * logind that this is a clean session shutdown, so
675                  * that it doesn't just go and slaughter us
676                  * immediately after closing the fd */
677
678                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
679                 if (!bus) {
680                         pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
681                         r = PAM_SESSION_ERR;
682                         goto finish;
683                 }
684
685                 m = dbus_message_new_method_call(
686                                 "org.freedesktop.login1",
687                                 "/org/freedesktop/login1",
688                                 "org.freedesktop.login1.Manager",
689                                 "ReleaseSession");
690                 if (!m) {
691                         pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
692                         r = PAM_BUF_ERR;
693                         goto finish;
694                 }
695
696                 if (!dbus_message_append_args(m,
697                                               DBUS_TYPE_STRING, &id,
698                                               DBUS_TYPE_INVALID)) {
699                         pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
700                         r = PAM_BUF_ERR;
701                         goto finish;
702                 }
703
704                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
705                 if (!reply) {
706                         pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
707                         r = PAM_SESSION_ERR;
708                         goto finish;
709                 }
710         }
711
712         r = PAM_SUCCESS;
713
714 finish:
715         pam_get_data(handle, "systemd.session-fd", &p);
716         if (p)
717                 close_nointr(PTR_TO_INT(p) - 1);
718
719         dbus_error_free(&error);
720
721         if (bus) {
722                 dbus_connection_close(bus);
723                 dbus_connection_unref(bus);
724         }
725
726         if (m)
727                 dbus_message_unref(m);
728
729         if (reply)
730                 dbus_message_unref(reply);
731
732         return r;
733 }