From nobody Mon Feb 26 17:35:57 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Tk77f1Gs0z5BqSS; Mon, 26 Feb 2024 17:35:58 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Tk77f0jrqz4fkf; Mon, 26 Feb 2024 17:35:58 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1708968958; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NoP1uL6EnIuIlHftWjiBITJJm9mYoBnvMYrVIuBSrx0=; b=xrr+81mbUTEsbOqdA/8Wfqkd232nRt2AVml0G0LL4DM2HbtGHiMf12nOY06ZgUQE2lnWtM CDBfiImnHEqdfhV4q/dENjzLB2q+bNtbuqWLju1K8ITck2JK0edrKLQ5hQre8V2WBcYrQ9 WiS4BrSHIdNe5RaDX+vkMUzwB8dL8bVDOT+Ooc3ByWBpv8av9dg3LkipzWL2gy0yUsBOns 6DYeuoH2mFQdJR4J9ubEt/zHULlQGCAhLCeU2O0+fMfSYWI4/xEkYFioNgWpoYSVjsAmEp cByyHvqBtqfLj7sqTWbje+uLh8jRPFhj8/10hraDiG6vqNZXuWg4yL6HHhQXNg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1708968958; a=rsa-sha256; cv=none; b=gPc7hY9oIv4bOaWDw/vNoZKLwXampVcYjk7chPCXWkF4LyMFPMc5jVzGHKScgPX8TUPp8D Ky6NKLP3qBbbsMbhHwU8kUmxJ7DXLRVw1O1JrpiuhLPZZDUSCCDSBHl6F4BBgxR9mMX8To ZD8XLifod0vpUUeLWY1tMk7Hgzg1Nv0b6iKGCuCQzu8jJhCvhVQK0cheWVNQbSt0eF7IeN KhxWHtqY0i3bVfdCP6J3XNkTm/Qspnb3sD8DLFlJ9M4wzuy7y+oOB6AOgcUdcgXKJObSrq NS0Y5/OS/uC29XJQf+kBpW2F8KT1OZs8V15u6b/bQWxTN2iccqhPNZ4Qc6U3+A== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1708968958; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NoP1uL6EnIuIlHftWjiBITJJm9mYoBnvMYrVIuBSrx0=; b=iYIPI3T33QjRnZrf+3mvc6jDUlgj+BC9lH20joaeYIFO+tiKrb53AdeaA3y6FnkEaN+ubL 5DwkXLV37MZdzoHbZe0uixxkrv7A6WGLtlvmZ59CnIJ9Prh8H+K8S11cffv9wNHXRMfj8W M2KLZAcG8ArcRqnPs7gcLHBo8SDXWjmqSMAkVd0WfuwlSCVVEOf68oyWwhXChvfjBEPWOq 1ozZu3ufPcpYdNglDHl70Xx9jzDh2DmUdmqL5BrcUa8ETxsxO+4J62zYprUyXEJ6twKZgf l4PROl8YFDFPf64BvYURh5UM8U9wQKICyhl9sUF8fyTDuJGRgUvnHqXQ6vZKuQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Tk77d6vNJzdHP; Mon, 26 Feb 2024 17:35:57 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 41QHZvwb027961; Mon, 26 Feb 2024 17:35:57 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 41QHZvL1027958; Mon, 26 Feb 2024 17:35:57 GMT (envelope-from git) Date: Mon, 26 Feb 2024 17:35:57 GMT Message-Id: <202402261735.41QHZvL1027958@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Emmanuel Vadot Subject: git: 6e69612d5df1 - main - pam: Add pam_xdg module List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: manu X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 6e69612d5df1c1d5bd86990ea4d9a170c030b292 Auto-Submitted: auto-generated The branch main has been updated by manu: URL: https://cgit.FreeBSD.org/src/commit/?id=6e69612d5df1c1d5bd86990ea4d9a170c030b292 commit 6e69612d5df1c1d5bd86990ea4d9a170c030b292 Author: Emmanuel Vadot AuthorDate: 2024-02-21 14:51:05 +0000 Commit: Emmanuel Vadot CommitDate: 2024-02-26 17:34:52 +0000 pam: Add pam_xdg module This is a module to setup the XDG directories and environment variables. For now the only usage is to have a XDG_RUNTIME_DIR environment setup at user login. All other environment variable have a default fallback so no need to export them in this module. The directory is created according to the XDG Base directory specification. The default base directory is /var/run/xdg/ but can be configured using the runtime_dir= module option. According to the spec the directory *must* not survive a reboot so adding var_run_enable="YES" to rc.conf is highly recommanded. Reviewed by: des, pauamma (manpages) Differential Revision: https://reviews.freebsd.org/D44011 Sponsored by: Beckhoff Automation GmbH & Co. KG --- lib/libpam/modules/modules.inc | 1 + lib/libpam/modules/pam_xdg/Makefile | 6 + lib/libpam/modules/pam_xdg/pam_xdg.8 | 56 +++++++ lib/libpam/modules/pam_xdg/pam_xdg.c | 311 +++++++++++++++++++++++++++++++++++ 4 files changed, 374 insertions(+) diff --git a/lib/libpam/modules/modules.inc b/lib/libpam/modules/modules.inc index ddbb326f0312..f3ab65333f4f 100644 --- a/lib/libpam/modules/modules.inc +++ b/lib/libpam/modules/modules.inc @@ -30,3 +30,4 @@ MODULES += pam_ssh .endif MODULES += pam_tacplus MODULES += pam_unix +MODULES += pam_xdg \ No newline at end of file diff --git a/lib/libpam/modules/pam_xdg/Makefile b/lib/libpam/modules/pam_xdg/Makefile new file mode 100644 index 000000000000..2a470e0850bf --- /dev/null +++ b/lib/libpam/modules/pam_xdg/Makefile @@ -0,0 +1,6 @@ + +LIB= pam_xdg +SRCS= pam_xdg.c +MAN= pam_xdg.8 + +.include diff --git a/lib/libpam/modules/pam_xdg/pam_xdg.8 b/lib/libpam/modules/pam_xdg/pam_xdg.8 new file mode 100644 index 000000000000..1a8b53def051 --- /dev/null +++ b/lib/libpam/modules/pam_xdg/pam_xdg.8 @@ -0,0 +1,56 @@ +.\" * SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2024 Beckhoff Automation GmbH & Co. KG +.\" +.\" * Redistribution and use in source and binary forms, with or without +.\" * modification, are permitted provided that the following conditions +.\" * are met: +.\" * 1. Redistributions of source code must retain the above copyright +.\" * notice, this list of conditions and the following disclaimer. +.\" * 2. Redistributions in binary form must reproduce the above copyright +.\" * notice, this list of conditions and the following disclaimer in the +.\" * documentation and/or other materials provided with the distribution. +.\" * +.\" * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" * SUCH DAMAGE. +.Dd February 21, 2024 +.Dt PAM_XDG 8 +.Os +.Sh NAME +.Nm pam_xdg +.Nd XDG PAM module +.Sh SYNOPSIS +.Op Ar service-name +.Ar module-type +.Ar control-flag +.Pa pam_xdg +.Op Ar arguments +.Sh DESCRIPTION +The xdg service module for PAM sets up the runtime directory according +to the XDG specifications. +.Pp +By default the directory is created under +.Pa /var/run/xdg/ . +.Pp +The following option may be passed to the authentication module: +.Bl -tag -width ".Cm runtime_dir" +.It Cm runtime_dir Ns = Ns Ar directory +Use an alternate base directory +.El +.Sh SEE ALSO +.Xr pam 3 , +.Xr pam.conf 5 +.Sh AUTHORS +The +.Nm +module and this manual page were written by +.An Emmanuel Vadot Aq Mt manu@FreeBSD.org . diff --git a/lib/libpam/modules/pam_xdg/pam_xdg.c b/lib/libpam/modules/pam_xdg/pam_xdg.c new file mode 100644 index 000000000000..40012fe463e0 --- /dev/null +++ b/lib/libpam/modules/pam_xdg/pam_xdg.c @@ -0,0 +1,311 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAM_SM_SESSION + +#include +#include +#include + +#define BASE_RUNTIME_DIR_PREFIX "/var/run/xdg" +#define RUNTIME_DIR_PREFIX runtime_dir_prefix != NULL ? runtime_dir_prefix : BASE_RUNTIME_DIR_PREFIX + +#define RUNTIME_DIR_PREFIX_MODE 0711 +#define RUNTIME_DIR_MODE 0700 /* XDG spec */ + +#define XDG_MAX_SESSION 100 /* Arbitrary limit because we need one */ + +static int +_pam_xdg_open(pam_handle_t *pamh, int flags __unused, + int argc __unused, const char *argv[] __unused) +{ + struct passwd *passwd; + const char *user; + const char *runtime_dir_prefix; + struct stat sb; + char *runtime_dir = NULL; + char *xdg_session_file; + int rv, rt_dir_prefix, rt_dir, session_file, i; + + session_file = -1; + rt_dir_prefix = -1; + runtime_dir_prefix = openpam_get_option(pamh, "runtime_dir_prefix"); + + /* Get user info */ + rv = pam_get_item(pamh, PAM_USER, (const void **)&user); + if (rv != PAM_SUCCESS) { + PAM_VERBOSE_ERROR("Can't get user information"); + goto out; + } + if ((passwd = getpwnam(user)) == NULL) { + PAM_VERBOSE_ERROR("Can't get user information"); + rv = PAM_SESSION_ERR; + goto out; + } + + /* Open or create the base xdg directory */ + rt_dir_prefix = open(RUNTIME_DIR_PREFIX, O_DIRECTORY | O_NOFOLLOW); + if (rt_dir_prefix < 0) { + rt_dir_prefix = mkdir(RUNTIME_DIR_PREFIX, RUNTIME_DIR_PREFIX_MODE); + if (rt_dir_prefix != 0) { + PAM_VERBOSE_ERROR("Can't mkdir %s", RUNTIME_DIR_PREFIX); + rv = PAM_SESSION_ERR; + goto out; + } + rt_dir_prefix = open(RUNTIME_DIR_PREFIX, O_DIRECTORY | O_NOFOLLOW); + } + + /* Open or create the user xdg directory */ + rt_dir = openat(rt_dir_prefix, user, O_DIRECTORY | O_NOFOLLOW); + if (rt_dir < 0) { + rt_dir = mkdirat(rt_dir_prefix, user, RUNTIME_DIR_MODE); + if (rt_dir != 0) { + PAM_VERBOSE_ERROR("mkdir: %s/%s (%d)", RUNTIME_DIR_PREFIX, user, rt_dir); + rv = PAM_SESSION_ERR; + goto out; + } + rv = fchownat(rt_dir_prefix, user, passwd->pw_uid, passwd->pw_gid, 0); + if (rv != 0) { + PAM_VERBOSE_ERROR("fchownat: %s/%s (%d)", RUNTIME_DIR_PREFIX, user, rv); + rv = unlinkat(rt_dir_prefix, user, AT_REMOVEDIR); + if (rv == -1) + PAM_VERBOSE_ERROR("unlinkat: %s/%s (%d)", RUNTIME_DIR_PREFIX, user, errno); + rv = PAM_SESSION_ERR; + goto out; + } + } else { + /* Check that the already create dir is correctly owned */ + rv = fstatat(rt_dir_prefix, user, &sb, 0); + if (rv == -1) { + PAM_VERBOSE_ERROR("fstatat %s/%s failed (%d)", RUNTIME_DIR_PREFIX, user, errno); + rv = PAM_SESSION_ERR; + goto out; + } + if (sb.st_uid != passwd->pw_uid || + sb.st_gid != passwd->pw_gid) { + PAM_VERBOSE_ERROR("%s/%s isn't owned by %d:%d\n", RUNTIME_DIR_PREFIX, user, passwd->pw_uid, passwd->pw_gid); + rv = PAM_SESSION_ERR; + goto out; + } + /* Test directory mode */ + if ((sb.st_mode & 0x1FF) != RUNTIME_DIR_MODE) { + PAM_VERBOSE_ERROR("%s/%s have wrong mode\n", RUNTIME_DIR_PREFIX, user); + rv = PAM_SESSION_ERR; + goto out; + } + } + + /* Setup the environment variable */ + asprintf(&runtime_dir, "XDG_RUNTIME_DIR=%s/%s", RUNTIME_DIR_PREFIX, user); + rv = pam_putenv(pamh, runtime_dir); + if (rv != PAM_SUCCESS) { + PAM_VERBOSE_ERROR("pam_putenv: failed (%d)", rv); + rv = PAM_SESSION_ERR; + goto out; + } + + /* Setup the session count file */ + for (i = 0; i < XDG_MAX_SESSION; i++) { + asprintf(&xdg_session_file, "%s/xdg_session.%d", user, i); + printf("Trying to open %s\n", xdg_session_file); + session_file = openat(rt_dir_prefix, xdg_session_file, O_CREAT | O_EXCL, RUNTIME_DIR_MODE); + free(xdg_session_file); + if (session_file >= 0) + break; + } + if (session_file < 0) { + PAM_VERBOSE_ERROR("Too many sessions"); + rv = PAM_SESSION_ERR; + goto out; + } + +out: + if (session_file >= 0) + close(session_file); + if (rt_dir_prefix >= 0) + close(rt_dir_prefix); + + if (runtime_dir) + free(runtime_dir); + return (rv); +} + +static int +remove_dir(int fd) +{ + DIR *dirp; + struct dirent *dp; + + dirp = fdopendir(fd); + if (dirp == NULL) + return (-1); + + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_type == DT_DIR) { + int dirfd; + + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + dirfd = openat(fd, dp->d_name, 0); + remove_dir(dirfd); + close(dirfd); + unlinkat(fd, dp->d_name, AT_REMOVEDIR); + continue; + } + unlinkat(fd, dp->d_name, 0); + } + + return (0); +} + +static int +_pam_xdg_close(pam_handle_t *pamh __unused, int flags __unused, + int argc __unused, const char *argv[] __unused) +{ + struct passwd *passwd; + const char *user; + const char *runtime_dir_prefix; + struct stat sb; + char *xdg_session_file; + int rv, rt_dir_prefix, rt_dir, session_file, i; + + rt_dir = -1; + rt_dir_prefix = -1; + runtime_dir_prefix = openpam_get_option(pamh, "runtime_dir_prefix"); + + /* Get user info */ + rv = pam_get_item(pamh, PAM_USER, (const void **)&user); + if (rv != PAM_SUCCESS) { + PAM_VERBOSE_ERROR("Can't get user information"); + goto out; + } + if ((passwd = getpwnam(user)) == NULL) { + PAM_VERBOSE_ERROR("Can't get user information"); + rv = PAM_SESSION_ERR; + goto out; + } + + /* Open the xdg base directory */ + rt_dir_prefix = open(RUNTIME_DIR_PREFIX, O_DIRECTORY | O_NOFOLLOW); + if (rt_dir_prefix < 0) { + PAM_VERBOSE_ERROR("open: %s failed (%d)\n", runtime_dir_prefix, rt_dir_prefix); + rv = PAM_SESSION_ERR; + goto out; + } + /* Check that the already created dir is correctly owned */ + rv = fstatat(rt_dir_prefix, user, &sb, 0); + if (rv == -1) { + PAM_VERBOSE_ERROR("fstatat %s/%s failed (%d)", RUNTIME_DIR_PREFIX, user, errno); + rv = PAM_SESSION_ERR; + goto out; + } + if (sb.st_uid != passwd->pw_uid || + sb.st_gid != passwd->pw_gid) { + PAM_VERBOSE_ERROR("%s/%s isn't owned by %d:%d\n", RUNTIME_DIR_PREFIX, user, passwd->pw_uid, passwd->pw_gid); + rv = PAM_SESSION_ERR; + goto out; + } + /* Test directory mode */ + if ((sb.st_mode & 0x1FF) != RUNTIME_DIR_MODE) { + PAM_VERBOSE_ERROR("%s/%s have wrong mode\n", RUNTIME_DIR_PREFIX, user); + rv = PAM_SESSION_ERR; + goto out; + } + + /* Open the user xdg directory */ + rt_dir = openat(rt_dir_prefix, user, O_DIRECTORY | O_NOFOLLOW); + if (rt_dir < 0) { + PAM_VERBOSE_ERROR("openat: %s/%s failed (%d)\n", RUNTIME_DIR_PREFIX, user, rt_dir_prefix); + rv = PAM_SESSION_ERR; + goto out; + } + + /* Get the last session file created */ + for (i = XDG_MAX_SESSION; i >= 0; i--) { + asprintf(&xdg_session_file, "%s/xdg_session.%d", user, i); + session_file = openat(rt_dir_prefix, xdg_session_file, 0); + if (session_file >= 0) { + unlinkat(rt_dir_prefix, xdg_session_file, 0); + free(xdg_session_file); + break; + } + free(xdg_session_file); + } + if (session_file < 0) { + PAM_VERBOSE_ERROR("Can't find session number\n"); + rv = PAM_SESSION_ERR; + goto out; + } + close(session_file); + + /* Final cleanup if last user session */ + if (i == 0) { + remove_dir(rt_dir); + if (unlinkat(rt_dir_prefix, user, AT_REMOVEDIR) != 0) { + PAM_VERBOSE_ERROR("Can't cleanup %s/%s\n", runtime_dir_prefix, user); + rv = PAM_SESSION_ERR; + goto out; + } + } + + rv = PAM_SUCCESS; +out: + if (rt_dir >= 0) + close(rt_dir); + if (rt_dir_prefix >= 0) + close(rt_dir_prefix); + return (rv); +} + +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + + return (_pam_xdg_open(pamh, flags, argc, argv)); +} + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char *argv[]) +{ + + return (_pam_xdg_close(pamh, flags, argc, argv)); +} + +PAM_MODULE_ENTRY("pam_xdg");