1
0
forked from ports/contrib

pam_xdg: strip to minimum (cannot track sessions via PAM), thus graceful

This commit is contained in:
Steffen Nurpmeso 2021-01-31 01:39:17 +01:00
parent 28d6763c03
commit f2193cf1ac
4 changed files with 55 additions and 174 deletions

View File

@ -1,7 +1,7 @@
untrusted comment: verify with /etc/ports/contrib.pub untrusted comment: verify with /etc/ports/contrib.pub
RWSagIOpLGJF3yVeUzlfQ7+UJq2gIuabqKJqhiWq7h2/UY5x8mlWVY7NEag9ndV9ypuodnfpLrP0QWzNjqO3eRJ1b5zTk3MRnAs= RWSagIOpLGJF3/opxnSlpIKUNMt7/zCoADPQ4PYlOmbd2GckIrSxOZvjqNNRaw5n+mhVd/iOl35BH6C2CIKF12kK6df2J7zZ4Q8=
SHA256 (Pkgfile) = e1b2ba87fd768518b4f81f3f9fb1fcce6780b2538cded18600564532e86f6a05 SHA256 (Pkgfile) = ac39f667ad9c2778b25753a6265ef2705335afb9b5cde1f70d9cb86f9ee22076
SHA256 (.footprint) = 56d789b652e6167f5fb93e1e6d48243e13f598c6d9a72705a8e54a003574ba31 SHA256 (.footprint) = 56d789b652e6167f5fb93e1e6d48243e13f598c6d9a72705a8e54a003574ba31
SHA256 (pam_xdg.c) = 9125ac3749b087f78844953a1a43790055a62efa0b8c25bd8766e35b061ca58c SHA256 (pam_xdg.c) = 81bfedeb798bc63d33f2b44874df0d796b439e93876a4a41f94d1ee26a001c38
SHA256 (pam_xdg.8) = 0f19e9f2437c6d0cb24465798ded6d6a3b2be07c012ee611648a4e4f1a21bbf1 SHA256 (pam_xdg.8) = 2929bcd6655d28127d386215d3d8c4fed6744b65c4866ac7e49d54cb438d9133
SHA256 (makefile) = 2466f499c3e84fd821176371fa9ff78143bf94b9ec09fd9e654b35613e4ead7d SHA256 (makefile) = 2466f499c3e84fd821176371fa9ff78143bf94b9ec09fd9e654b35613e4ead7d

View File

@ -3,7 +3,7 @@
# Maintainer: Steffen Nurpmeso, steffen at sdaoden dot eu # Maintainer: Steffen Nurpmeso, steffen at sdaoden dot eu
name=pam_xdg name=pam_xdg
version=20210130 version=20210131
release=1 release=1
source=($name.c $name.8 makefile) source=($name.c $name.8 makefile)

View File

@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
. .
.Dd January 30, 2021 .Dd January 31, 2021
.Dt PAM_XDG 8 .Dt PAM_XDG 8
.Os .Os
. .
@ -35,7 +35,7 @@
.Sh DESCRIPTION .Sh DESCRIPTION
. .
.Nm .Nm
is a PAM module that manages creation and deletion of the is a PAM module that manages creation of the
.Ev XDG_RUNTIME_DIR .Ev XDG_RUNTIME_DIR
directory, as well as injection of environment variables denoting all directory, as well as injection of environment variables denoting all
directories specified by the directories specified by the
@ -44,19 +44,17 @@ spec/\:basedir-\:spec-\:latest.html "XDG Base Directory Specification"
into user sessions. into user sessions.
. .
.Pp .Pp
When linked into the PAM system, the runtime directory will be created as When linked into the PAM session system the runtime directory will be
.Ql /run/user/`id -u` created once a user creates his or her first login session.
once a user creates his or her first login session, and it will be
removed recursively once the last such session ends.
Unless Unless
.Ar rundir .Ar rundir
was given all XDG related environment variables will be created in the was given all XDG related environment variables will be created in all
user session with their default or computed values, otherwise only user sessions with their default or computed values, otherwise only
.Ev XDG_RUNTIME_DIR . .Ev XDG_RUNTIME_DIR .
If If
.Ar notroot .Ar notroot
was given the module will bypass itself for root account logins, that was given the module will bypass itself for root account logins and
is, no actions will be performed. perform no actions for root.
. .
.Pp .Pp
In order to make use of this script, place the following in the control In order to make use of this script, place the following in the control

View File

@ -1,14 +1,12 @@
/*@ pam_xdg - manage XDG Base Directories (runtime dir life time, environment). /*@ pam_xdg - manage XDG Base Directories (runtime dir life time, environment).
*@ Create /run/user/`id -u` when the first session is opened, and remove it *@ Create /run/user/`id -u` when the first session is opened.
*@ again once the last is closed.
*@ It also creates according XDG_RUNTIME_DIR etc. environment variables in the *@ It also creates according XDG_RUNTIME_DIR etc. environment variables in the
*@ user sessions, except when given the "runtime" option, in which case it *@ user sessions, except when given the "runtime" option, in which case it
*@ only creates XDG_RUNTIME_DIR and not the others. *@ only creates XDG_RUNTIME_DIR and not the others.
*@ Place for example in /etc/pam.d/common-session one of the following: *@ Place for example in /etc/pam.d/common-session one of the following:
*@ session options pam_xdg.so [runtime] [notroot] *@ session options pam_xdg.so [runtime] [notroot]
*@ Notes: - effectively needs ISO C99 as it uses strtoull(3). *@ Notes: - according to XDG Base Directory Specification, v0.7.
*@ - according to XDG Base Directory Specification, v0.7. *@ - Linux-only (i think).
*@ - Linux.
* *
* Copyright (c) 2021 Steffen Nurpmeso <steffen@sdaoden.eu>. * Copyright (c) 2021 Steffen Nurpmeso <steffen@sdaoden.eu>.
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
@ -37,13 +35,9 @@
/* */ /* */
#define a_XDG "pam_xdg" #define a_XDG "pam_xdg"
#define a_RUNTIME_DIR_OUTER "/run" /* This must exist already */ #define a_RUNTIME_DIR_OUTER "/run" /* This must exist already */
#define a_RUNTIME_DIR_BASE "user" /* We create this as necessary, thus. */ #define a_RUNTIME_DIR_BASE "user" /* We create this as necessary, thus. */
#define a_RUNTIME_DIR_BASE_MODE 0755 /* 0711? */
#define a_LOCK_FILE "." a_XDG ".lck"
#define a_LOCK_TRIES 10
#define a_DAT_FILE "." a_XDG ".dat"
/* >8 -- 8< */ /* >8 -- 8< */
@ -55,13 +49,10 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <pwd.h> #include <pwd.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -82,23 +73,19 @@ static int a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc,
static int static int
a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
char uidbuf[sizeof "18446744073709551615"], char uidbuf[sizeof "18446744073709551615"],
wbuf[(((sizeof("18446744073709551615") -1) * 2) | wbuf[((sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
(sizeof("cd ..;rm -rf ") + sizeof("18446744073709551615")) |
(sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
sizeof(a_RUNTIME_DIR_BASE) + sizeof("18446744073709551615")) | sizeof(a_RUNTIME_DIR_BASE) + sizeof("18446744073709551615")) |
(sizeof("XDG_CONFIG_DIRS=") + PATH_MAX) (sizeof("XDG_CONFIG_DIRS=") + PATH_MAX)
) +1]; ) +1];
struct flock flp;
struct stat st; struct stat st;
uint64_t sessions;
struct passwd *pwp; struct passwd *pwp;
char const *emsg; char const *emsg;
int cntrlfd, fd, cwdfd, only_runtime, notroot, res, uidbuflen; int cwdfd, only_runtime, notroot, res, uidbuflen;
char const *user; char const *user;
(void)flags; (void)flags;
user = "<unset>"; user = "<unset>";
cntrlfd = fd = cwdfd = -1; cwdfd = -1;
only_runtime = notroot = 0; only_runtime = notroot = 0;
/* Command line */ /* Command line */
@ -114,7 +101,8 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
goto jerr; goto jerr;
} }
} }
} }else
goto jok; /* No longer used, session counting does not work */
/* We need the user we go for */ /* We need the user we go for */
if((res = pam_get_item(pamh, PAM_USER, (void const**)&user) if((res = pam_get_item(pamh, PAM_USER, (void const**)&user)
@ -143,7 +131,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
} }
/* We try create the base directory once as necessary */ /* We try create the base directory once as necessary */
if(isopen){ /*if(isopen)*/{
res = 0; res = 0;
while(fstatat(cwdfd, a_RUNTIME_DIR_BASE, &st, AT_SYMLINK_NOFOLLOW while(fstatat(cwdfd, a_RUNTIME_DIR_BASE, &st, AT_SYMLINK_NOFOLLOW
) == -1){ ) == -1){
@ -153,7 +141,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
goto jerr; goto jerr;
} }
if(mkdirat(cwdfd, a_RUNTIME_DIR_BASE, 0711) == -1){ if(mkdirat(cwdfd, a_RUNTIME_DIR_BASE, a_RUNTIME_DIR_BASE_MODE) == -1){
emsg = "cannot create base directory " emsg = "cannot create base directory "
a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE; a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE;
goto jerr; goto jerr;
@ -170,148 +158,47 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
close(cwdfd); close(cwdfd);
cwdfd = res; cwdfd = res;
/* Landed in the runtime base dir, obtain our lock */
if((cntrlfd = openat(cwdfd, a_LOCK_FILE,
(O_CREAT | O_WRONLY | O_NOFOLLOW | O_NOCTTY),
(S_IRUSR | S_IWUSR))) == -1){
emsg = "cannot open control lock file";
goto jerr;
}
for(res = a_LOCK_TRIES;;){
memset(&flp, 0, sizeof flp);
flp.l_type = F_WRLCK;
flp.l_start = 0;
flp.l_whence = SEEK_SET;
flp.l_len = 0;
if(fcntl(cntrlfd, F_SETLKW, &flp) != -1)
break;
if(errno != EINTR){
emsg = "unexpected error obtaining lock on control lock file";
goto jerr;
}
if(--res == 0){
emsg = "cannot obtain lock on control lock file";
goto jerr;
}
}
/* Turn to user management */ /* Turn to user management */
uidbuflen = snprintf(uidbuf, sizeof(uidbuf), "%lu", uidbuflen = snprintf(uidbuf, sizeof(uidbuf), "%lu",
(unsigned long)pwp->pw_uid); (unsigned long)pwp->pw_uid);
/* We create the per-user directory on isopen time as necessary */ /* We create the per-user directory on isopen time as necessary */
for(res = 0;; ++res){ /*if(isopen)*/{
int nfd; for(res = 0;; ++res){
int nfd;
if((nfd = openat(cwdfd, uidbuf, (O_PATH | O_DIRECTORY | O_NOFOLLOW)) if((nfd = openat(cwdfd, uidbuf, (O_PATH | O_DIRECTORY | O_NOFOLLOW))
) != -1){ ) != -1){
close(cwdfd); close(cwdfd);
cwdfd = nfd; cwdfd = nfd;
break;
}else{
if(errno == ENOENT){
if(!isopen)
goto jok;
if(res != 0)
goto jeurd;
}else{
jeurd:
emsg = "per user XDG_RUNTIME_DIR not accessible";
goto jerr;
}
}
if(mkdirat(cwdfd, uidbuf, 0700) == -1){
emsg = "cannot create per user XDG_RUNTIME_DIR";
goto jerr;
}
if(fchownat(cwdfd, uidbuf, pwp->pw_uid, pwp->pw_gid, AT_SYMLINK_NOFOLLOW
) == -1){
emsg = "cannot chown(2) per user XDG_RUNTIME_DIR";
goto jerr;
}
}
/* Read session counter; be simple and assume 0 if non-existent, this should
* not happen in practice */
sessions = 0;
if((fd = openat(cwdfd, a_DAT_FILE, O_RDONLY)) != -1){
char *ep;
ssize_t r;
while((r = read(fd, wbuf, sizeof(wbuf) -1)) == -1){
if(errno != EINTR){
emsg = "I/O error while reading session counter";
goto jerr;
}
}
/* We have written this as a valid POSIX text file, then, so chop tail */
if(r < 1 || (size_t)r >= (sizeof(wbuf) -1) / 2){
jecnt:
emsg = "session counter corrupted, ask administrator to remove "
a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE "/YOUR-UID/"
a_DAT_FILE;
goto jerr;
}
for(;;){
char c;
c = wbuf[(size_t)r - 1];
if(c == '\0' || c == '\n'){
if(--r == 0)
goto jecnt;
}else
break; break;
}else{
if(errno == ENOENT){
if(!isopen)
goto jok;
if(res != 0)
goto jeurd;
}else{
jeurd:
emsg = "per user XDG_RUNTIME_DIR not accessible";
goto jerr;
}
}
if(mkdirat(cwdfd, uidbuf, 0700) == -1){
emsg = "cannot create per user XDG_RUNTIME_DIR";
goto jerr;
}
if(fchownat(cwdfd, uidbuf, pwp->pw_uid, pwp->pw_gid,
AT_SYMLINK_NOFOLLOW) == -1){
emsg = "cannot chown(2) per user XDG_RUNTIME_DIR";
goto jerr;
}
} }
wbuf[(size_t)r] = '\0';
sessions = strtoull(wbuf, &ep, 10);
if(sessions == ULLONG_MAX || ep == wbuf || *ep != '\0')
goto jecnt;
close(fd);
fd = -1;
} }
if(isopen)
++sessions;
/* == 0 should never happen, but just handled it easily */
else if(sessions == 0 || --sessions == 0){
/* This is ridiculously simple, but everything else would be opposite */
char const cmd[] = "rm -rf " a_RUNTIME_DIR_OUTER "/"
a_RUNTIME_DIR_BASE "/";
memcpy(wbuf, cmd, sizeof(cmd) -1);
memcpy(&wbuf[sizeof(cmd) -1], uidbuf, uidbuflen +1);
res = system(wbuf);
if(!WIFEXITED(res) || WEXITSTATUS(res) != 0){
emsg = "unable to rm(1) -rf per user XDG_RUNTIME_DIR";
errno = EINVAL;
goto jerr;
}
goto jok;
}
/* Write out session counter (as a valid POSIX text file) */
res = snprintf(wbuf, sizeof wbuf, "%llu\n", (unsigned long long)sessions);
if(((fd = openat(cwdfd, a_DAT_FILE,
(O_CREAT | O_TRUNC | O_WRONLY | O_SYNC | O_NOFOLLOW | O_NOCTTY),
(S_IRUSR | S_IWUSR))) == -1) || write(fd, wbuf, res) != res){
emsg = "cannot write session counter, ask administrator to remove "
a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE "/YOUR-UID/"
a_DAT_FILE;
goto jerr;
}
close(fd);
fd = -1;
/* When opening, we want to put environment variables, too */ /* When opening, we want to put environment variables, too */
if(isopen){ /*if(isopen)*/{
char *cp; char *cp;
/* XDG_RUNTIME_DIR */ /* XDG_RUNTIME_DIR */
@ -374,10 +261,6 @@ jecnt:
jok: jok:
res = PAM_SUCCESS; res = PAM_SUCCESS;
jleave: jleave:
if(fd != -1)
close(fd);
if(cntrlfd != -1)
close(cntrlfd);
if(cwdfd != -1) if(cwdfd != -1)
close(cwdfd); close(cwdfd);