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