Skip to content

Commit f9c1f4e

Browse files
committed
pam-systemd: apply user record properties to session
This way any component providing us with JSON user record data can use this for automatic resource management and other session properties.
1 parent 7bfbf6c commit f9c1f4e

File tree

1 file changed

+145
-2
lines changed

1 file changed

+145
-2
lines changed

src/login/pam_systemd.c

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
#include "fd-util.h"
2626
#include "fileio.h"
2727
#include "format-util.h"
28+
#include "fs-util.h"
2829
#include "hostname-util.h"
30+
#include "locale-util.h"
2931
#include "login-util.h"
3032
#include "macro.h"
3133
#include "pam-util.h"
3234
#include "parse-util.h"
3335
#include "path-util.h"
3436
#include "process-util.h"
37+
#include "rlimit-util.h"
3538
#include "socket-util.h"
3639
#include "stdio-util.h"
3740
#include "strv.h"
@@ -473,6 +476,138 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u
473476
return false;
474477
}
475478

479+
static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) {
480+
int r;
481+
482+
assert(handle);
483+
assert(e);
484+
485+
r = pam_putenv(handle, e);
486+
if (r != PAM_SUCCESS) {
487+
pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable %s: %s", e, pam_strerror(handle, r));
488+
return r;
489+
}
490+
491+
if (debug)
492+
pam_syslog(handle, LOG_DEBUG, "PAM environment variable %s set based on user record.", e);
493+
494+
return PAM_SUCCESS;
495+
}
496+
497+
static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) {
498+
char **i;
499+
int r;
500+
501+
assert(handle);
502+
assert(ur);
503+
504+
if (ur->umask != MODE_INVALID) {
505+
umask(ur->umask);
506+
507+
if (debug)
508+
pam_syslog(handle, LOG_DEBUG, "Set user umask to %04o based on user record.", ur->umask);
509+
}
510+
511+
STRV_FOREACH(i, ur->environment) {
512+
_cleanup_free_ char *n = NULL;
513+
const char *e;
514+
515+
assert_se(e = strchr(*i, '=')); /* environment was already validated while parsing JSON record, this thus must hold */
516+
517+
n = strndup(*i, e - *i);
518+
if (!n)
519+
return pam_log_oom(handle);
520+
521+
if (pam_getenv(handle, n)) {
522+
if (debug)
523+
pam_syslog(handle, LOG_DEBUG, "PAM environment variable $%s already set, not changing based on record.", *i);
524+
continue;
525+
}
526+
527+
r = pam_putenv_and_log(handle, *i, debug);
528+
if (r != PAM_SUCCESS)
529+
return r;
530+
}
531+
532+
if (ur->email_address) {
533+
if (pam_getenv(handle, "EMAIL")) {
534+
if (debug)
535+
pam_syslog(handle, LOG_DEBUG, "PAM environment variable $EMAIL already set, not changing based on user record.");
536+
} else {
537+
_cleanup_free_ char *joined = NULL;
538+
539+
joined = strjoin("EMAIL=", ur->email_address);
540+
if (!joined)
541+
return pam_log_oom(handle);
542+
543+
r = pam_putenv_and_log(handle, joined, debug);
544+
if (r != PAM_SUCCESS)
545+
return r;
546+
}
547+
}
548+
549+
if (ur->time_zone) {
550+
if (pam_getenv(handle, "TZ")) {
551+
if (debug)
552+
pam_syslog(handle, LOG_DEBUG, "PAM environment variable $TZ already set, not changing based on user record.");
553+
} else if (!timezone_is_valid(ur->time_zone, LOG_DEBUG)) {
554+
if (debug)
555+
pam_syslog(handle, LOG_DEBUG, "Time zone specified in user record is not valid locally, not setting $TZ.");
556+
} else {
557+
_cleanup_free_ char *joined = NULL;
558+
559+
joined = strjoin("TZ=:", ur->time_zone);
560+
if (!joined)
561+
return pam_log_oom(handle);
562+
563+
r = pam_putenv_and_log(handle, joined, debug);
564+
if (r != PAM_SUCCESS)
565+
return r;
566+
}
567+
}
568+
569+
if (ur->preferred_language) {
570+
if (pam_getenv(handle, "LANG")) {
571+
if (debug)
572+
pam_syslog(handle, LOG_DEBUG, "PAM environment variable $LANG already set, not changing based on user record.");
573+
} else if (!locale_is_valid(ur->preferred_language)) {
574+
if (debug)
575+
pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid locally, not setting $LANG.");
576+
} else {
577+
_cleanup_free_ char *joined = NULL;
578+
579+
joined = strjoin("LANG=", ur->preferred_language);
580+
if (!joined)
581+
return pam_log_oom(handle);
582+
583+
r = pam_putenv_and_log(handle, joined, debug);
584+
if (r != PAM_SUCCESS)
585+
return r;
586+
}
587+
}
588+
589+
if (nice_is_valid(ur->nice_level)) {
590+
if (nice(ur->nice_level) < 0)
591+
pam_syslog(handle, LOG_ERR, "Failed to set nice level to %i, ignoring: %s", ur->nice_level, strerror_safe(errno));
592+
else if (debug)
593+
pam_syslog(handle, LOG_DEBUG, "Nice level set, based on user record.");
594+
}
595+
596+
for (int rl = 0; rl < _RLIMIT_MAX; rl++) {
597+
598+
if (!ur->rlimits[rl])
599+
continue;
600+
601+
r = setrlimit_closest(rl, ur->rlimits[rl]);
602+
if (r < 0)
603+
pam_syslog(handle, LOG_ERR, "Failed to set resource limit %s, ignoring: %s", rlimit_to_string(rl), strerror_safe(r));
604+
else if (debug)
605+
pam_syslog(handle, LOG_DEBUG, "Resource limit %s set, based on user record.", rlimit_to_string(rl));
606+
}
607+
608+
return PAM_SUCCESS;
609+
}
610+
476611
_public_ PAM_EXTERN int pam_sm_open_session(
477612
pam_handle_t *handle,
478613
int flags,
@@ -540,6 +675,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
540675
if (r != PAM_SUCCESS)
541676
return r;
542677

678+
r = apply_user_record_settings(handle, ur, debug);
679+
if (r != PAM_SUCCESS)
680+
return r;
681+
543682
return PAM_SUCCESS;
544683
}
545684

@@ -797,9 +936,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
797936
}
798937
}
799938

939+
r = apply_user_record_settings(handle, ur, debug);
940+
if (r != PAM_SUCCESS)
941+
return r;
942+
800943
/* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
801-
* not going to process the bus connection in that time, so let's better close before the daemon
802-
* kicks us off because we are not processing anything. */
944+
* not going to use the bus connection in that time, so let's better close before the daemon kicks us
945+
* off because we are not processing anything. */
803946
(void) pam_release_bus_connection(handle);
804947
return PAM_SUCCESS;
805948
}

0 commit comments

Comments
 (0)