1
0
forked from ports/contrib

pam_xdg: 20210801: bring back (optional) session support

This commit is contained in:
Steffen Nurpmeso 2021-08-02 16:01:45 +02:00
parent 0360f776d7
commit 63f8a96b40
5 changed files with 167 additions and 50 deletions

View File

@ -1,7 +1,7 @@
untrusted comment: verify with /etc/ports/contrib.pub
RWSagIOpLGJF3z9vXj+fCpENPa65XMag5E6REcBr0Kj7oKCdaZuUaD45UgkBxtRvncVQRDW3rQ8But8+nu14Ag1LHZ7s0OHJXg4=
SHA256 (Pkgfile) = 2c85163d75631d082443136b26d215babc716a9bf2521c616f58f4f1706ae4b4
RWSagIOpLGJF35ByKJGqZDYTDYgapw8YNFcmoQfuIz4G84szVxTjO1rS11BBqoORBYTNjhd+T3T+UY/nvIWL0h6PK6YhrCZ7rwA=
SHA256 (Pkgfile) = c8ca4da1cb69de37a134d64ca3acbe68d49a5ca2f07c03f86919d9287acd39bb
SHA256 (.footprint) = 56d789b652e6167f5fb93e1e6d48243e13f598c6d9a72705a8e54a003574ba31
SHA256 (pam_xdg.c) = 8569b0cbab468de159143bf58f9207dd9c3db204f44b7153ee410d0733526cea
SHA256 (pam_xdg.8) = 999548e64134d26d0ffeec2931f15a905e4029e6b404e86825d02978d2393d7a
SHA256 (pam_xdg.c) = 3ed98ccd23cee452086722be11c605420b5b96d2a5bb6734270121f1eb18463c
SHA256 (pam_xdg.8) = 946a65f7559e5e9c343fecf565fc3ddee8227e29e2dbb1729eaa96ee6eff75f1
SHA256 (makefile) = 2466f499c3e84fd821176371fa9ff78143bf94b9ec09fd9e654b35613e4ead7d

View File

@ -1,9 +1,9 @@
# Description: PAM module to manage XDG Base Directories
# Description: PAM module to manage XDG Base Directories (sessions)
# URL: https://www.sdaoden.eu/code.html#s-toolbox
# Maintainer: Steffen Nurpmeso, steffen at sdaoden dot eu
name=pam_xdg
version=20210731
version=20210801
release=1
source=($name.c $name.8 makefile)

View File

@ -8,9 +8,11 @@ For it to work it must be included in /etc/pam.d -- to make it a
vivid part of session handling the file /etc/pam.d/common-session
seems best. Include the following early:
session optional pam_xdg.so [notroot] [runtime]
session optional pam_xdg.so [notroot] [runtime] [track_sessions]
Use notroot argument to only handle XDG for non-root users.
Use runtime argument to only handle of XDG_RUNTIME_DIR.
Use track_sessions to remove XDG_RUNTIME_DIR once the last user
session ends (take care of CAVEATS of manual, maybe).
[1] https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

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 July 29, 2021
.Dd August 1, 2021
.Dt PAM_XDG 8
.Os
.
@ -30,7 +30,7 @@
.Nm
.Op Ar runtime
.Op Ar notroot
.\".Op Ar track_user_sessions Op Ar per_user_lock
.Op Ar track_sessions Op Ar per_user_lock
.
.
.Sh DESCRIPTION
@ -56,13 +56,13 @@ If
.Ar notroot
was given the module will bypass itself for root account logins and
perform no actions for root.
.\"Lastly
.\".Ar track_user_sessions
.\"will enable session tracking: once the last session ends, the user's
.\".Ev XDG_RUNTIME_DIR
.\"will be recursively removed; on high-load servers setting
.\".Ar per_user_lock
.\"then will reduce lock file lock contention.
Lastly
.Ar track_sessions
will enable session tracking: once the last session ends, the user's
.Ev XDG_RUNTIME_DIR
will be recursively removed; on high-load servers then setting
.Ar per_user_lock
will reduce lock file lock contention.
.
.Pp
In order to make use of this module, place the following in the
@ -81,7 +81,7 @@ and
may be desirable, adjusting paths as necessary:
.
.Bd -literal -offset indent
session optional pam_xdg.so notroot \"track_user_sessions
session optional pam_xdg.so notroot track_sessions
.Ed
.
.
@ -101,13 +101,12 @@ session optional pam_xdg.so notroot \"track_user_sessions
On Unix systems any
.Dq daemonized
program or script is reparented to the program running with PID 1,
therefore leaving the PAM user session without PAM recognizing this.
most likely leaving the PAM user session without PAM recognizing this.
Yet careless such code may hold or expect availability of resources of
the session it just left, truly performing cleanup when sessions end
seems thus unwise.
.\"However, many PAM modules do support cleanup upon closing the last
.\"session of a user, and therefore
.\".Nm
.\"supports this optionally, too.
Since so many PAM modules do support session tracking and cleanup
.Nm
readded optional support for this.
.
.\" s-ts-mode

View File

@ -1,8 +1,9 @@
/*@ pam_xdg - manage XDG Base Directories (runtime dir life time, environment).
*@ See pam_xdg.8 for more.
*@ - According to XDG Base Directory Specification, v0.7.
*@ - Supports libpam (Linux) and *BSD OpenPAM.
*@ - Supports libpam (Linux) and OpenPAM.
*@ - Requires C preprocessor with __VA_ARGS__ support!
*@ - Uses "rm -rf" to drop per-user directories. XXX Unroll this? nftw?
*
* Copyright (c) 2021 Steffen Nurpmeso <steffen@sdaoden.eu>.
* SPDX-License-Identifier: ISC
@ -34,6 +35,13 @@
#define a_RUNTIME_DIR_BASE "user"
#define a_RUNTIME_DIR_BASE_MODE 0755 /* 0711? */
/* Note: we manage these relative to the per-user directory!
* a_LOCK_FILE is only used without "per_user_lock" */
#define a_LOCK_FILE "../." a_XDG ".lck"
#define a_LOCK_TRIES 10
#define a_DAT_FILE "." a_XDG ".dat"
/* >8 -- 8< */
/*
@ -44,11 +52,13 @@
#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> /* xxx not, actually!?! */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -96,7 +106,7 @@
# define a_LOG_NOTICE LOG_NOTICE
#endif
/* Because of complicated file locking, use one function with two exec paths */
/* Just put it all in one big fun, use two exec paths */
static int a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc,
const char **argv);
@ -107,10 +117,9 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
/* Options */
a_RUNTIME = 1u<<0,
a_NOTROOT = 1u<<1,
#if 0
a_SESSIONS = 1u<<16,
a_USER_LOCK = 1u<<17,
#endif
/* Flags */
a_MPV = 1u<<29, /* Multi-Purpose-Vehicle */
a_SKIP_XDG = 1u<<30 /* We shall not act */
@ -128,20 +137,22 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
};
static int f_saved;
char uidbuf[sizeof ".18446744073709551615"],
wbuf[((sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
sizeof(a_RUNTIME_DIR_BASE) + sizeof(".18446744073709551615")) |
char uidbuf[sizeof "../.18446744073709551615"],
xbuf[((sizeof("XDG_RUNTIME_DIR=") + sizeof(a_RUNTIME_DIR_OUTER) +
sizeof(a_RUNTIME_DIR_BASE) +
sizeof("../.18446744073709551615")) |
(sizeof("XDG_CONFIG_DIRS=") + PATH_MAX)
) +1];
struct a_dirtree dt_user;
struct a_dirtree const *dtp;
struct passwd *pwp;
char const *emsg;
int cwdfd, f, res, uidbuflen;
int cwdfd, cntrlfd, datfd, f, res, uidbuflen;
char const *user;
user = "<unset>";
cwdfd = AT_FDCWD;
datfd = cntrlfd = -1;
/* Command line */
if(isopen){
@ -158,12 +169,10 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
}
else if(!strcmp(argv[0], "notroot"))
f |= a_NOTROOT;
#if 0
else if(!strcmp(argv[0], "track_user_sessions"))
else if(!strcmp(argv[0], "track_sessions"))
f |= a_SESSIONS;
else if(!strcmp(argv[0], "per_user_lock"))
f |= a_USER_LOCK;
#endif
else if(!(flags & PAM_SILENT)){
emsg = "command line";
errno = EINVAL;
@ -171,17 +180,14 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
}
}
#if 0
if((f & a_USER_LOCK) && !(f & a_SESSIONS))
a_LOG(pamh, a_LOG_NOTICE,
a_XDG ": \"per_user_lock\" requires \"track_user_sessions\"");
#endif
a_XDG ": \"per_user_lock\" requires \"track_sessions\"");
}else{
f = f_saved;
if(f & a_SKIP_XDG)
goto jok;
goto jok; /* No longer used, session counting does not work */
}
/* We need the user we go for */
@ -192,6 +198,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
goto jepam;
}
/* No PAM failure, no PAM_USER_UNKNOWN here: we are no authentificator! */
if((pwp = getpwnam(user)) == NULL){
emsg = "host does not know about user";
errno = EINVAL;
@ -204,13 +211,13 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
}
/* Our lockfile and per-user directory name */
uidbuflen = snprintf(uidbuf, sizeof(uidbuf), ".%lu",
(unsigned long)pwp->pw_uid);
uidbuflen = snprintf(uidbuf, sizeof(uidbuf), "../.%lu", /* xxx error?? */
(unsigned long)pwp->pw_uid) - 3;
dt_user.name = &uidbuf[1];
dt_user.mode = 0700;
dt_user.name = &uidbuf[4];
dt_user.mode = 0700; /* XDG implied */
/* Handle tree. On *BSD outermost may not exist! */
/* Handle tree, go to user runtime. On *BSD outermost may not exist! */
for(/*f &= ~a_MPV,*/ dtp = a_dirtree;;){
int e;
gid_t oegid;
@ -231,8 +238,8 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
}
if(!isopen)
/* Someone removed the entire directory tree while sessions were open!
* Silently out!?! */
/* XXX Entire directory tree disappeared while sessions were open!
* XXX Silently out!?! */
goto jok;
/* We try creating the directories once as necessary */
@ -263,19 +270,124 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
"(a mount point of) volatile storage!");
/* Just chown it! */
else if(dtp == &dt_user &&
fchownat(cwdfd, &uidbuf[1], pwp->pw_uid, pwp->pw_gid,
fchownat(cwdfd, &uidbuf[4], pwp->pw_uid, pwp->pw_gid,
AT_SYMLINK_NOFOLLOW) == -1){
emsg = "cannot chown(2) per user XDG_RUNTIME_DIR";
goto jerr;
}
}
/* In session mode we have to manage the counter file */
if(f & a_SESSIONS){
unsigned long long int sessions;
/* Landed in the runtime base dir, obtain our lock */
if((cntrlfd = openat(cwdfd,
(f & a_USER_LOCK ? uidbuf : 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;;){
struct flock flp;
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;
/* XXX It may happen we cannot manage the lock and thus not access
* XXX the session counter, ie this session is zombie to us.
* XXX Just like counter below, should globally disable sessions!! */
if(errno != EINTR){
emsg = "unexpected error obtaining lock on lock control file";
goto jerr;
}
if(--res == 0){
emsg = "cannot obtain lock on lock control file";
goto jerr;
}
}
sessions = 0;
if((datfd = openat(cwdfd, a_DAT_FILE, O_RDONLY)) != -1){
char *ep;
ssize_t r;
while((r = read(datfd, xbuf, sizeof(xbuf) -1)) == -1){
if(errno != EINTR)
goto jecnt;
}
close(datfd);
datfd = -1;
xbuf[(size_t)r] = '\0';
sessions = strtoull(xbuf, &ep, 10);
/* Do not log too often for "session counter error"s, as below */
if(ep == xbuf){
res = PAM_SESSION_ERR;
goto jleave;
}else if(sessions == ULLONG_MAX || ep != &xbuf[(size_t)r])
goto jecnt;
}
if(isopen)
++sessions;
else if(sessions > 0)
--sessions;
if(!isopen && sessions == 0){ /* former.. hmmm. */
/* Ridiculously simple, but everything else would be the opposite.
* Ie, E[MN]FILE failures, or whatever else */
char const cmd[] = "rm -rf " a_RUNTIME_DIR_OUTER "/"
a_RUNTIME_DIR_BASE "/";
memcpy(xbuf, cmd, sizeof(cmd) -1);
memcpy(&xbuf[sizeof(cmd) -1], &uidbuf[4], uidbuflen +1);
res = system(xbuf);
if(!WIFEXITED(res) || WEXITSTATUS(res) != 0){
emsg = "unable to rm(1) -rf per user XDG_RUNTIME_DIR";
errno = EINVAL;
goto jerr;
}
/* This is the end .. */
goto jok;
}else{
/* Write out session counter */
res = snprintf(xbuf, sizeof xbuf, "%llu", sessions); /* xxx error? */
if(((datfd = openat(cwdfd, a_DAT_FILE,
(O_CREAT | O_TRUNC | O_WRONLY | O_SYNC | O_NOFOLLOW |
O_NOCTTY),
(S_IRUSR | S_IWUSR))) == -1) ||
write(datfd, xbuf, res) != res){
jecnt:
/* Ensure read above fails, so that henceforth session teardown is
* skipped for this user */
truncate(a_DAT_FILE, 0);
emsg = "counter file error, disabled session tracking for user";
goto jerr;
}
close(datfd);
datfd = -1;
}
}
/* When opening, we want to put environment variables, too */
if(isopen){
char *cp;
/* XDG_RUNTIME_DIR */
cp = wbuf;
cp = xbuf;
memcpy(cp, "XDG_RUNTIME_DIR=", sizeof("XDG_RUNTIME_DIR=") -1);
cp += sizeof("XDG_RUNTIME_DIR=") -1;
memcpy(cp, a_RUNTIME_DIR_OUTER, sizeof(a_RUNTIME_DIR_OUTER) -1);
@ -284,9 +396,9 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
memcpy(cp, a_RUNTIME_DIR_BASE, sizeof(a_RUNTIME_DIR_BASE) -1);
cp += sizeof(a_RUNTIME_DIR_BASE) -1;
*cp++ = '/';
memcpy(cp, &uidbuf[1], uidbuflen);
memcpy(cp, &uidbuf[4], uidbuflen);
if((res = pam_putenv(pamh, wbuf)) != PAM_SUCCESS)
if((res = pam_putenv(pamh, xbuf)) != PAM_SUCCESS)
goto jepam;
/* And the rest unless disallowed */
@ -318,7 +430,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
i = strlen(pwp->pw_dir);
for(adp = a_dirs; adp->name != NULL; ++adp){
cp = wbuf;
cp = xbuf;
memcpy(cp, adp->name, adp->len);
cp += adp->len;
if(*(src = adp->defval) == '\1'){
@ -328,7 +440,7 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
}
memcpy(cp, src, strlen(src) +1);
if((res = pam_putenv(pamh, wbuf)) != PAM_SUCCESS)
if((res = pam_putenv(pamh, xbuf)) != PAM_SUCCESS)
goto jepam;
}
}
@ -337,6 +449,10 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
jok:
res = PAM_SUCCESS;
jleave:
if(datfd != -1)
close(datfd);
if(cntrlfd != -1)
close(cntrlfd);
if(cwdfd != -1 && cwdfd != AT_FDCWD) /* >=0, but AT_FDCWD unspecified */
close(cwdfd);