-static int create_user_group(
- pam_handle_t *handle,
- const char *controller,
- const char *group,
- struct passwd *pw,
- bool attach,
- bool remember) {
-
- int r;
-
- assert(handle);
- assert(group);
-
- if (attach)
- r = cg_create_and_attach(controller, group, 0);
- else
- r = cg_create(controller, group);
-
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to create cgroup: %s", strerror(-r));
- return PAM_SESSION_ERR;
- }
-
- if (r > 0 && remember) {
- /* Remember that it was us who created this group, and
- * that hence we need to remove it too. This is a
- * protection against removing the cgroup when run
- * recursively. */
- if ((r = pam_set_data(handle, "systemd.created", INT_TO_PTR(1), NULL)) != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to install created variable.");
- return r;
- }
- }
-
- if ((r = cg_set_task_access(controller, group, 0644, pw->pw_uid, pw->pw_gid)) < 0 ||
- (r = cg_set_group_access(controller, group, 0755, pw->pw_uid, pw->pw_gid)) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to change access modes: %s", strerror(-r));
- return PAM_SESSION_ERR;
- }
-
- return PAM_SUCCESS;
-}
-
-static int reset_group(
- pam_handle_t *handle,
- const char *controller) {
-
- int r;
-
- assert(handle);
-
- if ((r = cg_attach(controller, "/", 0)) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to reset cgroup for controller %s: %s", controller, strerror(-r));
- return PAM_SESSION_ERR;
- }
-
- return PAM_SUCCESS;
-}
-
-_public_ PAM_EXTERN int pam_sm_open_session(
- pam_handle_t *handle,
- int flags,
- int argc, const char **argv) {
-
- const char *username = NULL;
- struct passwd *pw;
- int r;
- char *buf = NULL;
- int lock_fd = -1;
- bool create_session = true;
- char **controllers = NULL, **reset_controllers = NULL, **c;
-
- assert(handle);
-
- /* pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); */
-
- /* Make this a NOP on non-systemd systems */
- if (sd_booted() <= 0)
- return PAM_SUCCESS;
-
- if (parse_argv(handle,
- argc, argv,
- &create_session, NULL, NULL,
- &controllers, &reset_controllers,
- NULL, NULL) < 0)
- return PAM_SESSION_ERR;
-
- if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
- goto finish;
-
- if (safe_mkdir(RUNTIME_DIR "/user", 0755, 0, 0) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to create runtime directory: %m");
- r = PAM_SYSTEM_ERR;
- goto finish;
- }
-
- if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m");
- r = PAM_SYSTEM_ERR;
- goto finish;
- }
-
- /* Create /var/run/$USER */
- free(buf);
- if (asprintf(&buf, RUNTIME_DIR "/user/%s", username) < 0) {
- r = PAM_BUF_ERR;
- goto finish;
- }
-
- if (safe_mkdir(buf, 0700, pw->pw_uid, pw->pw_gid) < 0) {
- pam_syslog(handle, LOG_WARNING, "Failed to create runtime directory: %m");
- r = PAM_SYSTEM_ERR;
- goto finish;
- } else if ((r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", buf, 0)) != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
- goto finish;
- }
-
- free(buf);
- buf = NULL;
-
- if (create_session) {
- const char *id;
-
- /* Reuse or create XDG session ID */
- if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
- int mode;
-
- if (asprintf(&buf, "%llux", (unsigned long long) get_session_id(&mode)) < 0) {
- r = PAM_BUF_ERR;
- goto finish;
- }
-
- /* To avoid id clashes we add the session id
- * source to our session ids. Note that the
- * session id source might change during
- * runtime, because a filesystem became
- * writable or the system reconfigured. */
- buf[strlen(buf)-1] =
- mode != SESSION_ID_AUDIT ? (char) mode : 0;
-
- if ((r = pam_misc_setenv(handle, "XDG_SESSION_ID", buf, 0)) != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set session id.");
- goto finish;
- }
-
- if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
- pam_syslog(handle, LOG_ERR, "Failed to get session id.");
- r = PAM_SESSION_ERR;
- goto finish;
- }
- }
-
- r = asprintf(&buf, "/user/%s/%s", username, id);
- } else
- r = asprintf(&buf, "/user/%s/master", username);
-
- if (r < 0) {
- r = PAM_BUF_ERR;
- goto finish;
- }
-
- pam_syslog(handle, LOG_INFO, "Moving new user session for %s into control group %s.", username, buf);
-
- if ((r = create_user_group(handle, SYSTEMD_CGROUP_CONTROLLER, buf, pw, true, true)) != PAM_SUCCESS)
- goto finish;
-
- /* The additional controllers don't really matter, so we
- * ignore the return value */
- STRV_FOREACH(c, controllers)
- create_user_group(handle, *c, buf, pw, true, false);
-
- STRV_FOREACH(c, reset_controllers)
- reset_group(handle, *c);
-
- r = PAM_SUCCESS;
-
-finish:
- free(buf);
-
- if (lock_fd >= 0)
- close_nointr_nofail(lock_fd);
-
- strv_free(controllers);
- strv_free(reset_controllers);
-
- return r;
-}
-
-static int session_remains(pam_handle_t *handle, const char *user_path) {
- int r;
- bool remains = false;
- DIR *d;
- char *subgroup;
-
- if ((r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, user_path, &d)) < 0)
- return r;
-
- while ((r = cg_read_subgroup(d, &subgroup)) > 0) {
-
- remains = !streq(subgroup, "master");
- free(subgroup);
-
- if (remains)
- break;
- }
-
- closedir(d);
-
- if (r < 0)
- return r;
-
- return !!remains;
-}
-