From 22f9331412816f50366e74dd3623ebbbc2590068 Mon Sep 17 00:00:00 2001 From: Jan Synacek Date: Tue, 17 Apr 2018 16:42:44 +0200 Subject: [PATCH] logind: enable limiting of user session scopes using pam context objects (#8397) --- man/pam_systemd.xml | 54 ++++++++++++++ src/login/logind-dbus.c | 28 +++++-- src/login/logind-session.c | 8 +- src/login/logind-session.h | 2 +- src/login/logind.c | 2 +- src/login/logind.h | 2 +- src/login/pam_systemd.c | 179 ++++++++++++++++++++++++++++++++++++++------- 7 files changed, 235 insertions(+), 40 deletions(-) diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 5aa12c7..cdf45f8 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -250,6 +250,60 @@ + Session limits + + PAM modules earlier in the stack, that is those that come before pam_systemd.so, + can set session scope limits using the PAM context objects. The data for these objects is provided as NUL-terminated C strings + and maps directly to the respective unit resource control directives. Note that these limits apply to individual sessions of the user, + they do not apply to all user processes as a combined whole. In particular, the per-user user@.service unit instance, + which runs the systemd --user manager process and its children, and is tracked outside of any session, being shared + by all the user's sessions, is not covered by these limits. + + + See + systemd.resource-control5 for more information about the resources. + Also, see pam_set_data3 for additional information about how to set + the context objects. + + + + + systemd.memory_max + + Sets unit MemoryMax=. + + + + systemd.tasks_max + + Sets unit TasksMax=. + + + + systemd.cpu_weight + + Sets unit CPUWeight=. + + + + systemd.io_weight + + Sets unit IOWeight=. + + + + Example data as can be provided from an another PAM module: + +pam_set_data(handle, "systemd.memory_max", (void *)"200M", cleanup); +pam_set_data(handle, "systemd.tasks_max", (void *)"50", cleanup); +pam_set_data(handle, "systemd.cpu_weight", (void *)"100", cleanup); +pam_set_data(handle, "systemd.io_weight", (void *)"340", cleanup); + + + + + + Example #%PAM-1.0 diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 563c7fe..d92437b 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -744,10 +744,6 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus } } - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - return r; - if (t == _SESSION_TYPE_INVALID) { if (!isempty(display)) t = SESSION_X11; @@ -903,7 +899,15 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus goto fail; } - r = session_start(session); + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + return r; + + r = session_start(session, message); + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(message); if (r < 0) goto fail; @@ -3056,7 +3060,7 @@ int manager_start_scope( const char *description, const char *after, const char *after2, - uint64_t tasks_max, + sd_bus_message *more_properties, sd_bus_error *error, char **job) { @@ -3120,9 +3124,17 @@ int manager_start_scope( if (r < 0) return r; - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + /* disable TasksMax= for the session scope, rely on the slice setting for it */ + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", (uint64_t)-1); if (r < 0) - return r; + return bus_log_create_error(r); + + if (more_properties) { + /* If TasksMax also appears here, it will overwrite the default value set above */ + r = sd_bus_message_copy(m, more_properties, true); + if (r < 0) + return r; + } r = sd_bus_message_close_container(m); if (r < 0) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index bfdc21c..d927750 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -545,7 +545,7 @@ int session_activate(Session *s) { return 0; } -static int session_start_scope(Session *s) { +static int session_start_scope(Session *s, sd_bus_message *properties) { int r; assert(s); @@ -570,7 +570,7 @@ static int session_start_scope(Session *s) { description, "systemd-logind.service", "systemd-user-sessions.service", - (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ + properties, &error, &job); if (r < 0) { @@ -591,7 +591,7 @@ static int session_start_scope(Session *s) { return 0; } -int session_start(Session *s) { +int session_start(Session *s, sd_bus_message *properties) { int r; assert(s); @@ -607,7 +607,7 @@ int session_start(Session *s) { return r; /* Create cgroup */ - r = session_start_scope(s); + r = session_start_scope(s, properties); if (r < 0) return r; diff --git a/src/login/logind-session.h b/src/login/logind-session.h index d81a339..6043048 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -127,7 +127,7 @@ void session_set_idle_hint(Session *s, bool b); int session_get_locked_hint(Session *s); void session_set_locked_hint(Session *s, bool b); int session_create_fifo(Session *s); -int session_start(Session *s); +int session_start(Session *s, sd_bus_message *properties); int session_stop(Session *s, bool force); int session_finalize(Session *s); int session_release(Session *s); diff --git a/src/login/logind.c b/src/login/logind.c index 726eca7..6bddcb5 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -1195,7 +1195,7 @@ static int manager_startup(Manager *m) { user_start(user); HASHMAP_FOREACH(session, m->sessions, i) - session_start(session); + session_start(session, NULL); HASHMAP_FOREACH(inhibitor, m->inhibitors, i) inhibitor_start(inhibitor); diff --git a/src/login/logind.h b/src/login/logind.h index 4786392..5574fcd 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -164,7 +164,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job); int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index b811e62..e1c2615 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -35,6 +35,7 @@ #include "terminal-util.h" #include "util.h" #include "path-util.h" +#include "cgroup-util.h" static int parse_argv( pam_handle_t *handle, @@ -198,13 +199,93 @@ error: return r; } +static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { + uint64_t val; + int r; + + if (isempty(limit)) + return 0; + + if (streq(limit, "infinity")) { + r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return r; + } + } else { + r = parse_percent(limit); + if (r >= 0) { + r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return r; + } + } else { + r = parse_size(limit, 1024, &val); + if (r >= 0) { + r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return r; + } + } else + pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit); + } + } + + return 0; +} + +static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) +{ + uint64_t val; + int r; + + /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */ + if (isempty(limit) || streq(limit, "infinity")) + return 0; + + r = safe_atou64(limit, &val); + if (r >= 0) { + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return r; + } + } else + pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit); + + return 0; +} + +static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) { + uint64_t val; + int r; + + if (!isempty(limit)) { + r = cg_weight_parse(limit, &val); + if (r >= 0) { + r = sd_bus_message_append(m, "(sv)", field, "t", val); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return r; + } + } else if (streq(field, "CPUWeight")) + pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit); + else + pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit); + } + + return 0; +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, int argc, const char **argv) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; const char *username, *id, *object_path, *runtime_path, *service = NULL, @@ -212,7 +293,8 @@ _public_ PAM_EXTERN int pam_sm_open_session( *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, - *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL; + *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, + *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int session_fd = -1, existing, r; bool debug = false, remote; @@ -353,6 +435,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( remote = !isempty(remote_host) && !is_localhost(remote_host); + (void) pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max); + (void) pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max); + (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight); + (void) pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight); + /* Talk to logind over the message bus */ r = sd_bus_open_system(&bus); @@ -361,7 +448,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( return PAM_SESSION_ERR; } - if (debug) + if (debug) { pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " "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", pw->pw_uid, getpid_cached(), @@ -369,29 +456,71 @@ _public_ PAM_EXTERN int pam_sm_open_session( type, class, strempty(desktop), strempty(seat), vtnr, strempty(tty), strempty(display), yes_no(remote), strempty(remote_user), strempty(remote_host)); + pam_syslog(handle, LOG_DEBUG, "Session limits: " + "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s", + strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight)); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CreateSession"); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + r = sd_bus_message_append(m, "uusssssussbss", + (uint32_t) pw->pw_uid, + (uint32_t) getpid_cached(), + service, + type, + class, + desktop, + seat, + vtnr, + tty, + display, + remote, + remote_user, + remote_host); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror(-r)); + return PAM_SYSTEM_ERR; + } + + r = append_session_memory_max(handle, m, memory_max); + if (r < 0) + return PAM_SESSION_ERR; + + r = append_session_tasks_max(handle, m, tasks_max); + if (r < 0) + return PAM_SESSION_ERR; + + r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight"); + if (r < 0) + return PAM_SESSION_ERR; + + r = append_session_cg_weight(handle, m, io_weight, "IOWeight"); + if (r < 0) + return PAM_SESSION_ERR; + + r = sd_bus_message_close_container(m); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror(-r)); + return PAM_SYSTEM_ERR; + } - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CreateSession", - &error, - &reply, - "uusssssussbssa(sv)", - (uint32_t) pw->pw_uid, - (uint32_t) getpid_cached(), - service, - type, - class, - desktop, - seat, - vtnr, - tty, - display, - remote, - remote_user, - remote_host, - 0); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) { pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r)); -- 2.7.4