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
RWSagIOpLGJF3yVeUzlfQ7+UJq2gIuabqKJqhiWq7h2/UY5x8mlWVY7NEag9ndV9ypuodnfpLrP0QWzNjqO3eRJ1b5zTk3MRnAs=
SHA256 (Pkgfile) = e1b2ba87fd768518b4f81f3f9fb1fcce6780b2538cded18600564532e86f6a05
RWSagIOpLGJF3/opxnSlpIKUNMt7/zCoADPQ4PYlOmbd2GckIrSxOZvjqNNRaw5n+mhVd/iOl35BH6C2CIKF12kK6df2J7zZ4Q8=
SHA256 (Pkgfile) = ac39f667ad9c2778b25753a6265ef2705335afb9b5cde1f70d9cb86f9ee22076
SHA256 (.footprint) = 56d789b652e6167f5fb93e1e6d48243e13f598c6d9a72705a8e54a003574ba31
SHA256 (pam_xdg.c) = 9125ac3749b087f78844953a1a43790055a62efa0b8c25bd8766e35b061ca58c
SHA256 (pam_xdg.8) = 0f19e9f2437c6d0cb24465798ded6d6a3b2be07c012ee611648a4e4f1a21bbf1
SHA256 (pam_xdg.c) = 81bfedeb798bc63d33f2b44874df0d796b439e93876a4a41f94d1ee26a001c38
SHA256 (pam_xdg.8) = 2929bcd6655d28127d386215d3d8c4fed6744b65c4866ac7e49d54cb438d9133
SHA256 (makefile) = 2466f499c3e84fd821176371fa9ff78143bf94b9ec09fd9e654b35613e4ead7d

View File

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

View File

@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.
.Dd January 30, 2021
.Dd January 31, 2021
.Dt PAM_XDG 8
.Os
.
@ -35,7 +35,7 @@
.Sh DESCRIPTION
.
.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
directory, as well as injection of environment variables denoting all
directories specified by the
@ -44,19 +44,17 @@ spec/\:basedir-\:spec-\:latest.html "XDG Base Directory Specification"
into user sessions.
.
.Pp
When linked into the PAM system, the runtime directory will be created as
.Ql /run/user/`id -u`
once a user creates his or her first login session, and it will be
removed recursively once the last such session ends.
When linked into the PAM session system the runtime directory will be
created once a user creates his or her first login session.
Unless
.Ar rundir
was given all XDG related environment variables will be created in the
user session with their default or computed values, otherwise only
was given all XDG related environment variables will be created in all
user sessions with their default or computed values, otherwise only
.Ev XDG_RUNTIME_DIR .
If
.Ar notroot
was given the module will bypass itself for root account logins, that
is, no actions will be performed.
was given the module will bypass itself for root account logins and
perform no actions for root.
.
.Pp
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).
*@ Create /run/user/`id -u` when the first session is opened, and remove it
*@ again once the last is closed.
*@ Create /run/user/`id -u` when the first session is opened.
*@ It also creates according XDG_RUNTIME_DIR etc. environment variables in the
*@ user sessions, except when given the "runtime" option, in which case it
*@ only creates XDG_RUNTIME_DIR and not the others.
*@ Place for example in /etc/pam.d/common-session one of the following:
*@ session options pam_xdg.so [runtime] [notroot]
*@ Notes: - effectively needs ISO C99 as it uses strtoull(3).
*@ - according to XDG Base Directory Specification, v0.7.
*@ - Linux.
*@ Notes: - according to XDG Base Directory Specification, v0.7.
*@ - Linux-only (i think).
*
* Copyright (c) 2021 Steffen Nurpmeso <steffen@sdaoden.eu>.
* SPDX-License-Identifier: ISC
@ -37,13 +35,9 @@
/* */
#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_LOCK_FILE "." a_XDG ".lck"
#define a_LOCK_TRIES 10
#define a_DAT_FILE "." a_XDG ".dat"
#define a_RUNTIME_DIR_BASE_MODE 0755 /* 0711? */
/* >8 -- 8< */
@ -55,13 +49,10 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -82,23 +73,19 @@ static int a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc,
static int
a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
char uidbuf[sizeof "18446744073709551615"],
wbuf[(((sizeof("18446744073709551615") -1) * 2) |
(sizeof("cd ..;rm -rf ") + sizeof("18446744073709551615")) |
(sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
wbuf[((sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
sizeof(a_RUNTIME_DIR_BASE) + sizeof("18446744073709551615")) |
(sizeof("XDG_CONFIG_DIRS=") + PATH_MAX)
) +1];
struct flock flp;
struct stat st;
uint64_t sessions;
struct passwd *pwp;
char const *emsg;
int cntrlfd, fd, cwdfd, only_runtime, notroot, res, uidbuflen;
int cwdfd, only_runtime, notroot, res, uidbuflen;
char const *user;
(void)flags;
user = "<unset>";
cntrlfd = fd = cwdfd = -1;
cwdfd = -1;
only_runtime = notroot = 0;
/* Command line */
@ -114,7 +101,8 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
goto jerr;
}
}
}
}else
goto jok; /* No longer used, session counting does not work */
/* We need the user we go for */
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 */
if(isopen){
/*if(isopen)*/{
res = 0;
while(fstatat(cwdfd, a_RUNTIME_DIR_BASE, &st, AT_SYMLINK_NOFOLLOW
) == -1){
@ -153,7 +141,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
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 "
a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE;
goto jerr;
@ -170,148 +158,47 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
close(cwdfd);
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 */
uidbuflen = snprintf(uidbuf, sizeof(uidbuf), "%lu",
(unsigned long)pwp->pw_uid);
/* We create the per-user directory on isopen time as necessary */
for(res = 0;; ++res){
int nfd;
/*if(isopen)*/{
for(res = 0;; ++res){
int nfd;
if((nfd = openat(cwdfd, uidbuf, (O_PATH | O_DIRECTORY | O_NOFOLLOW))
) != -1){
close(cwdfd);
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
if((nfd = openat(cwdfd, uidbuf, (O_PATH | O_DIRECTORY | O_NOFOLLOW))
) != -1){
close(cwdfd);
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;
}
}
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 */
if(isopen){
/*if(isopen)*/{
char *cp;
/* XDG_RUNTIME_DIR */
@ -374,10 +261,6 @@ jecnt:
jok:
res = PAM_SUCCESS;
jleave:
if(fd != -1)
close(fd);
if(cntrlfd != -1)
close(cntrlfd);
if(cwdfd != -1)
close(cwdfd);