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