[crux-commits] ports/contrib (3.6): pam_xdg.c: update to 20210731

crux at crux.nu crux at crux.nu
Fri Jul 30 23:12:33 UTC 2021


commit eab8dce31fb76bf601842bb9eb5c27880fc45865
Author: Steffen Nurpmeso <steffen at sdaoden.eu>
Date:   Sat Jul 31 01:09:51 2021 +0200

    pam_xdg.c: update to 20210731

diff --git a/pam_xdg/.md5sum b/pam_xdg/.md5sum
deleted file mode 100644
index f1f334aef..000000000
--- a/pam_xdg/.md5sum
+++ /dev/null
@@ -1,3 +0,0 @@
-c9095bcca36ad19232016d2871e59546  makefile
-c8562d9eb117543c267e992a898ad617  pam_xdg.8
-09f7153e4300cf57d6a6a4bfe5fa0f3a  pam_xdg.c
diff --git a/pam_xdg/.signature b/pam_xdg/.signature
index 1c5851ad2..417c693bd 100644
--- a/pam_xdg/.signature
+++ b/pam_xdg/.signature
@@ -1,7 +1,7 @@
 untrusted comment: verify with /etc/ports/contrib.pub
-RWSagIOpLGJF3wSopQt7TvcJLCKXnqY7+rEYaiXbcNkFqhjUOyafFSwoNvjHD/yU0MwPrRqfsDzZj4+X//VOhTm+XpBl8CXh4Ag=
-SHA256 (Pkgfile) = 4dff33d08e9f6699bffa06a6137c05c9bb446827b9ccde2b376f8aa8495306d1
+RWSagIOpLGJF3z9vXj+fCpENPa65XMag5E6REcBr0Kj7oKCdaZuUaD45UgkBxtRvncVQRDW3rQ8But8+nu14Ag1LHZ7s0OHJXg4=
+SHA256 (Pkgfile) = 2c85163d75631d082443136b26d215babc716a9bf2521c616f58f4f1706ae4b4
 SHA256 (.footprint) = 56d789b652e6167f5fb93e1e6d48243e13f598c6d9a72705a8e54a003574ba31
-SHA256 (pam_xdg.c) = 4e9215a0f695920f04e925f55fd221167b2f376a75cc2668f9d4842540ccdeed
-SHA256 (pam_xdg.8) = 2929bcd6655d28127d386215d3d8c4fed6744b65c4866ac7e49d54cb438d9133
+SHA256 (pam_xdg.c) = 8569b0cbab468de159143bf58f9207dd9c3db204f44b7153ee410d0733526cea
+SHA256 (pam_xdg.8) = 999548e64134d26d0ffeec2931f15a905e4029e6b404e86825d02978d2393d7a
 SHA256 (makefile) = 2466f499c3e84fd821176371fa9ff78143bf94b9ec09fd9e654b35613e4ead7d
diff --git a/pam_xdg/Pkgfile b/pam_xdg/Pkgfile
index 79694314a..26f7c28e4 100644
--- a/pam_xdg/Pkgfile
+++ b/pam_xdg/Pkgfile
@@ -3,7 +3,7 @@
 # Maintainer:  Steffen Nurpmeso, steffen at sdaoden dot eu
 
 name=pam_xdg
-version=20210222
+version=20210731
 release=1
 source=($name.c $name.8 makefile)
 
diff --git a/pam_xdg/pam_xdg.8 b/pam_xdg/pam_xdg.8
index 67c62ad81..c4d0eba66 100644
--- a/pam_xdg/pam_xdg.8
+++ b/pam_xdg/pam_xdg.8
@@ -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 31, 2021
+.Dd July 29, 2021
 .Dt PAM_XDG 8
 .Os
 .
@@ -28,8 +28,9 @@
 .Sh SYNOPSIS
 .
 .Nm
-.Op Ar rundir
+.Op Ar runtime
 .Op Ar notroot
+.\".Op Ar track_user_sessions Op Ar per_user_lock
 .
 .
 .Sh DESCRIPTION
@@ -47,7 +48,7 @@ into user sessions.
 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
+.Ar runtime
 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 .
@@ -55,29 +56,58 @@ 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.
 .
 .Pp
-In order to make use of this script, place the following in the control
-file of desire under
+In order to make use of this module, place the following in the
+.Ql session
+part of the control file of desire under
 .Pa /etc/pam.d ,
-best maybe
+on Linux it may be
 .Pa /etc/pam.d/common-session
-if that exists (possibly adjusting paths):
+if that exists, on BSD's the files
+.Pa /etc/pam.d/system
+as well as
+.Pa /etc/pam.d/login ,
+.Pa /etc/pam.d/sshd
+and
+.Pa /etc/pam.d/su
+may be desirable, adjusting paths as necessary:
 .
 .Bd -literal -offset indent
-session optional pam_xdg.so notroot
+session optional pam_xdg.so notroot \"track_user_sessions
 .Ed
 .
 .
 .Sh "SEE ALSO"
 .
-.Xr pam.conf 5 ,
-.Xr pam.d 8 ,
-.Xr pam 8
+.Xr pam 3 ,
+.Xr pam.conf 5
 .
 .
 .Sh AUTHORS
 .
 .An "Steffen Nurpmeso" Aq steffen at sdaoden.eu .
 .
+.
+.Sh CAVEATS
+.
+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.
+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.
+.
 .\" s-ts-mode
diff --git a/pam_xdg/pam_xdg.c b/pam_xdg/pam_xdg.c
index 4c121e93c..54fd7178c 100644
--- a/pam_xdg/pam_xdg.c
+++ b/pam_xdg/pam_xdg.c
@@ -1,12 +1,8 @@
 /*@ pam_xdg - manage XDG Base Directories (runtime dir life time, environment).
- *@ 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: - according to XDG Base Directory Specification, v0.7.
- *@        - Linux-only (i think).
+ *@ See pam_xdg.8 for more.
+ *@ - According to XDG Base Directory Specification, v0.7.
+ *@ - Supports libpam (Linux) and *BSD OpenPAM.
+ *@ - Requires C preprocessor with __VA_ARGS__ support!
  *
  * Copyright (c) 2021 Steffen Nurpmeso <steffen at sdaoden.eu>.
  * SPDX-License-Identifier: ISC
@@ -32,11 +28,10 @@
 #define a_XDG_CONFIG_DIRS_DEF "/etc/xdg/"
 #define a_XDG_CACHE_HOME_DEF "\1/.cache"
 
-/* */
-#define a_XDG "pam_xdg"
-
-#define a_RUNTIME_DIR_OUTER "/run" /* This must exist already */
-#define a_RUNTIME_DIR_BASE "user" /* We create this as necessary, thus. */
+/* We create the outer directories as necessary (stack buffer storage!) */
+#define a_RUNTIME_DIR_OUTER "/run"
+#define a_RUNTIME_DIR_OUTER_MODE 0755
+#define a_RUNTIME_DIR_BASE "user"
 #define a_RUNTIME_DIR_BASE_MODE 0755 /* 0711? */
 
 /* >8 -- 8< */
@@ -52,57 +47,142 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <syslog.h>
 #include <unistd.h>
 
+#include <security/pam_appl.h>
 #include <security/pam_modules.h>
-#include <security/pam_ext.h>
+#ifdef OPENPAM_VERSION
+# include <security/openpam.h>
+#else
+# include <security/pam_ext.h>
+#endif
+
+#ifdef OPENPAM_VERSION
+#else
+# include <syslog.h>
+#endif
+
+/* Who are we? */
+#define a_XDG "pam_xdg"
 
 /* _XOPEN_PATH_MAX POSIX 2008/Cor 1-2013 */
 #ifndef PATH_MAX
 # define PATH_MAX 1024
 #endif
 
+/* */
+#ifdef O_SEARCH
+# define a_O_SEARCH O_SEARCH
+#elif defined O_PATH
+# define a_O_SEARCH O_PATH
+#else
+  /* Well, hardly, but not in practice so do not #error out */
+# define a_O_SEARCH 0
+#endif
+
+/* libpam / OpenPAM compat */
+#ifdef OPENPAM_VERSION
+# define a_LOG(HDL, LVL, ...) ((void)HDL, openpam_log(LVL, __VA_ARGS__))
+# define a_LOG_ERR PAM_LOG_ERROR
+# define a_LOG_NOTICE PAM_LOG_NOTICE
+#else
+# define a_LOG(HDL, LVL, ...) pam_syslog(HDL, LVL, __VA_ARGS__)
+# define a_LOG_ERR LOG_ERR
+# define a_LOG_NOTICE LOG_NOTICE
+#endif
+
+/* Because of complicated file locking, use one function with two exec paths */
 static int a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc,
       const char **argv);
 
 static int
 a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
-   char uidbuf[sizeof "18446744073709551615"],
+   enum a_flags{
+      a_NONE,
+      /* 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 */
+   };
+
+   struct a_dirtree{
+      char const *name;
+      int mode;
+   };
+
+   static struct a_dirtree const a_dirtree[] = {
+      {a_RUNTIME_DIR_OUTER, a_RUNTIME_DIR_OUTER_MODE},
+      {a_RUNTIME_DIR_BASE, a_RUNTIME_DIR_BASE_MODE},
+      {NULL, 0} /* XXX -> nelem/item/countof */
+   };
+   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")) |
+               sizeof(a_RUNTIME_DIR_BASE) + sizeof(".18446744073709551615")) |
             (sizeof("XDG_CONFIG_DIRS=") + PATH_MAX)
             ) +1];
-   struct stat st;
+   struct a_dirtree dt_user;
+   struct a_dirtree const *dtp;
    struct passwd *pwp;
    char const *emsg;
-   int cwdfd, only_runtime, notroot, res, uidbuflen;
+   int cwdfd, f, res, uidbuflen;
    char const *user;
-   (void)flags;
 
    user = "<unset>";
-   cwdfd = -1;
-   only_runtime = notroot = 0;
+   cwdfd = AT_FDCWD;
 
    /* Command line */
    if(isopen){
+      f = a_NONE;
+
       for(; argc > 0; ++argv, --argc){
          if(!strcmp(argv[0], "runtime"))
-            only_runtime = 1;
+            f |= a_RUNTIME;
+         else if(!strcmp(argv[0], "rundir")){ /* XXX COMPAT */
+            a_LOG(pamh, a_LOG_NOTICE,
+               a_XDG ": \"rundir\" was a misdocumentation of \"runtime\", "
+               "sorry for this");
+            f |= a_RUNTIME;
+         }
          else if(!strcmp(argv[0], "notroot"))
-            notroot = 1;
+            f |= a_NOTROOT;
+#if 0
+         else if(!strcmp(argv[0], "track_user_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;
             goto jerr;
          }
       }
-   }else
+
+#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
+   }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 */
    if((res = pam_get_item(pamh, PAM_USER, (void const**)&user)
@@ -113,102 +193,85 @@ a_xdg(int isopen, pam_handle_t *pamh, int flags, int argc, const char **argv){
    }
 
    if((pwp = getpwnam(user)) == NULL){
-      emsg = "host machine does not know about user";
+      emsg = "host does not know about user";
       errno = EINVAL;
       goto jerr;
    }
 
-   if(notroot && pwp->pw_uid == 0)
+   if((f & a_NOTROOT) && pwp->pw_uid == 0){
+      f |= a_SKIP_XDG;
       goto jok;
-
-   /* I admit all this is overly complicated and expensive */
-   if((cwdfd = open(a_RUNTIME_DIR_OUTER, (O_PATH | O_DIRECTORY | O_NOFOLLOW))
-         ) == -1){
-      emsg = "cannot obtain chdir(2) descriptor to " a_RUNTIME_DIR_OUTER;
-      goto jerr;
    }
 
-   /* We try create the base directory once as necessary */
-   /*if(isopen)*/{
-      gid_t oegid;
-      mode_t oumask;
-
-      res = 0;
-      while(fstatat(cwdfd, a_RUNTIME_DIR_BASE, &st, AT_SYMLINK_NOFOLLOW
-            ) == -1){
-         if(res++ != 0 || errno != ENOENT){
-            emsg = "base directory " a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE
-                  " not accessible";
-            goto jerr;
-         }
+   /* Our lockfile and per-user directory name */
+   uidbuflen = snprintf(uidbuf, sizeof(uidbuf), ".%lu",
+         (unsigned long)pwp->pw_uid);
 
-         oumask = umask(0000);
-         oegid = getegid();
-         setegid(0);
+   dt_user.name = &uidbuf[1];
+   dt_user.mode = 0700;
 
-         if(mkdirat(cwdfd, a_RUNTIME_DIR_BASE, a_RUNTIME_DIR_BASE_MODE
-               ) == -1 && errno != EEXIST){
-            emsg = "cannot create base directory "
-                  a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE;
-            goto jerr;
-         }
+   /* Handle tree.  On *BSD outermost may not exist! */
+   for(/*f &= ~a_MPV,*/ dtp = a_dirtree;;){
+      int e;
+      gid_t oegid;
+      mode_t oumask;
 
-         setegid(oegid);
-         umask(oumask);
+      if((res = openat(cwdfd, dtp->name,
+               (a_O_SEARCH | O_DIRECTORY | O_NOFOLLOW))) != -1){
+         if(cwdfd != AT_FDCWD)
+            close(cwdfd); /* XXX error hdl */
+         cwdfd = res;
+
+         if(dtp == &dt_user)
+            break;
+         else if((++dtp)->name == NULL)
+            dtp = &dt_user;
+         f &= ~a_MPV;
+         continue;
       }
-      /* Not worth doing S_ISDIR(st.st_mode), O_DIRECTORY will bail next */
-   }
-
-   if((res = openat(cwdfd, a_RUNTIME_DIR_BASE,
-         (O_PATH | O_DIRECTORY | O_NOFOLLOW))) == -1){
-      emsg = "cannot obtain chdir(2) descriptor to " a_RUNTIME_DIR_OUTER "/"
-            a_RUNTIME_DIR_BASE;
-      goto jerr;
-   }
-   close(cwdfd);
-   cwdfd = res;
 
-   /* 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((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(!isopen)
+         /* Someone removed the entire directory tree while sessions were open!
+          * Silently out!?! */
+         goto jok;
 
-      if(mkdirat(cwdfd, uidbuf, 0700) == -1 && errno != EEXIST){
-         emsg = "cannot create per user XDG_RUNTIME_DIR";
+      /* We try creating the directories once as necessary */
+      if((f & a_MPV) || errno != ENOENT){
+         emsg = "cannot obtain chdir(2) descriptor (within) tree "
+               a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE;
          goto jerr;
       }
-
+      f |= a_MPV;
+
+      oumask = umask(0000);
+      oegid = getegid();
+      setegid(0);
+         res = mkdirat(cwdfd, dtp->name, dtp->mode);
+         e = (res == -1) ? errno : 0;
+      setegid(oegid);
+      umask(oumask);
+
+      if(res == -1){
+         if(e != EEXIST){
+            emsg = "cannot create directory (within) tree "
+                  a_RUNTIME_DIR_OUTER "/" a_RUNTIME_DIR_BASE;
+            goto jerr;
+         }
+      }else if(cwdfd == AT_FDCWD)
+         a_LOG(pamh, a_LOG_NOTICE,
+            a_XDG ": " a_RUNTIME_DIR_OUTER " did not exist, but should be "
+            "(a mount point of) volatile storage!");
       /* Just chown it! */
-      if(fchownat(cwdfd, uidbuf, pwp->pw_uid, pwp->pw_gid,
-            AT_SYMLINK_NOFOLLOW) == -1){
+      else if(dtp == &dt_user &&
+            fchownat(cwdfd, &uidbuf[1], pwp->pw_uid, pwp->pw_gid,
+               AT_SYMLINK_NOFOLLOW) == -1){
          emsg = "cannot chown(2) per user XDG_RUNTIME_DIR";
          goto jerr;
       }
    }
 
    /* When opening, we want to put environment variables, too */
-   /*if(isopen)*/{
+   if(isopen){
       char *cp;
 
       /* XDG_RUNTIME_DIR */
@@ -221,18 +284,20 @@ jeurd:
       memcpy(cp, a_RUNTIME_DIR_BASE, sizeof(a_RUNTIME_DIR_BASE) -1);
       cp += sizeof(a_RUNTIME_DIR_BASE) -1;
       *cp++ = '/';
-      memcpy(cp, uidbuf, uidbuflen +1);
+      memcpy(cp, &uidbuf[1], uidbuflen);
 
       if((res = pam_putenv(pamh, wbuf)) != PAM_SUCCESS)
          goto jepam;
 
-      /* And the rest */
-      if(!only_runtime){
-         struct adir{
+      /* And the rest unless disallowed */
+      if(!(f & a_RUNTIME)){
+         struct a_dir{
             char const *name;
             size_t len;
             char const *defval;
-         } const adirs[] = {
+         };
+
+         static struct a_dir const a_dirs[] = {
             {"XDG_DATA_HOME=", sizeof("XDG_DATA_HOME=") -1,
                a_XDG_DATA_HOME_DEF},
             {"XDG_CONFIG_HOME=", sizeof("XDG_CONFIG_HOME=") -1,
@@ -243,15 +308,16 @@ jeurd:
                a_XDG_CONFIG_DIRS_DEF},
             {"XDG_CACHE_HOME=", sizeof("XDG_CACHE_HOME=") -1,
                a_XDG_CACHE_HOME_DEF},
-            {NULL,0,NULL} /* xxx nelem */
-         }, *adp;
+            {NULL,0,NULL} /* XXX -> nelem/item/countof */
+         };
 
          char const *src;
+         struct a_dir const *adp;
          size_t i;
 
          i = strlen(pwp->pw_dir);
 
-         for(adp = adirs; adp->name != NULL; ++adp){
+         for(adp = a_dirs; adp->name != NULL; ++adp){
             cp = wbuf;
             memcpy(cp, adp->name, adp->len);
             cp += adp->len;
@@ -271,20 +337,23 @@ jeurd:
 jok:
    res = PAM_SUCCESS;
 jleave:
-   if(cwdfd != -1)
+   if(cwdfd != -1 && cwdfd != AT_FDCWD) /* >=0, but AT_FDCWD unspecified */
       close(cwdfd);
 
+   f_saved = f;
    return (res == PAM_SUCCESS) ? PAM_SUCCESS : PAM_SESSION_ERR;
 
 jerr:
-   pam_syslog(pamh, LOG_ERR, a_XDG ": user %s: %s: %s\n",
+   a_LOG(pamh, a_LOG_ERR, a_XDG ": user %s: %s: %s\n",
       user, emsg, strerror(errno));
+   f |= a_SKIP_XDG;
    res = PAM_SESSION_ERR;
    goto jleave;
 
 jepam:
-   pam_syslog(pamh, LOG_ERR, a_XDG ": user %s: PAM failure: %s\n",
+   a_LOG(pamh, a_LOG_ERR, a_XDG ": user %s: PAM failure: %s\n",
       user, pam_strerror(pamh, res));
+   f |= a_SKIP_XDG;
    goto jleave;
 }
 
@@ -305,7 +374,9 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv){
    (void)flags;
    (void)argc;
    (void)argv;
-   pam_syslog(pamh, LOG_NOTICE, "pam_sm_acct_mgmt not used by " a_XDG);
+
+   a_LOG(pamh, a_LOG_NOTICE, a_XDG ": pam_sm_acct_mgmt not used");
+
    return PAM_SERVICE_ERR;
 }
 
@@ -314,7 +385,9 @@ pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv){
    (void)flags;
    (void)argc;
    (void)argv;
-   pam_syslog(pamh, LOG_NOTICE, "pam_sm_setcred not used by " a_XDG);
+
+   a_LOG(pamh, a_LOG_NOTICE, a_XDG ": pam_sm_setcred not used");
+
    return PAM_SERVICE_ERR;
 }
 
@@ -323,7 +396,9 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv){
    (void)flags;
    (void)argc;
    (void)argv;
-   pam_syslog(pamh, LOG_NOTICE, "pam_sm_chauthtok not used by " a_XDG);
+
+   a_LOG(pamh, a_LOG_NOTICE, a_XDG ": pam_sm_chauthtok not used");
+
    return PAM_SERVICE_ERR;
 }
 


More information about the crux-commits mailing list