forked from ports/contrib
pam_xdg: 20210801: bring back (optional) session support
This commit is contained in:
parent
0360f776d7
commit
63f8a96b40
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user