NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: XDG_RUNTIME_DIR



RVP wrote in
 <alpine.NEB.2.22.394.2104242131560.16468%faeroes.freeshell.org@localhost>:
 |On Sat, 24 Apr 2021, RVP wrote:
 |
 |> 3. ~/.xinitrc
 |> if [ -x /usr/local/bin/dbus-launch -a -z "${DBUS_SESSION_BUS_ADDRESS}" ];
 |> then        eval $(dbus-launch --sh-syntax --exit-with-session)
 |> fi
 |>
 |
 |Correction: On NetBSD that would be:

If you run Linux you could also use the pam_xdg module i have
written.  For example my /etc/pam.d/common-session is

  session optional pam_xdg.so notroot

  session required    pam_unix.so quiet

and the stuff is as attached.  It handles the other directories of
the standard as well.  (Unfortunately i could not subscribe to the
RedHat hosted PAM list for inclusion upstream, i gave up after
trying several times.)
Could be ported to NetBSD with little effort (unfortunately PAM is
not really portable).

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)
/*@ 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).
 *
 * Copyright (c) 2021 Steffen Nurpmeso <steffen%sdaoden.eu@localhost>.
 * SPDX-License-Identifier: ISC
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* For these a leading \1 is replaced with struct passwd::pw_dir.
 * Be aware we use a stack buffer for storage */
#define a_XDG_DATA_HOME_DEF "\1/.local/share"
#define a_XDG_CONFIG_HOME_DEF "\1/.config"
#define a_XDG_DATA_DIRS_DEF "/usr/local/share:/usr/share"
#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. */
#define a_RUNTIME_DIR_BASE_MODE 0755 /* 0711? */

/* >8 -- 8< */

/*
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
*/
#define _GNU_SOURCE /* Always the same mess */

#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include <security/pam_modules.h>
#include <security/pam_ext.h>

/* _XOPEN_PATH_MAX POSIX 2008/Cor 1-2013 */
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

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"],
         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 stat st;
   struct passwd *pwp;
   char const *emsg;
   int cwdfd, only_runtime, notroot, res, uidbuflen;
   char const *user;
   (void)flags;

   user = "<unset>";
   cwdfd = -1;
   only_runtime = notroot = 0;

   /* Command line */
   if(isopen){
      for(; argc > 0; ++argv, --argc){
         if(!strcmp(argv[0], "runtime"))
            only_runtime = 1;
         else if(!strcmp(argv[0], "notroot"))
            notroot = 1;
         else if(!(flags & PAM_SILENT)){
            emsg = "command line";
            errno = EINVAL;
            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)
         ) != PAM_SUCCESS){
      user = "<lookup failed>";
      emsg = "cannot query PAM_USER name";
      goto jepam;
   }

   if((pwp = getpwnam(user)) == NULL){
      emsg = "host machine does not know about user";
      errno = EINVAL;
      goto jerr;
   }

   if(notroot && pwp->pw_uid == 0)
      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;
         }

         oumask = umask(0000);
         oegid = getegid();
         setegid(0);

         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;
         }

         setegid(oegid);
         umask(oumask);
      }
      /* 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(mkdirat(cwdfd, uidbuf, 0700) == -1 && errno != EEXIST){
         emsg = "cannot create per user XDG_RUNTIME_DIR";
         goto jerr;
      }

      /* Just chown it! */
      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;
      }
   }

   /* When opening, we want to put environment variables, too */
   /*if(isopen)*/{
      char *cp;

      /* XDG_RUNTIME_DIR */
      cp = wbuf;
      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);
      cp += sizeof(a_RUNTIME_DIR_OUTER) -1;
      *cp++ = '/';
      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);

      if((res = pam_putenv(pamh, wbuf)) != PAM_SUCCESS)
         goto jepam;

      /* And the rest */
      if(!only_runtime){
         struct adir{
            char const *name;
            size_t len;
            char const *defval;
         } const adirs[] = {
            {"XDG_DATA_HOME=", sizeof("XDG_DATA_HOME=") -1,
               a_XDG_DATA_HOME_DEF},
            {"XDG_CONFIG_HOME=", sizeof("XDG_CONFIG_HOME=") -1,
               a_XDG_CONFIG_HOME_DEF},
            {"XDG_DATA_DIRS=", sizeof("XDG_DATA_DIRS=") -1,
               a_XDG_DATA_DIRS_DEF},
            {"XDG_CONFIG_DIRS=", sizeof("XDG_CONFIG_DIRS=") -1,
               a_XDG_CONFIG_DIRS_DEF},
            {"XDG_CACHE_HOME=", sizeof("XDG_CACHE_HOME=") -1,
               a_XDG_CACHE_HOME_DEF},
            {NULL,0,NULL} /* xxx nelem */
         }, *adp;

         char const *src;
         size_t i;

         i = strlen(pwp->pw_dir);

         for(adp = adirs; adp->name != NULL; ++adp){
            cp = wbuf;
            memcpy(cp, adp->name, adp->len);
            cp += adp->len;
            if(*(src = adp->defval) == '\1'){
               memcpy(cp, pwp->pw_dir, i);
               cp += i;
               ++src;
            }
            memcpy(cp, src, strlen(src) +1);

            if((res = pam_putenv(pamh, wbuf)) != PAM_SUCCESS)
               goto jepam;
         }
      }
   }

jok:
   res = PAM_SUCCESS;
jleave:
   if(cwdfd != -1)
      close(cwdfd);

   return (res == PAM_SUCCESS) ? PAM_SUCCESS : PAM_SESSION_ERR;

jerr:
   pam_syslog(pamh, LOG_ERR, a_XDG ": user %s: %s: %s\n",
      user, emsg, strerror(errno));
   res = PAM_SESSION_ERR;
   goto jleave;

jepam:
   pam_syslog(pamh, LOG_ERR, a_XDG ": user %s: PAM failure: %s\n",
      user, pam_strerror(pamh, res));
   goto jleave;
}

int
pam_sm_open_session(pam_handle_t *pamh, int flags,
      int argc, const char **argv){
   return a_xdg(1, pamh, flags, argc, argv);
}

int
pam_sm_close_session(pam_handle_t *pamh, int flags,
      int argc, const char **argv){
   return a_xdg(0, pamh, flags, argc, argv);
}

int
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);
   return PAM_SERVICE_ERR;
}

int
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);
   return PAM_SERVICE_ERR;
}

int
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);
   return PAM_SERVICE_ERR;
}

/* s-it-mode */
.\"@ pam_xdg - manage XDG Base Directories (runtime dir life time, environ).
.\"
.\" Copyright (c) 2021 Steffen Nurpmeso <steffen%sdaoden.eu@localhost>.
.\" SPDX-License-Identifier: ISC
.\"
.\" Permission to use, copy, modify, and/or distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" 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
.Dt PAM_XDG 8
.Os
.
.
.Sh NAME
.Nm pam_xdg.so
.Nd PAM module that manages XDG Base Directories
.
.
.Sh SYNOPSIS
.
.Nm
.Op Ar rundir
.Op Ar notroot
.
.
.Sh DESCRIPTION
.
.Nm
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
.Lk https://specifications.\:freedesktop.\:org/basedir-\:\
spec/\:basedir-\:spec-\:latest.html "XDG Base Directory Specification"
into user sessions.
.
.Pp
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 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 and
perform no actions for root.
.
.Pp
In order to make use of this script, place the following in the control
file of desire under
.Pa /etc/pam.d ,
best maybe
.Pa /etc/pam.d/common-session
if that exists (possibly adjusting paths):
.
.Bd -literal -offset indent
session optional pam_xdg.so notroot
.Ed
.
.
.Sh "SEE ALSO"
.
.Xr pam.conf 5 ,
.Xr pam.d 8 ,
.Xr pam 8
.
.
.Sh AUTHORS
.
.An "Steffen Nurpmeso" Aq steffen%sdaoden.eu@localhost .
.
.\" s-ts-mode


Home | Main Index | Thread Index | Old Index