mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
pam_systemd/pam_systemd_home: various fixes (#36505)
This commit is contained in:
5
TODO
5
TODO
@@ -140,6 +140,11 @@ Features:
|
||||
them under various conditions: 1. if tpm2 is available or not available;
|
||||
2. if sb is on or off; 3. if we are netbooted or not; …
|
||||
|
||||
* logind: invoke a service manager for "area" logins too. i.e. instantiate
|
||||
user@.service also for logins where XDG_AREA is set, in per-area fashion, and
|
||||
ref count it properly. Benefit: graphical logins should start working with
|
||||
the area logic.
|
||||
|
||||
* repart: introduce concept of "ghost" partitions, that we setup in almost all
|
||||
ways like other partitions, but do not actually register in the actual gpt
|
||||
table, but only tell the kernel about via BLKPG ioctl. These partitions are
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
# You really want to adjust this to your local distribution. If you use this
|
||||
# unmodified you are not building systems safely and securely.
|
||||
|
||||
-auth [success=done authtok_err=bad perm_denied=bad maxtries=bad default=ignore] pam_systemd_home.so
|
||||
auth sufficient pam_unix.so
|
||||
-auth sufficient pam_systemd_home.so
|
||||
auth required pam_deny.so
|
||||
|
||||
account required pam_nologin.so
|
||||
-account sufficient pam_systemd_home.so
|
||||
account sufficient pam_unix.so
|
||||
account required pam_permit.so
|
||||
-account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
|
||||
account required pam_unix.so
|
||||
|
||||
-password sufficient pam_systemd_home.so
|
||||
password sufficient pam_unix.so sha512 shadow try_first_pass
|
||||
|
||||
@@ -476,14 +476,13 @@ pam_set_data(handle, "systemd.runtime_max_sec", (void *)"3600", cleanup);
|
||||
<filename>systemd-logind.service</filename>:</para>
|
||||
|
||||
<programlisting>#%PAM-1.0
|
||||
-auth [success=done authtok_err=bad perm_denied=bad maxtries=bad default=ignore] pam_systemd_home.so
|
||||
auth sufficient pam_unix.so
|
||||
-auth sufficient pam_systemd_home.so
|
||||
auth required pam_deny.so
|
||||
|
||||
account required pam_nologin.so
|
||||
-account sufficient pam_systemd_home.so
|
||||
account sufficient pam_unix.so
|
||||
account required pam_permit.so
|
||||
-account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
|
||||
account required pam_unix.so
|
||||
|
||||
-password sufficient pam_systemd_home.so
|
||||
password sufficient pam_unix.so sha512 shadow try_first_pass
|
||||
|
||||
@@ -195,14 +195,13 @@ lennart@zeta$ cp -av /etc/skel ~/Areas/versuch1</programlisting>
|
||||
<filename>systemd-homed.service</filename> to log in:</para>
|
||||
|
||||
<programlisting>#%PAM-1.0
|
||||
<command>-auth [success=done authtok_err=bad perm_denied=bad maxtries=bad default=ignore] pam_systemd_home.so</command>
|
||||
auth sufficient pam_unix.so
|
||||
<command>-auth sufficient pam_systemd_home.so</command>
|
||||
auth required pam_deny.so
|
||||
|
||||
account required pam_nologin.so
|
||||
<command>-account sufficient pam_systemd_home.so</command>
|
||||
account sufficient pam_unix.so
|
||||
account required pam_permit.so
|
||||
<command>-account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so</command>
|
||||
account required pam_unix.so
|
||||
|
||||
<command>-password sufficient pam_systemd_home.so</command>
|
||||
password sufficient pam_unix.so sha512 shadow try_first_pass
|
||||
|
||||
@@ -77,6 +77,11 @@ int rmdir_parents(const char *path, const char *stop) {
|
||||
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
|
||||
int r;
|
||||
|
||||
assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
|
||||
assert(oldpath);
|
||||
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
|
||||
assert(newpath);
|
||||
|
||||
/* Try the ideal approach first */
|
||||
if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
|
||||
return 0;
|
||||
|
||||
@@ -553,24 +553,24 @@ static int acquire_home(
|
||||
|
||||
/* This acquires a reference to a home directory in the following ways:
|
||||
*
|
||||
* 1. If please_authenticate is false, it tries to call RefHome() first — which
|
||||
* will get us a reference to the home without authentication (which will work for homes that are
|
||||
* not encrypted, or that already are activated). If this works, we are done. Yay!
|
||||
* 1. If ACQUIRE_MUST_AUTHENTICATE is not set, it tries to call RefHome() first — which will get us a
|
||||
* reference to the home without authentication (which will work for homes that are not encrypted,
|
||||
* or that already are activated). If this works, we are done. Yay!
|
||||
*
|
||||
* 2. Otherwise, we'll call AcquireHome() — which will try to activate the home getting us a
|
||||
* reference. If this works, we are done. Yay!
|
||||
*
|
||||
* 3. if ref_anyway, we'll call RefHomeUnrestricted() — which will give us a reference in any case
|
||||
* (even if the activation failed!).
|
||||
* 3. if ACQUIRE_REF_ANYWAY is set, we'll call RefHomeUnrestricted() — which will give us a reference
|
||||
* in any case (even if the activation failed!).
|
||||
*
|
||||
* The idea is that please_authenticate is set to false for the PAM session hooks (since for those
|
||||
* authentication doesn't matter), and true for the PAM authentication hooks (since for those
|
||||
* authentication is essential). And ref_anyway should be set if we are pretty sure that we can later
|
||||
* activate the home directory via our fallback shell logic, and hence are OK if we can't activate
|
||||
* things here. Usecase for that are SSH logins where SSH does the authentication and thus only the
|
||||
* session hooks are called. But from the session hooks SSH doesn't allow asking questions, hence we
|
||||
* simply allow the login attempt to continue but then invoke our fallback shell that will prompt the
|
||||
* user for the missing unlock credentials, and then chainload the real shell.
|
||||
* The idea is that ACQUIRE_MUST_AUTHENTICATE is off for the PAM session hooks (since for those
|
||||
* authentication doesn't matter), and on for the PAM authentication hooks (since for those
|
||||
* authentication is essential). And ACQUIRE_REF_ANYWAY should be set if we are pretty sure that we
|
||||
* can later activate the home directory via our fallback shell logic, and hence are OK if we can't
|
||||
* activate things here. Usecase for that are SSH logins where SSH does the authentication and thus
|
||||
* only the session hooks are called. But from the session hooks SSH doesn't allow asking questions,
|
||||
* hence we simply allow the login attempt to continue but then invoke our fallback shell that will
|
||||
* prompt the user for the missing unlock credentials, and then chainload the real shell.
|
||||
*/
|
||||
|
||||
r = pam_get_user(handle, &username, NULL);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
#include "userdb.h"
|
||||
|
||||
@@ -370,14 +371,14 @@ static int export_legacy_dbus_address(
|
||||
pam_handle_t *handle,
|
||||
const char *runtime) {
|
||||
|
||||
const char *s;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r = PAM_BUF_ERR;
|
||||
int r;
|
||||
|
||||
assert(handle);
|
||||
|
||||
/* We need to export $DBUS_SESSION_BUS_ADDRESS because various applications will not connect
|
||||
* correctly to the bus without it. This setting matches what dbus.socket does for the user
|
||||
* session using 'systemctl --user set-environment'. We want to have the same configuration
|
||||
* in processes started from the PAM session.
|
||||
* correctly to the bus without it. This setting matches what dbus.socket does for the user session
|
||||
* using 'systemctl --user set-environment'. We want to have the same configuration in processes
|
||||
* started from the PAM session.
|
||||
*
|
||||
* The setting of the address is guarded by the access() check because it is also possible to compile
|
||||
* dbus without --enable-user-session, in which case this socket is not used, and
|
||||
@@ -386,14 +387,22 @@ static int export_legacy_dbus_address(
|
||||
* expect the socket to be present by the time we do this check, so we can just as well check once
|
||||
* here. */
|
||||
|
||||
s = strjoina(runtime, "/bus");
|
||||
if (access(s, F_OK) < 0)
|
||||
if (!runtime)
|
||||
return PAM_SUCCESS;
|
||||
|
||||
const char *s = strjoina(runtime, "/bus");
|
||||
if (access(s, F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
pam_syslog_errno(handle, LOG_WARNING, errno, "Failed to check if %s/bus exists, ignoring: %m", runtime);
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *t = NULL;
|
||||
if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 0);
|
||||
r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, /* readonly= */ false);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set bus variable: @PAMERR@");
|
||||
|
||||
@@ -577,12 +586,26 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
|
||||
assert(handle);
|
||||
assert(key);
|
||||
|
||||
/* Updates the environment, but only if there's actually a value set. Also, log about errors */
|
||||
/* Updates the environment, and removes environment variables if value is NULL or empty. Also, log
|
||||
* about errors. */
|
||||
|
||||
if (isempty(value)) {
|
||||
/* Unset the variable if set. Note that pam_putenv() would log nastily behind our back if we
|
||||
* call it without the variable actually being set. Hence we check explicitly if it's set
|
||||
* before. */
|
||||
|
||||
if (!pam_getenv(handle, key))
|
||||
return PAM_SUCCESS;
|
||||
|
||||
r = pam_putenv(handle, key);
|
||||
if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
|
||||
return pam_syslog_pam_error(handle, LOG_WARNING, r,
|
||||
"Failed to unset %s environment variable: @PAMERR@", key);
|
||||
|
||||
if (isempty(value))
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
r = pam_misc_setenv(handle, key, value, 0);
|
||||
r = pam_misc_setenv(handle, key, value, /* readonly= */ false);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to set environment variable %s: @PAMERR@", key);
|
||||
@@ -810,27 +833,6 @@ static int apply_user_record_settings(
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int configure_runtime_directory(
|
||||
pam_handle_t *handle,
|
||||
UserRecord *ur,
|
||||
const char *rt) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(handle);
|
||||
assert(ur);
|
||||
assert(rt);
|
||||
|
||||
if (!validate_runtime_directory(handle, rt, ur->uid))
|
||||
return PAM_SUCCESS;
|
||||
|
||||
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set runtime dir: @PAMERR@");
|
||||
|
||||
return export_legacy_dbus_address(handle, rt);
|
||||
}
|
||||
|
||||
static uint64_t pick_default_capability_ambient_set(
|
||||
UserRecord *ur,
|
||||
const char *service,
|
||||
@@ -1078,7 +1080,8 @@ static int register_session(
|
||||
SessionContext *c,
|
||||
UserRecord *ur,
|
||||
bool debug,
|
||||
char **ret_seat) {
|
||||
char **ret_seat,
|
||||
char **ret_runtime_dir) {
|
||||
|
||||
int r;
|
||||
|
||||
@@ -1086,18 +1089,19 @@ static int register_session(
|
||||
assert(c);
|
||||
assert(ur);
|
||||
assert(ret_seat);
|
||||
assert(ret_runtime_dir);
|
||||
|
||||
/* We don't register session class none with logind */
|
||||
if (streq(c->class, "none")) {
|
||||
pam_debug_syslog(handle, debug, "Skipping logind registration for session class none.");
|
||||
*ret_seat = NULL;
|
||||
*ret_seat = *ret_runtime_dir = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/* Make most of this a NOP on non-logind systems */
|
||||
if (!logind_running()) {
|
||||
pam_debug_syslog(handle, debug, "Skipping logind registration as logind is not running.");
|
||||
*ret_seat = NULL;
|
||||
*ret_seat = *ret_runtime_dir = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1170,7 +1174,7 @@ static int register_session(
|
||||
if (streq_ptr(error_id, "io.systemd.Login.AlreadySessionMember")) {
|
||||
/* We are already in a session, don't do anything */
|
||||
pam_debug_syslog(handle, debug, "Not creating session: %s", error_id);
|
||||
*ret_seat = NULL;
|
||||
*ret_seat = *ret_runtime_dir = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
if (error_id)
|
||||
@@ -1267,7 +1271,7 @@ static int register_session(
|
||||
/* We are already in a session, don't do anything */
|
||||
pam_debug_syslog(handle, debug,
|
||||
"Not creating session: %s", bus_error_message(&error, r));
|
||||
*ret_seat = NULL;
|
||||
*ret_seat = *ret_runtime_dir = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1303,16 +1307,6 @@ static int register_session(
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (original_uid == ur->uid) {
|
||||
/* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the
|
||||
* original user of the session. We do this in order not to result in privileged apps
|
||||
* clobbering the runtime directory unnecessarily. */
|
||||
|
||||
r = configure_runtime_directory(handle, ur, runtime_path);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Most likely we got the session/type/class from environment variables, but might have gotten the data
|
||||
* somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
|
||||
* data is inherited into the session processes, and programs can rely on them to be initialized. */
|
||||
@@ -1357,17 +1351,24 @@ static int register_session(
|
||||
TAKE_FD(fd);
|
||||
}
|
||||
|
||||
/* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the
|
||||
* original user of the session. We do this in order not to result in privileged apps
|
||||
* clobbering the runtime directory unnecessarily. */
|
||||
_cleanup_free_ char *rt = NULL;
|
||||
if (original_uid == ur->uid && validate_runtime_directory(handle, runtime_path, ur->uid))
|
||||
if (strdup_to(&rt, runtime_path) < 0)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
/* Everything worked, hence let's patch in the data we learned. Since 'real_set' points into the
|
||||
* D-Bus message, let's copy it and return it as a buffer */
|
||||
char *rs = NULL;
|
||||
if (real_seat) {
|
||||
rs = strdup(real_seat);
|
||||
if (!rs)
|
||||
return pam_log_oom(handle);
|
||||
}
|
||||
_cleanup_free_ char *rs = NULL;
|
||||
if (strdup_to(&rs, real_seat) < 0)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
c->seat = *ret_seat = rs;
|
||||
c->vtnr = real_vtnr;
|
||||
c->seat = *ret_seat = TAKE_PTR(rs);
|
||||
*ret_runtime_dir = TAKE_PTR(rt);
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1392,9 +1393,103 @@ static int import_shell_credentials(pam_handle_t *handle) {
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int update_home_env(
|
||||
static int mkdir_chown_open_directory(
|
||||
int parent_fd,
|
||||
const char *name,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
mode_t mode) {
|
||||
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
assert(parent_fd >= 0);
|
||||
assert(name);
|
||||
assert(uid_is_valid(uid));
|
||||
assert(gid_is_valid(gid));
|
||||
assert(mode != MODE_INVALID);
|
||||
|
||||
for (unsigned attempt = 0;; attempt++) {
|
||||
_cleanup_close_ int fd = openat(parent_fd, name, O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
|
||||
if (fd >= 0)
|
||||
return TAKE_FD(fd);
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* Let's create the directory under a temporary name first, since we want to make sure that
|
||||
* once it appears under the right name it has the right ownership */
|
||||
r = tempfn_random(name, /* extra= */ NULL, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = open_mkdir_at(parent_fd, t, O_CLOEXEC|O_EXCL, 0700); /* Use restrictive mode until ownership is in order */
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = RET_NERRNO(fchown(fd, uid, gid));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = RET_NERRNO(fchmod(fd, mode));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = rename_noreplace(parent_fd, t, parent_fd, name);
|
||||
if (r >= 0)
|
||||
return TAKE_FD(fd);
|
||||
if (r != -EEXIST || attempt >= 5)
|
||||
goto fail;
|
||||
|
||||
/* Maybe some other login attempt created the directory at the same time? Let's retry */
|
||||
(void) unlinkat(parent_fd, t, AT_REMOVEDIR);
|
||||
t = mfree(t);
|
||||
}
|
||||
|
||||
fail:
|
||||
(void) unlinkat(parent_fd, ASSERT_PTR(t), AT_REMOVEDIR);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int make_area_runtime_directory(
|
||||
pam_handle_t *handle,
|
||||
UserRecord *ur,
|
||||
const char *runtime_directory,
|
||||
const char *area,
|
||||
char **ret) {
|
||||
|
||||
assert(handle);
|
||||
assert(ur);
|
||||
assert(runtime_directory);
|
||||
assert(area);
|
||||
assert(ret);
|
||||
|
||||
/* Let's be careful with creating these directories, the runtime directory is owned by the user after all,
|
||||
* and they might play symlink games with us. */
|
||||
|
||||
_cleanup_close_ int fd = open(runtime_directory, O_CLOEXEC|O_PATH|O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, errno, "Unable to open runtime directory '%s': %m", runtime_directory);
|
||||
|
||||
_cleanup_close_ int fd_areas = mkdir_chown_open_directory(fd, "Areas", ur->uid, user_record_gid(ur), 0755);
|
||||
if (fd_areas < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, fd_areas, "Unable to create 'Areas' directory below '%s': %m", runtime_directory);
|
||||
|
||||
_cleanup_close_ int fd_area = mkdir_chown_open_directory(fd_areas, area, ur->uid, user_record_gid(ur), 0755);
|
||||
if (fd_area < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, fd_area, "Unable to create '%s' directory below '%s/Areas': %m", area, runtime_directory);
|
||||
|
||||
char *j = path_join(runtime_directory, "Areas", area);
|
||||
if (!j)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
*ret = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_environment(
|
||||
pam_handle_t *handle,
|
||||
UserRecord *ur,
|
||||
const char *runtime_directory,
|
||||
const char *area,
|
||||
bool debug) {
|
||||
|
||||
@@ -1408,7 +1503,7 @@ static int update_home_env(
|
||||
/* If an empty area string is specified, this means an explicit: do not use the area logic, normalize this here */
|
||||
area = empty_to_null(area);
|
||||
|
||||
_cleanup_free_ char *ha = NULL;
|
||||
_cleanup_free_ char *ha = NULL, *area_copy = NULL;
|
||||
if (area) {
|
||||
_cleanup_free_ char *j = path_join(h, "Areas", area);
|
||||
if (!j)
|
||||
@@ -1436,27 +1531,47 @@ static int update_home_env(
|
||||
pam_info(handle, "Path '%s' of requested user area '%s' is not owned by user, reverting to regular home directory.", ha, area);
|
||||
area = NULL;
|
||||
} else {
|
||||
pam_debug_syslog(handle, debug, "Area '%s' selected, setting $HOME to '%s': %m", area, ha);
|
||||
/* All good, now make a copy of the area string, since we quite likely are
|
||||
* going to invalidate it (if it points into the environment block), via the
|
||||
* update_environment() call below */
|
||||
area_copy = strdup(area);
|
||||
if (!area_copy)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
pam_debug_syslog(handle, debug, "Area '%s' selected, setting $HOME to '%s'.", area, ha);
|
||||
h = ha;
|
||||
area = area_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (area) {
|
||||
r = update_environment(handle, "XDG_AREA", area);
|
||||
r = update_environment(handle, "XDG_AREA", area);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = update_environment(handle, "HOME", h);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *per_area_runtime_directory = NULL;
|
||||
if (runtime_directory && area) {
|
||||
/* Also create a per-area subdirectory for $XDG_RUNTIME_DIR, so that each area has their own
|
||||
* set of runtime services. We follow the same directory structure as for $HOME. Note that we
|
||||
* do not define any form of automatic clean-up for the per-aera subdirs beyond the regular
|
||||
* clean-up of the whole $XDG_RUNTIME_DIRECTORY hierarchy when the user finally logs out. */
|
||||
|
||||
r = make_area_runtime_directory(handle, ur, runtime_directory, area, &per_area_runtime_directory);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
} else if (pam_getenv(handle, "XDG_AREA")) {
|
||||
/* Unset the $XDG_AREA variable if set. Note that pam_putenv() would log nastily behind our
|
||||
* back if we call it without $XDG_AREA actually being set. Hence we check explicitly if it's
|
||||
* set before. */
|
||||
r = pam_putenv(handle, "XDG_AREA");
|
||||
if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
|
||||
pam_syslog_pam_error(handle, LOG_WARNING, r,
|
||||
"Failed to unset XDG_AREA environment variable, ignoring: @PAMERR@");
|
||||
|
||||
runtime_directory = per_area_runtime_directory;
|
||||
}
|
||||
|
||||
return update_environment(handle, "HOME", h);
|
||||
r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_directory);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
return export_legacy_dbus_address(handle, runtime_directory);
|
||||
}
|
||||
|
||||
_public_ PAM_EXTERN int pam_sm_open_session(
|
||||
@@ -1522,8 +1637,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
|
||||
|
||||
session_context_mangle(handle, &c, ur, debug);
|
||||
|
||||
_cleanup_free_ char *seat_buffer = NULL;
|
||||
r = register_session(handle, &c, ur, debug, &seat_buffer);
|
||||
_cleanup_free_ char *seat_buffer = NULL, *runtime_dir = NULL;
|
||||
r = register_session(handle, &c, ur, debug, &seat_buffer, &runtime_dir);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
@@ -1531,7 +1646,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = update_home_env(handle, ur, c.area, debug);
|
||||
r = setup_environment(handle, ur, runtime_dir, c.area, debug);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
# Used by systemd --user instances.
|
||||
|
||||
{% if ENABLE_HOMED %}
|
||||
-account sufficient pam_systemd_home.so
|
||||
-account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
|
||||
{% endif %}
|
||||
account sufficient pam_unix.so no_pass_expiry
|
||||
account required pam_permit.so
|
||||
account required pam_unix.so no_pass_expiry
|
||||
|
||||
{% if HAVE_SELINUX %}
|
||||
session required pam_selinux.so close
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Used by run0 sessions
|
||||
|
||||
{% if ENABLE_HOMED %}
|
||||
-account sufficient pam_systemd_home.so
|
||||
-account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
|
||||
{% endif %}
|
||||
account required pam_unix.so
|
||||
|
||||
|
||||
@@ -576,12 +576,11 @@ if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPT
|
||||
if [[ -f "$dir/pam.d/sshd" ]]; then
|
||||
mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
|
||||
cat >"$dir/pam.d/sshd" <<EOF
|
||||
auth [success=done authtok_err=bad perm_denied=bad maxtries=bad default=ignore] pam_systemd_home.so
|
||||
auth sufficient pam_unix.so nullok
|
||||
auth sufficient pam_systemd_home.so debug
|
||||
auth required pam_deny.so
|
||||
account sufficient pam_systemd_home.so debug
|
||||
account sufficient pam_unix.so
|
||||
account required pam_permit.so
|
||||
account [success=done authtok_expired=bad new_authtok_reqd=bad maxtries=bad acct_expired=bad default=ignore] pam_systemd_home.so
|
||||
account required pam_unix.so
|
||||
session optional pam_systemd_home.so debug
|
||||
session optional pam_systemd.so
|
||||
session required pam_unix.so
|
||||
@@ -680,14 +679,18 @@ run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest ln -s
|
||||
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $HOME')" = "/home/subareatest"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo x$XDG_AREA')" = "x"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_AREA')" = "furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
|
||||
|
||||
PASSWORD=quux homectl update subareatest --default-area=molb
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $HOME')" = "/home/subareatest/Areas/molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_AREA')" = "molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_AREA')" = "furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
|
||||
|
||||
# Install a PK rule that allows 'subareatest' user to invoke run0 without password, just for testing
|
||||
cat > /usr/share/polkit-1/rules.d/subareatest.rules <<'EOF'
|
||||
@@ -702,17 +705,22 @@ EOF
|
||||
# Test "recursive" operation
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb sh -c 'echo $HOME')" = "/home/subareatest/Areas/molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb sh -c 'echo $XDG_AREA')" = "molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/molb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $HOME')" = "/home/subareatest/Areas/furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_AREA')" = "furb"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a molb run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a furb sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/furb"
|
||||
|
||||
# Test symlinked area
|
||||
mkdir -p /home/srub
|
||||
chown subareatest:subareatest /home/srub
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a srub sh -c 'echo $HOME')" = "/home/srub"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a srub sh -c 'echo $XDG_AREA')" = "srub"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a srub sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)/Areas/srub"
|
||||
|
||||
# Verify that login into an area not owned by target user will be redirected to main area
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a root sh -c 'echo $HOME')" = "/home/subareatest"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a root sh -c 'echo x$XDG_AREA')" = "x"
|
||||
test "$(run0 --property=SetCredential=pam.authtok.systemd-run0:quux -u subareatest -a root sh -c 'echo $XDG_RUNTIME_DIR')" = "/run/user/$(id -u subareatest)"
|
||||
|
||||
systemctl stop user@"$(id -u subareatest)".service
|
||||
|
||||
|
||||
Reference in New Issue
Block a user