├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── pam_dumb_runtime_dir.c /.gitignore: -------------------------------------------------------------------------------- 1 | pam_dumb_runtime_dir.so 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Isaac Freund 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 11 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr 2 | PAMDIR = $(PREFIX)/lib/security 3 | 4 | CC = cc 5 | CFLAGS = -Os -Wall -Wextra -Wpedantic -Wconversion -Werror 6 | PAMFLAGS = $$(pkg-config --cflags --libs pam) 7 | 8 | RUNTIME_DIR_PARENT = /run/user 9 | 10 | pam_dumb_runtime_dir.so: pam_dumb_runtime_dir.c 11 | $(CC) -o $@ pam_dumb_runtime_dir.c -shared -fPIC -std=c99 \ 12 | $(PAMFLAGS) $(CFLAGS) $(LDFLAGS) \ 13 | '-DRUNTIME_DIR_PARENT="$(RUNTIME_DIR_PARENT)"' 14 | 15 | .PHONY: all install uninstall clean 16 | 17 | all: pam_dumb_runtime_dir.so 18 | 19 | install: pam_dumb_runtime_dir.so 20 | mkdir -p $(DESTDIR)$(PAMDIR) 21 | cp -f pam_dumb_runtime_dir.so $(DESTDIR)$(PAMDIR) 22 | 23 | uninstall: 24 | rm -f $(DESTDIR)$(PAMDIR)/pam_dumb_runtime_dir.so 25 | 26 | clean: 27 | rm -f pam_dumb_runtime_dir.so 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dumb_runtime_dir 2 | 3 | Creates an `XDG_RUNTIME_DIR` directory on login per the freedesktop.org 4 | base directory spec. Flaunts the spec and never removes it, even after last 5 | logout. This keeps things simple and predictable. 6 | 7 | The user is responsible for ensuring that the `RUNTIME_DIR_PARENT` directory 8 | (`/run/user` by default) exists and is only writable by root. 9 | 10 | ## PAM configuration 11 | 12 | To enable the pam module, add the following recommended configuration to 13 | `/etc/pam.d/system-login`: 14 | 15 | ``` 16 | session optional pam_dumb_runtime_dir.so 17 | ``` 18 | 19 | See also `pam.conf(5)`. 20 | 21 | ## Licensing 22 | 23 | dumb_runtime_dir is released under the Zero Clause BSD license. 24 | -------------------------------------------------------------------------------- /pam_dumb_runtime_dir.c: -------------------------------------------------------------------------------- 1 | /** 2 | * pam_dumb_runtime_dir.c 3 | * 4 | * Creates an XDG_RUNTIME_DIR directory on login per the freedesktop.org 5 | * base directory spec. Flaunts the spec and never removes it, even after 6 | * last logout. This keeps things simple and predictable. 7 | * 8 | * The user is responsible for ensuring that the RUNTIME_DIR_PARENT directory, 9 | * (/run/user by default) exists and is only writable by root. 10 | * 11 | * Copyright 2021 Isaac Freund 12 | * 13 | * Permission to use, copy, modify, and/or distribute this software for any 14 | * purpose with or without fee is hereby granted. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 17 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 19 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 22 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | int pam_sm_open_session(pam_handle_t *pamh, int flags, 36 | int argc, const char **argv) { 37 | (void)flags; 38 | (void)argc; 39 | (void)argv; 40 | 41 | const char *user; 42 | if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { 43 | return PAM_SESSION_ERR; 44 | } 45 | 46 | struct passwd *pw = getpwnam(user); 47 | if (pw == NULL) { 48 | return PAM_SESSION_ERR; 49 | } 50 | 51 | /* The bit size of uintmax_t will always be larger than the number of 52 | * bytes needed to print it. */ 53 | char buffer[sizeof("XDG_RUNTIME_DIR="RUNTIME_DIR_PARENT"/") + 54 | sizeof(uintmax_t) * 8]; 55 | /* Valid UIDs are always positive even if POSIX allows the uid_t type 56 | * itself to be signed. Therefore, we can convert to uintmax_t for 57 | * safe formatting. */ 58 | int ret = snprintf(buffer, sizeof(buffer), 59 | "XDG_RUNTIME_DIR="RUNTIME_DIR_PARENT"/%ju", (uintmax_t)pw->pw_uid); 60 | assert(ret >= 0 && (size_t)ret < sizeof(buffer)); 61 | const char *path = buffer + sizeof("XDG_RUNTIME_DIR=") - 1; 62 | 63 | if (mkdir(path, 0700) < 0) { 64 | /* It's ok if the directory already exists, in that case we just 65 | * ensure the mode is correct before we chown(). */ 66 | if (errno != EEXIST) { 67 | return PAM_SESSION_ERR; 68 | } 69 | if (chmod(path, 0700) < 0) { 70 | return PAM_SESSION_ERR; 71 | } 72 | } 73 | 74 | if (chown(path, pw->pw_uid, pw->pw_gid) < 0) { 75 | return PAM_SESSION_ERR; 76 | } 77 | 78 | if (pam_putenv(pamh, buffer) != PAM_SUCCESS) { 79 | return PAM_SESSION_ERR; 80 | } 81 | 82 | return PAM_SUCCESS; 83 | } 84 | 85 | /* PAM requires all functions in a group to be defined, even if a noop is 86 | * desired. Otherwise, PAM_MODULE_UNKNOWN is returned when the application 87 | * calls pam_close_session(3). */ 88 | int pam_sm_close_session(pam_handle_t *pamh, int flags, 89 | int argc, const char **argv) { 90 | (void)pamh; 91 | (void)flags; 92 | (void)argc; 93 | (void)argv; 94 | 95 | return PAM_SUCCESS; 96 | } 97 | --------------------------------------------------------------------------------