chiark / gitweb /
sd-bus: drop references to legacy /var/run D-Bus socket
[elogind.git] / src / login / pam_elogind.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <endian.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <pwd.h>
25 #include <security/_pam_macros.h>
26 #include <security/pam_ext.h>
27 #include <security/pam_misc.h>
28 #include <security/pam_modules.h>
29 #include <security/pam_modutil.h>
30 #include <sys/file.h>
31
32 #include "alloc-util.h"
33 #include "audit-util.h"
34 #include "bus-common-errors.h"
35 #include "bus-error.h"
36 #include "bus-util.h"
37 #include "def.h"
38 #include "fd-util.h"
39 #include "fileio.h"
40 #include "format-util.h"
41 #include "hostname-util.h"
42 #include "login-util.h"
43 #include "macro.h"
44 #include "parse-util.h"
45 #include "process-util.h"
46 #include "socket-util.h"
47 #include "strv.h"
48 #include "terminal-util.h"
49 #include "util.h"
50 #include "path-util.h"
51
52 static int parse_argv(
53                 pam_handle_t *handle,
54                 int argc, const char **argv,
55                 const char **class,
56                 const char **type,
57                 bool *debug) {
58
59         unsigned i;
60
61         assert(argc >= 0);
62         assert(argc == 0 || argv);
63
64         for (i = 0; i < (unsigned) argc; i++) {
65                 if (startswith(argv[i], "class=")) {
66                         if (class)
67                                 *class = argv[i] + 6;
68
69                 } else if (startswith(argv[i], "type=")) {
70                         if (type)
71                                 *type = argv[i] + 5;
72
73                 } else if (streq(argv[i], "debug")) {
74                         if (debug)
75                                 *debug = true;
76
77                 } else if (startswith(argv[i], "debug=")) {
78                         int k;
79
80                         k = parse_boolean(argv[i] + 6);
81                         if (k < 0)
82                                 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
83                         else if (debug)
84                                 *debug = k;
85
86                 } else
87                         pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
88         }
89
90         return 0;
91 }
92
93 static int get_user_data(
94                 pam_handle_t *handle,
95                 const char **ret_username,
96                 struct passwd **ret_pw) {
97
98         const char *username = NULL;
99         struct passwd *pw = NULL;
100         int r;
101
102         assert(handle);
103         assert(ret_username);
104         assert(ret_pw);
105
106         r = pam_get_user(handle, &username, NULL);
107         if (r != PAM_SUCCESS) {
108                 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
109                 return r;
110         }
111
112         if (isempty(username)) {
113                 pam_syslog(handle, LOG_ERR, "User name not valid.");
114                 return PAM_AUTH_ERR;
115         }
116
117         pw = pam_modutil_getpwnam(handle, username);
118         if (!pw) {
119                 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
120                 return PAM_USER_UNKNOWN;
121         }
122
123         *ret_pw = pw;
124         *ret_username = username;
125
126         return PAM_SUCCESS;
127 }
128
129 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
130         union sockaddr_union sa = {
131                 .un.sun_family = AF_UNIX,
132         };
133         _cleanup_free_ char *p = NULL, *tty = NULL;
134         _cleanup_close_ int fd = -1;
135         struct ucred ucred;
136         int v, r;
137
138         assert(display);
139         assert(vtnr);
140
141         /* We deduce the X11 socket from the display name, then use
142          * SO_PEERCRED to determine the X11 server process, ask for
143          * the controlling tty of that and if it's a VC then we know
144          * the seat and the virtual terminal. Sounds ugly, is only
145          * semi-ugly. */
146
147         r = socket_from_display(display, &p);
148         if (r < 0)
149                 return r;
150         strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
151
152         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
153         if (fd < 0)
154                 return -errno;
155
156         if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
157                 return -errno;
158
159         r = getpeercred(fd, &ucred);
160         if (r < 0)
161                 return r;
162
163         r = get_ctty(ucred.pid, NULL, &tty);
164         if (r < 0)
165                 return r;
166
167         v = vtnr_from_tty(tty);
168         if (v < 0)
169                 return v;
170         else if (v == 0)
171                 return -ENOENT;
172
173         if (seat)
174                 *seat = "seat0";
175         *vtnr = (uint32_t) v;
176
177         return 0;
178 }
179
180 static int export_legacy_dbus_address(
181                 pam_handle_t *handle,
182                 uid_t uid,
183                 const char *runtime) {
184
185         _cleanup_free_ char *s = NULL;
186         int r = PAM_BUF_ERR;
187
188         /* FIXME: We *really* should move the access() check into the
189          * daemons that spawn dbus-daemon, instead of forcing
190          * DBUS_SESSION_BUS_ADDRESS= here. */
191
192         s = strjoin(runtime, "/bus");
193         if (!s)
194                 goto error;
195
196         if (access(s, F_OK) < 0)
197                 return PAM_SUCCESS;
198
199         s = mfree(s);
200         if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
201                 goto error;
202
203         r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
204         if (r != PAM_SUCCESS)
205                 goto error;
206
207         return PAM_SUCCESS;
208
209 error:
210         pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
211         return r;
212 }
213
214 _public_ PAM_EXTERN int pam_sm_open_session(
215                 pam_handle_t *handle,
216                 int flags,
217                 int argc, const char **argv) {
218
219         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
220         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
221         const char
222                 *username, *id, *object_path, *runtime_path,
223                 *service = NULL,
224                 *tty = NULL, *display = NULL,
225                 *remote_user = NULL, *remote_host = NULL,
226                 *seat = NULL,
227                 *type = NULL, *class = NULL,
228                 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
229         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
230         int session_fd = -1, existing, r;
231         bool debug = false, remote;
232         struct passwd *pw;
233         uint32_t vtnr = 0;
234         uid_t original_uid;
235
236         assert(handle);
237
238 #if 0 /// with elogind, it is always a "logind system".
239         /* Make this a NOP on non-logind systems */
240         if (!logind_running())
241                 return PAM_SUCCESS;
242 #endif // 0
243
244         if (parse_argv(handle,
245                        argc, argv,
246                        &class_pam,
247                        &type_pam,
248                        &debug) < 0)
249                 return PAM_SESSION_ERR;
250
251         if (debug)
252 #if 0 /// This is pam-elogind, not pam-systemd
253                 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
254 #else
255                 pam_syslog(handle, LOG_DEBUG, "pam-elogind initializing");
256 #endif // 0
257
258         r = get_user_data(handle, &username, &pw);
259         if (r != PAM_SUCCESS) {
260                 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
261                 return r;
262         }
263
264         /* Make sure we don't enter a loop by talking to
265          * systemd-logind when it is actually waiting for the
266          * background to finish start-up. If the service is
267          * "systemd-user" we simply set XDG_RUNTIME_DIR and
268          * leave. */
269
270         pam_get_item(handle, PAM_SERVICE, (const void**) &service);
271 #if 0 /// Actually it is elogind-user with elogind.
272         if (streq_ptr(service, "systemd-user")) {
273 #else
274         if (streq_ptr(service, "elogind-user")) {
275 #endif // 0
276                 _cleanup_free_ char *rt = NULL;
277
278                 if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
279                         return PAM_BUF_ERR;
280
281                 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
282                 if (r != PAM_SUCCESS) {
283                         pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
284                         return r;
285                 }
286
287                 r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
288                 if (r != PAM_SUCCESS)
289                         return r;
290
291                 return PAM_SUCCESS;
292         }
293
294         /* Otherwise, we ask logind to create a session for us */
295
296         pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
297         pam_get_item(handle, PAM_TTY, (const void**) &tty);
298         pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
299         pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
300
301         seat = pam_getenv(handle, "XDG_SEAT");
302         if (isempty(seat))
303                 seat = getenv("XDG_SEAT");
304
305         cvtnr = pam_getenv(handle, "XDG_VTNR");
306         if (isempty(cvtnr))
307                 cvtnr = getenv("XDG_VTNR");
308
309         type = pam_getenv(handle, "XDG_SESSION_TYPE");
310         if (isempty(type))
311                 type = getenv("XDG_SESSION_TYPE");
312         if (isempty(type))
313                 type = type_pam;
314
315         class = pam_getenv(handle, "XDG_SESSION_CLASS");
316         if (isempty(class))
317                 class = getenv("XDG_SESSION_CLASS");
318         if (isempty(class))
319                 class = class_pam;
320
321         desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
322         if (isempty(desktop))
323                 desktop = getenv("XDG_SESSION_DESKTOP");
324
325         tty = strempty(tty);
326
327         if (strchr(tty, ':')) {
328                 /* A tty with a colon is usually an X11 display,
329                  * placed there to show up in utmp. We rearrange
330                  * things and don't pretend that an X display was a
331                  * tty. */
332
333                 if (isempty(display))
334                         display = tty;
335                 tty = NULL;
336         } else if (streq(tty, "cron")) {
337                 /* cron has been setting PAM_TTY to "cron" for a very
338                  * long time and it probably shouldn't stop doing that
339                  * for compatibility reasons. */
340                 type = "unspecified";
341                 class = "background";
342                 tty = NULL;
343         } else if (streq(tty, "ssh")) {
344                 /* ssh has been setting PAM_TTY to "ssh" for a very
345                  * long time and probably shouldn't stop doing that
346                  * for compatibility reasons. */
347                 type ="tty";
348                 class = "user";
349                 tty = NULL;
350         } else
351                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
352                 tty = skip_dev_prefix(tty);
353
354         /* If this fails vtnr will be 0, that's intended */
355         if (!isempty(cvtnr))
356                 (void) safe_atou32(cvtnr, &vtnr);
357
358         if (!isempty(display) && !vtnr) {
359                 if (isempty(seat))
360                         get_seat_from_display(display, &seat, &vtnr);
361                 else if (streq(seat, "seat0"))
362                         get_seat_from_display(display, NULL, &vtnr);
363         }
364
365         if (seat && !streq(seat, "seat0") && vtnr != 0) {
366                 pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
367                 vtnr = 0;
368         }
369
370         if (isempty(type))
371                 type = !isempty(display) ? "x11" :
372                            !isempty(tty) ? "tty" : "unspecified";
373
374         if (isempty(class))
375                 class = streq(type, "unspecified") ? "background" : "user";
376
377         remote = !isempty(remote_host) && !is_localhost(remote_host);
378
379         /* Talk to logind over the message bus */
380
381         r = sd_bus_open_system(&bus);
382         if (r < 0) {
383                 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
384                 return PAM_SESSION_ERR;
385         }
386
387         if (debug)
388                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
389                            "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
390                            pw->pw_uid, getpid_cached(),
391                            strempty(service),
392                            type, class, strempty(desktop),
393                            strempty(seat), vtnr, strempty(tty), strempty(display),
394                            yes_no(remote), strempty(remote_user), strempty(remote_host));
395
396         r = sd_bus_call_method(bus,
397                                "org.freedesktop.login1",
398                                "/org/freedesktop/login1",
399                                "org.freedesktop.login1.Manager",
400                                "CreateSession",
401                                &error,
402                                &reply,
403                                "uusssssussbssa(sv)",
404                                (uint32_t) pw->pw_uid,
405                                (uint32_t) getpid_cached(),
406                                service,
407                                type,
408                                class,
409                                desktop,
410                                seat,
411                                vtnr,
412                                tty,
413                                display,
414                                remote,
415                                remote_user,
416                                remote_host,
417                                0);
418         if (r < 0) {
419                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
420                         pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
421                         return PAM_SUCCESS;
422                 } else {
423                         pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
424                         return PAM_SYSTEM_ERR;
425                 }
426         }
427
428         r = sd_bus_message_read(reply,
429                                 "soshusub",
430                                 &id,
431                                 &object_path,
432                                 &runtime_path,
433                                 &session_fd,
434                                 &original_uid,
435                                 &seat,
436                                 &vtnr,
437                                 &existing);
438         if (r < 0) {
439                 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
440                 return PAM_SESSION_ERR;
441         }
442
443         if (debug)
444                 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
445                            "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
446                            id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
447
448         r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
449         if (r != PAM_SUCCESS) {
450                 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
451                 return r;
452         }
453
454         if (original_uid == pw->pw_uid) {
455                 /* Don't set $XDG_RUNTIME_DIR if the user we now
456                  * authenticated for does not match the original user
457                  * of the session. We do this in order not to result
458                  * in privileged apps clobbering the runtime directory
459                  * unnecessarily. */
460
461                 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
462                 if (r != PAM_SUCCESS) {
463                         pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
464                         return r;
465                 }
466
467                 r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
468                 if (r != PAM_SUCCESS)
469                         return r;
470         }
471
472         if (!isempty(seat)) {
473                 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
474                 if (r != PAM_SUCCESS) {
475                         pam_syslog(handle, LOG_ERR, "Failed to set seat.");
476                         return r;
477                 }
478         }
479
480         if (vtnr > 0) {
481                 char buf[DECIMAL_STR_MAX(vtnr)];
482                 sprintf(buf, "%u", vtnr);
483
484                 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
485                 if (r != PAM_SUCCESS) {
486                         pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
487                         return r;
488                 }
489         }
490
491         r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
492         if (r != PAM_SUCCESS) {
493                 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
494                 return r;
495         }
496
497         if (session_fd >= 0) {
498                 session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
499                 if (session_fd < 0) {
500                         pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
501                         return PAM_SESSION_ERR;
502                 }
503
504                 r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
505                 if (r != PAM_SUCCESS) {
506                         pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
507                         safe_close(session_fd);
508                         return r;
509                 }
510         }
511
512         return PAM_SUCCESS;
513 }
514
515 _public_ PAM_EXTERN int pam_sm_close_session(
516                 pam_handle_t *handle,
517                 int flags,
518                 int argc, const char **argv) {
519
520         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
521         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
522         const void *existing = NULL;
523         const char *id;
524         int r;
525
526         assert(handle);
527
528         /* Only release session if it wasn't pre-existing when we
529          * tried to create it */
530         pam_get_data(handle, "systemd.existing", &existing);
531
532         id = pam_getenv(handle, "XDG_SESSION_ID");
533         if (id && !existing) {
534
535                 /* Before we go and close the FIFO we need to tell
536                  * logind that this is a clean session shutdown, so
537                  * that it doesn't just go and slaughter us
538                  * immediately after closing the fd */
539
540                 r = sd_bus_open_system(&bus);
541                 if (r < 0) {
542                         pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
543                         return PAM_SESSION_ERR;
544                 }
545
546                 r = sd_bus_call_method(bus,
547                                        "org.freedesktop.login1",
548                                        "/org/freedesktop/login1",
549                                        "org.freedesktop.login1.Manager",
550                                        "ReleaseSession",
551                                        &error,
552                                        NULL,
553                                        "s",
554                                        id);
555                 if (r < 0) {
556                         pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
557                         return PAM_SESSION_ERR;
558                 }
559         }
560
561         /* Note that we are knowingly leaking the FIFO fd here. This
562          * way, logind can watch us die. If we closed it here it would
563          * not have any clue when that is completed. Given that one
564          * cannot really have multiple PAM sessions open from the same
565          * process this means we will leak one FD at max. */
566
567         return PAM_SUCCESS;
568 }