- Update 0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch

with bsc#1231699 improvements for security fix CVE-2024-47191

OBS-URL: https://build.opensuse.org/package/show/security/oath-toolkit?expand=0&rev=41
This commit is contained in:
Torsten Gruner 2024-10-16 17:33:55 +00:00 committed by Git OBS Bridge
commit f3299b3ea7
9 changed files with 1552 additions and 0 deletions

23
.gitattributes vendored Normal file
View File

@ -0,0 +1,23 @@
## Default LFS
*.7z filter=lfs diff=lfs merge=lfs -text
*.bsp filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.gem filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.lz filter=lfs diff=lfs merge=lfs -text
*.lzma filter=lfs diff=lfs merge=lfs -text
*.obscpio filter=lfs diff=lfs merge=lfs -text
*.oxt filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.rpm filter=lfs diff=lfs merge=lfs -text
*.tbz filter=lfs diff=lfs merge=lfs -text
*.tbz2 filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.txz filter=lfs diff=lfs merge=lfs -text
*.whl filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.osc

View File

@ -0,0 +1,914 @@
From 345ae06e0f698bdb1e9b4529e5a882f12df04426 Mon Sep 17 00:00:00 2001
From: Matthias Gerstner <matthias.gerstner@suse.de>
Date: Wed, 16 Oct 2024 09:58:35 +0200
Subject: [PATCH] usersfile: fix potential security issues in PAM module
With the addition of the possibility to place a usersfile also into
a user's home directory via variable expansion of ${HOME} and ${USER} in
the `usersfile=` path specification, security issues sneaked in. The PAM
process usually runs with root privileges. The file operations in an
unprivileged user's home directory follow symlinks both when reading and
creating files, allowing for a potential local root exploit, because of
the `fchown()` performed on the newly created usersfile.
The situation is not that easy to fix, since the current PAM module
configuration does not indicate explicitly whether the usersfile will be
placed in an unprivileged or in a privileged location. It is advisable
to drop privileges to the owner of the usersfile, if we're running as
root. To determine the ownership of the usersfile, it first has to be
opened in a safe way, though.
This change addresses the issue by introducing a usersfile_ctx datatype
which holds state information about the target usersfile. The new
function `safe_open_usersfile()` will open the target path in a safe
way, rejecting any symlinks on the way. The function also rejects any
world-writable directories or files, which would generally be a bad idea
to have in the usersfile path.
The global `umask()` alteration is dropped in favor of using an unnamed
temporary file to achieve the proper file permissions of a newly created
usersfile. Since the target mode is 0600, the umask would need to be
really awkward anyway to change the outcome. `fchown()` is no longer
called on the new file, assuming we are already running with the correct
credentials.
The locking logic of the existing code is incomplete, because the
initial reading of the usersfile is performed without locking. Only
during updating of the file, the lock is obtained. I believe this can
lead to inconsistencies. Also the current code unlinks the lockfile
after its use, which opens a race condition making the lock again
unreliable.
The creation of the lockfile in the directory containing the usersfile
is somewhat unfortunate. Lockfiles are runtime state data that should go
into /run or a shared sticky-bit directory. It is unclear whether mixed
root and non-root accesses need to be synchronized (probably). An
advantage of using the location of the usersfile is that if the
usersfile should be placed on a network share (NFS, CIFS), that the
locking can theoretically happen across the network.
This patch aims to make the locking complete by acquiring it before
parsing the actual usersfile. To prevent cluttering of users' home
directories no separate lockfile is used anymore, but the usersfile
itself it used for locking. This involves some extra complexity, since
even after acquiring the lock, the actual usersfile on disk might have
been replaced by a new one in the meantime. This situation needs to be
detected and recovered from.
In the PAM module context the unprivileged user could try to DoS the
privileged PAM stack, by taking the lock and never releasing it.
Therefore a polling loop is implemented that fails after 15 seconds of
failing to obtain the lock. Unfortunately there exists no lock with
timeout API, thus it needs to be polled.
Instead of the POSIX compatible fcntl(F_SETLK) locking API this patch
switches to the Linux specific fcntl(F_OFD_SETLK) locking. The reason
for this is that locks obtained with F_SETLK cannot be inherited to
child processes, which we need to do now. flock() would also have been
an alternative, but it has unfortunate properties if the lockfile should
be located on a network file system.
This is a follow-up version of the patch that addresses a few
shortcomings of the originally shared patch:
- setgroups() is invoked to drop supplementary group membership. Without
this the forked process wrongly retains the root group membership.
Since the privilege drop is only an additional hardening measure, the
original patch should still prove safe.
- the usersfile is checked for additional hard-links; if the link count
is not zero, then the file is rejected. This prevents possible hard
link attacks on the end of the unprivileged user. With the Linux
kernel sysctl protected_hardlinks set to 1 (the usual default on most
distributions), this attack will not work either way.
- O_NOCTTY has been added to the open() call of the usersfile. This
makes this aspect explicit, although the code already checks that the
file is a regular file, so the situation shouldn't arise in the first
place.
---
liboath/errors.c | 7 +-
liboath/oath.h.in | 8 +-
liboath/usersfile.c | 706 ++++++++++++++++++++++++++++++++++++--------
3 files changed, 593 insertions(+), 128 deletions(-)
diff --git a/liboath/errors.c b/liboath/errors.c
index c1725f9..67ed008 100644
--- a/liboath/errors.c
+++ b/liboath/errors.c
@@ -58,7 +58,12 @@ static const err_t errors[] = {
ERR (OATH_FILE_SYNC_ERROR, "System error when syncing file to disk"),
ERR (OATH_FILE_CLOSE_ERROR, "System error when closing file"),
ERR (OATH_FILE_CHOWN_ERROR, "System error when changing file ownership"),
- ERR (OATH_FILE_STAT_ERROR, "System error when getting file status")
+ ERR (OATH_FILE_STAT_ERROR, "System error when getting file status"),
+ ERR (OATH_FILE_OPEN_ERROR, "System error trying to open file"),
+ ERR (OATH_FORK_ERROR, "System error when forking a process"),
+ ERR (OATH_WAIT_ERROR, "System error when waiting for a process"),
+ ERR (OATH_SETUID_ERROR, "System error when setting process UID"),
+ ERR (OATH_SETGID_ERROR, "System error when setting process GID")
};
/**
diff --git a/liboath/oath.h.in b/liboath/oath.h.in
index b8b4fbd..5ad7045 100644
--- a/liboath/oath.h.in
+++ b/liboath/oath.h.in
@@ -152,9 +152,15 @@ extern "C"
OATH_FILE_CLOSE_ERROR = -25,
OATH_FILE_CHOWN_ERROR = -26,
OATH_FILE_STAT_ERROR = -27,
+ OATH_FILE_OPEN_ERROR = -28,
+ OATH_FORK_ERROR = -29,
+ OATH_WAIT_ERROR = -30,
+ OATH_SETUID_ERROR = -31,
+ OATH_SETGID_ERROR = -32,
+ OATH_SETGROUPS_ERROR = -33,
/* When adding anything here, update OATH_LAST_ERROR, errors.c
and tests/tst_errors.c. */
- OATH_LAST_ERROR = -27
+ OATH_LAST_ERROR = -34
} oath_rc;
/* Global */
diff --git a/liboath/usersfile.c b/liboath/usersfile.c
index 3b139d1..c9625f4 100644
--- a/liboath/usersfile.c
+++ b/liboath/usersfile.c
@@ -29,10 +29,239 @@
#include <unistd.h> /* For ssize_t. */
#include <fcntl.h> /* For fcntl. */
#include <errno.h> /* For errno. */
+#include <limits.h> /* For PATH_MAX & friends. */
#include <sys/stat.h> /* For S_IRUSR, S_IWUSR. */
+#include <sys/wait.h> /* For wait */
+#include <sys/stat.h> /* For stat */
+#include <grp.h> /* For setgroups */
#ifndef _WIN32
+struct usersfile_ctx {
+ const char *path;
+ const char *basename; /* basename of path, points into `path` */
+ int parent_fd; /* file descriptor for the parent directory of the usersfile */
+ int fd; /* file descriptor for the usersfile */
+ struct stat st; /* stat information for the usersfile */
+};
+
+/*
+ * Upgrade a file descriptor opened with O_PATH to a fully functional file
+ * descriptor.
+ *
+ * To achieve this the file is reopened via /proc, which is supported by the
+ * Linux kernel. `fd` needs to point to the currently open file descriptor. On
+ * success it will be replaced by the new upgraded file descriptor, while the
+ * original file descriptor will be closed.
+ *
+ * `flags` are passed to `open()` for the new file descriptor.
+ */
+static int
+reopen_path_fd (int *fd, int flags)
+{
+ /* we need to open /proc/self/fd/<int>, so the path won't get too long here */
+ char proc_path[128];
+ int res = snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", *fd);
+
+ if (res < 0 || res >= sizeof(proc_path))
+ return OATH_PRINTF_ERROR;
+
+ int newfd = open(proc_path, flags);
+
+ if (newfd < 0)
+ return OATH_FILE_OPEN_ERROR;
+
+ close(*fd);
+ *fd = newfd;
+ return OATH_OK;
+}
+
+static void
+init_usersfile_ctx(struct usersfile_ctx *ctx, const char *path)
+{
+ ctx->path = path;
+ ctx->basename = NULL;
+ ctx->parent_fd = -1;
+ ctx->fd = -1;
+ memset(&ctx->st, 0, sizeof(ctx->st));
+}
+
+static void
+destroy_usersfile_ctx(struct usersfile_ctx *ctx)
+{
+ if (ctx->parent_fd != -1)
+ {
+ close (ctx->parent_fd);
+ ctx->parent_fd = -1;
+ }
+
+ if (ctx->fd != -1)
+ {
+ close (ctx->fd);
+ ctx->fd = -1;
+ }
+
+ /* reset everything but keep the path so it might be reused */
+ init_usersfile_ctx(ctx, ctx->path);
+}
+
+/*
+ * Obtain a lock for the usersfile. The lock is placed on the usersfile itself
+ * as found in `ctx->fd`
+ *
+ * On success the lock on `ctx->fd` has been correctly obtained.
+ */
+static int
+lock_usersfile (struct usersfile_ctx *ctx)
+{
+ /*
+ * There exist three file locking APIs:
+ *
+ * - flock(): this would be the simplest API, but it doesn't properly support
+ * network file systems like NFS, which then causes a transparent fallback
+ * to fcntl() file locking.
+ * - fcntl using F_SETLCK & friends: this lock is not based on the open file
+ * description and thus cannot be inherited to child processes, which we
+ * need to do.
+ * - fcntl using F_OFD_SETLCK & friends: this is a Linux specific lock that
+ * _is_ based on the open file description. It seems like the best bet for
+ * our scenario.
+ *
+ * Since we are potentially running in PAM module context, we have to
+ * take a local DoS scenario into account here, where the unprivileged user
+ * holds the lock, preventing us from ever getting it.
+ *
+ * There's no file locking API supporting a timeout (except for using a
+ * SIGALRM timer to interrupt the system call). Using asynchronous signals
+ * in a library is not so great. Thus make a best effort polling attempt:
+ *
+ * `F_OFD_SETLK` polls for the lock. If we cannot get it, sleep half a
+ * second and retry. Do this for at max 15 seconds, else fail.
+ */
+
+ struct flock fl;
+ memset(&fl, 0, sizeof(fl));
+ /* lock the entire file with a write lock */
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ for (int i = 0; i < 30; i++) {
+ if (fcntl(ctx->fd, F_OFD_SETLK, &fl) == 0)
+ return OATH_OK;
+
+ if (errno == EACCES || errno == EAGAIN)
+ usleep(1000 * 500);
+ else
+ break;
+ }
+
+ return OATH_FILE_LOCK_ERROR;
+}
+
+/*
+ * After traversing all directory path elements this function actually opens
+ * the target usersfile. `ctx->parent_fd` must be valid.
+ *
+ * This function takes care of the locking logic, which is a bit complicated,
+ * since we use the usersfile itself for locking. This is done, because we
+ * don't want to clutter arbitrary directories with lockfiles, possibly making
+ * the locking also less robust (e.g. if users delete them interactively).
+ *
+ * Since we don't actually write to the usersfile, but replace it atomically,
+ * to prevent any inconsistent state to ever be stored to disk, we need a
+ * recovery mechanism if we obtain a lock on the file, but the file has
+ * already been replaced by a new version. This situation is detected by
+ * opening the file again after the lock has been placed and comparing the
+ * inode numbers. If they no longer match, then the new file has to be locked
+ * instead.
+ *
+ * On successful return ctx->fd will be valid and locked and ctx->st will
+ * contain the current stat information for the usersfile.
+ */
+static int
+finish_open_usersfile (struct usersfile_ctx *ctx)
+{
+ const int oflags = O_RDONLY|O_PATH|O_CLOEXEC|O_NOFOLLOW;
+ ctx->fd = openat(ctx->parent_fd, ctx->basename, oflags);
+
+ if (ctx->fd < 0)
+ return errno == ENOENT ? OATH_NO_SUCH_FILE : OATH_FILE_OPEN_ERROR;
+
+ if (fstat(ctx->fd, &ctx->st) != 0)
+ return OATH_FILE_STAT_ERROR;
+
+ /* don't allow hard-linked files, which would allow to fool our logic -
+ * this can only happen if protected_hardlinks is disabled in the kernel,
+ * though */
+ if (ctx->st.st_nlink > 1)
+ {
+ close(ctx->fd);
+ return OATH_FILE_OPEN_ERROR;
+ }
+
+ /* lock and retry opening until all is consistent, abort after a couple of
+ * times, it's unlikely that we race all the time (could be a DoS attempt) */
+ for (int i = 0; i < 5; i++)
+ {
+ /* deny world-writable or special usersfile */
+ if ((ctx->st.st_mode & S_IWOTH) != 0 || !S_ISREG(ctx->st.st_mode))
+ return OATH_FILE_OPEN_ERROR;
+
+ /* we need to open it read-write for write-locking it via fcntl(),
+ * otherwise we wouldn't need write access for the file, since we'll
+ * atomically replace it with a new one. */
+ int err = reopen_path_fd(&ctx->fd, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (err != OATH_OK)
+ return err;
+
+ err = lock_usersfile(ctx);
+ if (err != OATH_OK)
+ return err;
+
+ /*
+ * we now own a lock on the usersfile, but another process might already
+ * have replaced the file in question by new version. Thus we need to
+ * check whether the file is still there and is the same as the one we
+ * have opened. Otherwise a race occurred an we need to retry.
+ */
+ int check_fd = openat(ctx->parent_fd, ctx->basename, oflags);
+ struct stat check_st;
+ err = fstat(check_fd, &check_st);
+ if (err != OATH_OK)
+ {
+ close(check_fd);
+ return err;
+ }
+
+ /* comparing the inode should be enough, since parent_fd didn't change,
+ * so it should be the same file system */
+ if (ctx->st.st_ino != check_st.st_ino)
+ {
+ /* race occurred, retry using the new FD */
+ close(ctx->fd);
+ ctx->fd = check_fd;
+ memcpy(&ctx->st, &check_st, sizeof(ctx->st));
+ continue;
+ }
+
+ /* we own the lock and the file is still in place, we did it */
+ close(check_fd);
+
+ /* now also reopen the parent directory FD, so it can be used for
+ * fsync() later on. */
+ err = reopen_path_fd(&ctx->parent_fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (err != OATH_OK)
+ return err;
+
+ return OATH_OK;
+ }
+
+ /* maximum number of locking attempts exceeded */
+ return OATH_FILE_LOCK_ERROR;
+}
+
static int
parse_type (const char *str, unsigned *digits, unsigned *totpstepsize)
{
@@ -298,8 +527,92 @@ update_usersfile2 (const char *username,
return OATH_OK;
}
+/*
+ * create a new file in the directory referred to by ctx->parent_fd. A unique
+ * filename will be selected and written out to `newname`.
+ */
+static int
+create_new_usersfile(struct usersfile_ctx *ctx, char *newname)
+{
+ int err = OATH_OK;
+ newname[0] = '\0';
+
+ /* create an unnamed temporary file, this way we can fix the file mode
+ without anybody else being able to access the file */
+ int fd = openat(ctx->parent_fd, ".", O_TMPFILE|O_WRONLY|O_CLOEXEC, 0600);
+ if (fd < 0)
+ return OATH_FILE_OPEN_ERROR;
+
+ /* make sure the mode is as we want it, since umask might have changed the outcome. */
+ if (fchmod(fd, 0600) != 0)
+ {
+ err = OATH_FILE_CHOWN_ERROR;
+ goto out;
+ }
+
+ /* there's nothing like mkostmpat() where we can use our parent_fd.
+ * tmpname() & friends are deprecated and also not fully suitable here.
+ *
+ * what we're actually missing here is an additional flag LINKAT_REPLACE
+ * which would allow to atomically replace the original file, instead of
+ * using renameat(). This doesn't exist yet, though.
+ *
+ * linkat() doesn't follow symlinks or overwrite files, so we're safe here
+ * against any shenanigans. The user owning parent_fd can try to guess the
+ * filename we're using here and thus DoS us. Setup an arbitrary limit of
+ * creation attempts to prevent an infinite loop in such situations. Such a
+ * bad actor would then only DoS itself, preventing login.
+ *
+ * Shared world-writable directories should never be used for the usersfile,
+ * this would be a configuration error, thus we don't try to protect against
+ * such scenarios.
+ *
+ * An alternative would be using rand(), but then we'd need to also seed it,
+ * with possible process wide side effects, which is also not great.
+ */
+
+ int ret = snprintf(newname, NAME_MAX, "%s.new.%d", ctx->basename, getpid());
+ if (ret < 0 || ret >= NAME_MAX)
+ {
+ err = OATH_PRINTF_ERROR;
+ goto out;
+ }
+
+ /* we need to specify /proc/self/fd/<int>, so the path won't get too long here */
+ char proc_path[128];
+ ret = snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
+ if (ret < 0 || ret >= NAME_MAX)
+ {
+ err = OATH_PRINTF_ERROR;
+ goto out;
+ }
+
+ /* we cannot reliably use AT_EMPTY_PATH here, since it can require the
+ * CAP_DAC_READ_SEARCH capability when running as non-root. Starting with
+ * kernel 6.10 this requirement has been softened, but we need to stay
+ * backward compatible. Linking the magic link in /proc into the directory
+ * works without extra capabilities.
+ * For this workaround to function AT_SYMLINK_FOLLOW _must_ be specified
+ * so this is a conscious decision.
+ */
+ if (linkat(AT_FDCWD, proc_path, ctx->parent_fd, newname, AT_SYMLINK_FOLLOW))
+ {
+ err = OATH_FILE_CREATE_ERROR;
+ }
+
+out:
+ if (err != OATH_OK)
+ {
+ if (fd >= 0)
+ close(fd);
+ return err;
+ }
+
+ return fd;
+}
+
static int
-update_usersfile (const char *usersfile,
+update_usersfile (struct usersfile_ctx *ctx,
const char *username,
const char *otp,
FILE *infh,
@@ -307,9 +620,7 @@ update_usersfile (const char *usersfile,
size_t *n, char *timestamp, uint64_t new_moving_factor,
size_t skipped_users)
{
- FILE *outfh, *lockfh;
int rc;
- char *newfilename, *lockfile;
/* Rewind input file. */
{
@@ -321,112 +632,236 @@ update_usersfile (const char *usersfile,
clearerr (infh);
}
- /* Open lockfile. */
- {
- int l;
+ char newfilename[NAME_MAX];
- l = asprintf (&lockfile, "%s.lock", usersfile);
- if (lockfile == NULL || ((size_t) l) != strlen (usersfile) + 5)
- return OATH_PRINTF_ERROR;
+ /* Open the "new" file. We aim for atomic replacement of the old file to
+ * address possible power failure or system lockup scenarios. */
+ int outfd = create_new_usersfile(ctx, newfilename);
+ if (outfd < 0)
+ {
+ return outfd;
+ }
- lockfh = fopen (lockfile, "w");
- if (!lockfh)
- {
- free (lockfile);
- return OATH_FILE_CREATE_ERROR;
- }
- }
+ FILE *outfh = fdopen (outfd, "w");
+ if (!outfh)
+ {
+ rc = OATH_FILE_CREATE_ERROR;
+ goto out;
+ }
- /* Lock the lockfile. */
- {
- struct flock l;
+ /* ownership has been transferred to outfh */
+ outfd = -1;
- memset (&l, 0, sizeof (l));
- l.l_whence = SEEK_SET;
- l.l_start = 0;
- l.l_len = 0;
- l.l_type = F_WRLCK;
+ /* Create the new usersfile content. */
+ rc = update_usersfile2 (username, otp, infh, outfh, lineptr, n,
+ timestamp, new_moving_factor, skipped_users);
- while ((rc = fcntl (fileno (lockfh), F_SETLKW, &l)) < 0 && errno == EINTR)
- continue;
- if (rc == -1)
- {
- fclose (lockfh);
- free (lockfile);
- return OATH_FILE_LOCK_ERROR;
- }
+ if (rc != OATH_OK)
+ goto out;
+
+ /* On success, flush the buffers. */
+ if (fflush (outfh) != 0) {
+ rc = OATH_FILE_FLUSH_ERROR;
+ goto out;
}
- /* Open the "new" file. */
- {
- int l;
+ /* On success, sync the disks. */
+ if (fsync (fileno (outfh)) != 0) {
+ rc = OATH_FILE_SYNC_ERROR;
+ goto out;
+ }
- l = asprintf (&newfilename, "%s.new", usersfile);
- if (newfilename == NULL || ((size_t) l) != strlen (usersfile) + 4)
- {
- fclose (lockfh);
- free (lockfile);
- return OATH_PRINTF_ERROR;
- }
-
- outfh = fopen (newfilename, "w");
- if (!outfh)
- {
- free (newfilename);
- fclose (lockfh);
- free (lockfile);
- return OATH_FILE_CREATE_ERROR;
- }
+ /* On success, replace the usersfile with the new copy.
+ * This does not follow symlinks in the target, the target will always be
+ * replaced.
+ * */
+ if (renameat (ctx->parent_fd, newfilename, ctx->parent_fd, ctx->basename) != 0) {
+ rc = OATH_FILE_RENAME_ERROR;
+ goto out;
}
- /* Create the new usersfile content. */
- rc = update_usersfile2 (username, otp, infh, outfh, lineptr, n,
- timestamp, new_moving_factor, skipped_users);
+ /* this name no longer exists now */
+ newfilename[0] = '\0';
- /* Preserve ownership of the new usersfile file */
- {
- struct stat insb;
+ /* make sure the directory is also synced such that directory inodes are written out */
+ if (fsync(ctx->parent_fd) != 0) {
+ rc = OATH_FILE_SYNC_ERROR;
+ goto out;
+ }
- if (rc == OATH_OK && fstat (fileno (infh), &insb) == -1)
- rc = OATH_FILE_STAT_ERROR;
+out:
+ if (outfd >= 0)
+ close(outfd);
+ if (outfh)
+ fclose(outfh);
+ if (rc != OATH_OK && newfilename[0])
+ unlinkat(ctx->parent_fd, newfilename, 0);
+ return rc;
+}
- if (rc == OATH_OK
- && fchown (fileno (outfh), insb.st_uid, insb.st_gid) != 0)
- rc = OATH_FILE_CHOWN_ERROR;
- }
+static int
+oath_process_usersfile (struct usersfile_ctx *ctx,
+ const char *username,
+ const char *otp,
+ size_t window,
+ const char *passwd, time_t *last_otp)
+{
+ FILE *infh;
+ char *line = NULL;
+ size_t n = 0;
+ uint64_t new_moving_factor;
+ int rc;
+ size_t skipped_users;
- /* On success, flush the buffers. */
- if (rc == OATH_OK && fflush (outfh) != 0)
- rc = OATH_FILE_FLUSH_ERROR;
+ infh = fdopen (ctx->fd, "r");
+ if (infh == NULL)
+ return OATH_FILE_OPEN_ERROR;
- /* On success, sync the disks. */
- if (rc == OATH_OK && fsync (fileno (outfh)) != 0)
- rc = OATH_FILE_SYNC_ERROR;
+ /* ownership has been transferred to the FILE stream now */
+ ctx->fd = -1;
- /* Close the file regardless of success. */
- if (fclose (outfh) != 0)
- rc = OATH_FILE_CLOSE_ERROR;
+ rc = parse_usersfile (username, otp, window, passwd, last_otp,
+ infh, &line, &n, &new_moving_factor, &skipped_users);
- /* On success, overwrite the usersfile with the new copy. */
- if (rc == OATH_OK && rename (newfilename, usersfile) != 0)
- rc = OATH_FILE_RENAME_ERROR;
+ if (rc == OATH_OK)
+ {
+ char timestamp[30];
+ size_t max = sizeof (timestamp);
+ struct tm now;
+ time_t t;
+ size_t l;
- /* Something has failed, don't leave garbage lying around. */
- if (rc != OATH_OK)
- unlink (newfilename);
+ if (time (&t) == (time_t) - 1)
+ return OATH_TIME_ERROR;
+
+ if (localtime_r (&t, &now) == NULL)
+ return OATH_TIME_ERROR;
- free (newfilename);
+ l = strftime (timestamp, max, TIME_FORMAT_STRING, &now);
+ if (l != 20)
+ return OATH_TIME_ERROR;
+
+ rc = update_usersfile (ctx, username, otp, infh,
+ &line, &n, timestamp, new_moving_factor,
+ skipped_users);
+ }
- /* Complete, close the lockfile */
- if (fclose (lockfh) != 0)
- rc = OATH_FILE_CLOSE_ERROR;
- if (unlink (lockfile) != 0)
- rc = OATH_FILE_UNLINK_ERROR;
- free (lockfile);
+ free (line);
+ fclose (infh);
return rc;
}
+/*
+ * Safely open `ctx->path`, filling all the other fields in `ctx` from it. On
+ * error destroy_usersfile_ctx() is invoked for `ctx`.
+ *
+ * When operating with raised privileges we cannot know the ownership of
+ * `ctx->path` in advance, thus we need to carefully open the path. Any
+ * symbolic links in the path will be rejected for simplicity reasons.
+ *
+ * Every path element will be extracted step-by-step and opened by passing the
+ * `O_PATH` flag. This is the safest approach which prevents any side effects
+ * that might result from opening e.g. FIFO special files, symlinks or device
+ * files.
+ *
+ * Once the final path element has been reached and verified, the file
+ * descriptors have to be upgraded to regular ones without the `O_PATH`
+ * property, for being able to use them for regular file operations.
+ *
+ * NOTE: a similar result can be achieved by using openat2() and passing
+ * RESOLVE_NO_SYMLINKS, but the system call is not yet wrapped in Glibc, which
+ * makes it hard to use it.
+ */
+static int
+safe_open_usersfile (struct usersfile_ctx *ctx)
+{
+ int err = OATH_OK;
+
+ /* reject relative paths */
+ if (ctx->path[0] != '/')
+ return OATH_FILE_OPEN_ERROR;
+
+ ctx->parent_fd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (ctx->parent_fd < 0)
+ return OATH_FILE_OPEN_ERROR;
+
+ char *path_start = strdup (ctx->path);
+ if (!path_start) {
+ err = OATH_MALLOC_ERROR;
+ goto out;
+ }
+
+ char *element = path_start;
+
+ while (true)
+ {
+ /* ignore any extra leading slashes */
+ while (element[0] == '/')
+ element++;
+
+ /* end of path has been reached (trailing slashes? shouldn't really happen) */
+ if (!element[0])
+ {
+ err = OATH_FILE_OPEN_ERROR;
+ goto out;
+ }
+
+ char *sep = strpbrk(element, "/");
+
+ /* intermediate path (directory) element */
+ if (sep)
+ {
+ *sep = '\0';
+
+ ctx->fd = openat(ctx->parent_fd, element, O_RDONLY|O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
+
+ if (ctx->fd < 0)
+ {
+ err = errno == ENOENT ? OATH_NO_SUCH_FILE : OATH_FILE_OPEN_ERROR;
+ goto out;
+ }
+
+ if (fstat(ctx->fd, &ctx->st) != 0)
+ {
+ err = OATH_FILE_STAT_ERROR;
+ goto out;
+ }
+
+ /* If we encounter any world-writable components, refuse the path.
+ * This prevents any unwise configurations like placing the file into
+ * /var/tmp or a dedicated world-writable sticky-bit directory from
+ * working. */
+ if (ctx->st.st_mode & S_IWOTH)
+ {
+ err = OATH_FILE_OPEN_ERROR;
+ goto out;
+ }
+
+ close(ctx->parent_fd);
+ ctx->parent_fd = ctx->fd;
+ ctx->fd = -1;
+ element = sep + 1;
+ }
+ /* final path element has been encountered */
+ else
+ {
+ ctx->basename = ctx->path + (element - path_start);
+ err = finish_open_usersfile(ctx);
+ break;
+ }
+ }
+
+
+out:
+ if (err != OATH_OK)
+ {
+ destroy_usersfile_ctx(ctx);
+ }
+ free (path_start);
+ return err;
+}
+
/**
* oath_authenticate_usersfile:
* @usersfile: string with user credential filename, in UsersFile format
@@ -460,52 +895,71 @@ oath_authenticate_usersfile (const char *usersfile,
size_t window,
const char *passwd, time_t *last_otp)
{
- FILE *infh;
- char *line = NULL;
- size_t n = 0;
- uint64_t new_moving_factor;
int rc;
- size_t skipped_users;
-
- infh = fopen (usersfile, "r");
- if (!infh)
- return OATH_NO_SUCH_FILE;
-
- rc = parse_usersfile (username, otp, window, passwd, last_otp,
- infh, &line, &n, &new_moving_factor, &skipped_users);
-
- if (rc == OATH_OK)
+ struct usersfile_ctx ctx;
+ init_usersfile_ctx(&ctx, usersfile);
+
+ rc = safe_open_usersfile (&ctx);
+ if (rc < 0)
+ return rc;
+
+ /* if user is not root we cannot change credentials,
+ just run _oath_authenticate_usersfile normally in this case.
+ Similarly if the file is owned by root, we don't need to change
+ credentials. */
+ if (geteuid () != 0 || ctx.st.st_uid == 0)
{
- char timestamp[30];
- size_t max = sizeof (timestamp);
- struct tm now;
- time_t t;
- size_t l;
- mode_t old_umask;
-
- if (time (&t) == (time_t) - 1)
- return OATH_TIME_ERROR;
-
- if (localtime_r (&t, &now) == NULL)
- return OATH_TIME_ERROR;
-
- l = strftime (timestamp, max, TIME_FORMAT_STRING, &now);
- if (l != 20)
- return OATH_TIME_ERROR;
-
- old_umask = umask (~(S_IRUSR | S_IWUSR));
-
- rc = update_usersfile (usersfile, username, otp, infh,
- &line, &n, timestamp, new_moving_factor,
- skipped_users);
-
- umask (old_umask);
+ rc = oath_process_usersfile (&ctx, username, otp, window, passwd, last_otp);
+ destroy_usersfile_ctx(&ctx);
+ return rc;
}
- free (line);
- fclose (infh);
+ /* else spawn a new process so we can drop privileges to the owner of the
+ * file, to be on the safe side when operating in a directory owned by
+ * non-root. */
+ pid_t cpid = fork ();
+ if (cpid < 0)
+ {
+ destroy_usersfile_ctx(&ctx);
+ return OATH_FORK_ERROR;
+ }
- return rc;
+ if (cpid == 0)
+ {
+ /* child */
+ if (setgroups(0, NULL) != 0)
+ exit (abs(OATH_SETGROUPS_ERROR));
+ if (setgid (ctx.st.st_gid) != 0)
+ exit (abs(OATH_SETGID_ERROR));
+ if (setuid (ctx.st.st_uid) != 0)
+ exit (abs(OATH_SETUID_ERROR));
+ rc = oath_process_usersfile (&ctx, username, otp, window, passwd, last_otp);
+ exit (abs(rc));
+ }
+ else
+ {
+ int status;
+ rc = waitpid (cpid, &status, 0);
+ if (rc < 0)
+ goto wait_out;
+
+ if (!WIFEXITED(status))
+ {
+ /* child exited abnormally */
+ rc = OATH_WAIT_ERROR;
+ goto wait_out;
+ }
+
+ const int exit_code = WEXITSTATUS(status);
+ rc = exit_code == 0 ? OATH_OK : -exit_code;
+wait_out:
+ /*
+ * only destroy the ctx after the child exited, otherwise the lockfile
+ * would be unlinked before the job is finished.
+ */
+ destroy_usersfile_ctx(&ctx);
+ return rc;
+ }
}
#else /* _WIN32 */
--
2.45.2

View File

@ -0,0 +1,135 @@
From f69897e6e8b3881b9e470a384cefc41a851b2475 Mon Sep 17 00:00:00 2001
From: Luna <luna.dragon@suse.com>
Date: Mon, 9 Sep 2024 19:14:08 +0530
Subject: [PATCH 2/2] pam_oath: add null_usersfile_okay parameter to pam_oath
Co-authored-by: Jan Zerebecki <jzerebecki@suse.com>
Co-authored-by: Miika Alikirri <miika.alikirri@suse.com>
---
pam_oath/README | 10 ++++++++++
pam_oath/pam_oath.c | 32 ++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+)
diff --git a/pam_oath/README b/pam_oath/README
index 9a8e7366..3c7da052 100644
--- a/pam_oath/README
+++ b/pam_oath/README
@@ -77,6 +77,7 @@ jas@mocca:~$ su
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
+[pam_oath.c:parse_cfg(129)] no_usersfile_okay=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=0
[pam_oath.c:parse_cfg(132)] window=20
@@ -144,6 +145,7 @@ jas@mocca:~$ su
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
+[pam_oath.c:parse_cfg(129)] no_usersfile_okay=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=6
[pam_oath.c:parse_cfg(132)] window=20
@@ -176,6 +178,7 @@ jas@mocca:~$ su
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
+[pam_oath.c:parse_cfg(129)] no_usersfile_okay=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=6
[pam_oath.c:parse_cfg(132)] window=20
@@ -213,6 +216,13 @@ List of all parameters
never prompt the user - if no password is
available or the password is not appropriate, the
user will be denied access.
+ "no_usersfile_okay": The argument no_usersfile_okay forces the module
+ to act as if the user is not present in the config,
+ if the config file does not exist. This has
+ security implications only use if you know what you
+ are doing. E.g. if the file is in a mount like home
+ and that fails to be mounted, then this will succeed
+ even if the OTP if configured for that user.
"usersfile": Specify filename where credentials are stored, for
example "/etc/users.oath". The placeholder values
diff --git a/pam_oath/pam_oath.c b/pam_oath/pam_oath.c
index 25eb83e6..72712b53 100644
--- a/pam_oath/pam_oath.c
+++ b/pam_oath/pam_oath.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <libgen.h>
#include <ctype.h>
#include <pwd.h>
#include <unistd.h>
@@ -72,6 +73,7 @@ struct cfg
int alwaysok;
int try_first_pass;
int use_first_pass;
+ int no_usersfile_okay;
char *usersfile;
unsigned digits;
unsigned window;
@@ -86,6 +88,7 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
cfg->alwaysok = 0;
cfg->try_first_pass = 0;
cfg->use_first_pass = 0;
+ cfg->no_usersfile_okay = 0;
cfg->usersfile = NULL;
cfg->digits = -1;
cfg->window = 5;
@@ -100,6 +103,8 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
cfg->try_first_pass = 1;
if (strcmp (argv[i], "use_first_pass") == 0)
cfg->use_first_pass = 1;
+ if (strcmp (argv[i], "no_usersfile_okay") == 0)
+ cfg->no_usersfile_okay = 1;
if (strncmp (argv[i], "usersfile=", 10) == 0)
cfg->usersfile = (char *) argv[i] + 10;
if (strncmp (argv[i], "digits=", 7) == 0)
@@ -126,6 +131,7 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
D (("alwaysok=%d", cfg->alwaysok));
D (("try_first_pass=%d", cfg->try_first_pass));
D (("use_first_pass=%d", cfg->use_first_pass));
+ D (("no_usersfile_okay=%d", cfg->no_usersfile_okay));
D (("usersfile=%s", cfg->usersfile ? cfg->usersfile : "(null)"));
D (("digits=%d", cfg->digits));
D (("window=%d", cfg->window));
@@ -292,6 +298,32 @@ pam_sm_authenticate (pam_handle_t *pamh,
}
DBG (("usersfile is %s", usersfile));
+ if (cfg.no_usersfile_okay)
+ {
+ char *ucopy, *base;
+ ucopy = strdup (usersfile);
+ base = dirname (ucopy);
+
+ /* make sure that the base dir exists so we are sure that, for example,
+ the user home directory is mounted. */
+ rc = access (base, F_OK);
+ free (ucopy);
+ if (rc != 0)
+ {
+ DBG (("Basepath of file cannot be accessed '%s'", usersfile));
+ retval = PAM_AUTH_ERR;
+ goto done;
+ }
+
+ if (access (usersfile, F_OK) != 0)
+ {
+ DBG (("no_usersfile_okay set and no userfile was found, authenticating..."));
+ retval = PAM_SUCCESS;
+ goto done;
+ }
+ }
+
+
// quick check to skip unconfigured users before prompting for password
{
time_t last_otp;
--
GitLab

BIN
oath-toolkit-2.6.11.tar.gz (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

249
oath-toolkit.changes Normal file
View File

@ -0,0 +1,249 @@
-------------------------------------------------------------------
Wed Oct 16 14:24:27 UTC 2024 - Jan Zerebecki <jan.suse@zerebecki.de>
- Update 0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch
with bsc#1231699 improvements for security fix CVE-2024-47191
-------------------------------------------------------------------
Fri Sep 13 15:10:22 UTC 2024 - Jan Zerebecki <jan.suse@zerebecki.de>
- Fix security issue CVE-2024-47191 by adding
0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch .
- Add patch to implement new null_usersfile_okay argument
42-null_usersfile_okay.patch .
- Makes this version 2.6.11.12 to be able to depend on it.
-------------------------------------------------------------------
Wed Apr 3 11:18:24 UTC 2024 - pgajdos@suse.com
- version update to 2.6.11
* liboath: Handle invalid base32 encoded secrets. Fixes: #41.
* Various build fixes including updated gnulib files.
* Improve compatibility with recent libxmlsec.
-------------------------------------------------------------------
Sun Jul 9 12:03:29 UTC 2023 - Martin Hauke <mardnh@gmx.de>
- Update to version 2.6.8
* libpskc: Fixes for recent libxmlsec releases.
* pam_oath: Provide fallback pam_modutil_getpwnam implementation.
* pam_oath: Don't fail authentication when pam_modutil_getpwnam
doesn't ** know the user when usersfile don't include ${USER}
or ${HOME}.
* pam_oath: Self-test improvements.
-------------------------------------------------------------------
Tue Aug 2 20:39:41 UTC 2022 - Torsten Gruner <simmphonie@opensuse.org>
- Use %_pam_moduledir instead of hardcoding %{_lib}/security
- Define macro _pam_moduledir if not set to fix builds for Leap and SLE
-------------------------------------------------------------------
Thu Apr 21 09:52:55 UTC 2022 - Marcus Meissner <meissner@suse.com>
- url -> https
-------------------------------------------------------------------
Sun May 2 14:36:13 UTC 2021 - Martin Hauke <mardnh@gmx.de>
- Update to version 2.6.7
* pam_oath: Support variables in usersfile string parameter.
These changes introduce the ${USER} and ${HOME} placeholder
values for the usersfile string in the pam_oath configuration
file. The placeholder values allow the user credentials file
to be stored in a file path that is relative to the user, and
mimics similar behavior found in google-authenticator-libpam.
The motivation for these changes is to allow for
non-privileged processes to use pam_oath (e.g., for 2FA with
xscreensaver). Non-privileged and non-suid programs are
unable to use pam_oath. These changes are a proposed
alternative to a suid helper binary as well.
* doc: Fix project URL in man pages.
* build: Drop use of libxml's AM_PATH_XML2 in favor of pkg-config.
* build: Modernize autotools usage.
Most importantly, no longer use -Werror with AM_INIT_AUTOMAKE
to make rebuilding from source more safe with future automake
versions.
* Updated gnulib files.
-------------------------------------------------------------------
Wed Jan 20 21:40:44 UTC 2021 - Martin Hauke <mardnh@gmx.de>
- Update to version 2.6.6
* oathtool: Support for reading KEY and OTP from standard input
or filename. KEY and OTP may now be given as '-' to mean
stdin, or @FILE to read from a particular file. This is
recommended on multi-user systems, since secrets as command
line parameters leak.
* pam_oath: Fix unlikely logic fail on out of memory conditions.
-------------------------------------------------------------------
Tue Dec 29 11:58:14 UTC 2020 - Martin Hauke <mardnh@gmx.de>
- Update to version 2.6.5
* oathtool: Support for reading KEY and OTP from standard input
or filename.
KEY and OTP may now be given as '-' to mean stdin, or @FILE to
read from a particular file. This is recommended on multi-user
systems, since secrets as command line parameters leak.
* pam_oath: Fix unlikely logic fail on out of memory conditions.
* Doc fixes.
- Update to version 2.6.4
* libpskc: New --with-xmlsec-crypto-engine to hard-code crypto
engine. Use it like --with-xmlsec-crypto-engine=gnutls or
--with-xmlsec-crypto-engine=openssl if the default dynamic
loading fails because of runtime linker search path issues.
* oathtool --totp --verbose now prints TOTP hash mode.
* oathtool: Hash names (e.g., SHA256) for --totp are now upper
case. Lower/mixed case hash names are supported for
compatibility.
* pam_oath: Fail gracefully for missing users.
This allows you to incrementally add support for OATH
authentication instead of forcing it on all users.
* Fix libpskc memory corruption bug.
* Fix man pages.
* Build fixes.
- Update to version 2.6.3
* pam_oath: Fix self-tests.
- Drop not longer needed patches:
* 0001-Fix-no-return-in-nonvoid-function-errors-reported-by.patch
* 0003-pam_oath-assign-safe-default-to-alwaysok-config-memb.patch
* 0002-update_gnulibs_files.patch
* gnulib-libio.patch
- Use source verification
- Use proper source URLs
-------------------------------------------------------------------
Mon Aug 6 07:59:16 UTC 2018 - schwab@suse.de
- gnulib-libio.patch: Update gnulib for libio.h removal
-------------------------------------------------------------------
Thu Jul 5 17:00:51 UTC 2018 - matthias.gerstner@suse.com
- Add patch 0003-pam_oath-assign-safe-default-to-alwaysok-config-memb.patch:
- fix potential security issue in low memory situation (bsc#1089114)
-------------------------------------------------------------------
Sun May 20 21:40:32 UTC 2018 - julio@juliogonzalez.es
- Fix build for openSUSE Leap 42.2 and 42.3
-------------------------------------------------------------------
Wed Apr 18 07:32:43 UTC 2018 - jengelh@inai.de
- Trim/update descriptions. Fix RPM groups. Remove useless
--with-pic.
-------------------------------------------------------------------
Fri Apr 13 13:26:47 UTC 2018 - mpluskal@suse.com
- Run spe-cleaner
- Drop useless conditions
-------------------------------------------------------------------
Wed Apr 11 12:18:59 UTC 2018 - ncutler@suse.com
- bring License line into closer accordance with actual licenses
mentioned in the tarball
- split off xml/pskc/ directory/files from liboath0 into a separate
"oath-toolkit-xml" subpackage to prevent conflicts if two versions of the
liboath library were ever installed at the same time
-------------------------------------------------------------------
Wed Apr 11 11:26:36 UTC 2018 - ncutler@suse.com
- use %license instead of %doc to package license-related files
-------------------------------------------------------------------
Tue Jan 16 11:18:53 UTC 2018 - dmarcoux@posteo.de
- Add patch (last commit which changed source, not released in 2.6.2):
- 0002-update_gnulibs_files.patch
-------------------------------------------------------------------
Mon Aug 29 20:03:11 UTC 2016 - mardnh@gmx.de
- Update to Version 2.6.2
- no changes in upstream code
- Fix RPM groups for -devel packages
- build with libpskc on supported suse-versions
- Add patch:
- 0001-Fix-no-return-in-nonvoid-function-errors-reported-by.patch
-------------------------------------------------------------------
Wed Sep 9 14:31:24 UTC 2015 - t.gruner@katodev.de
- Update to Version 2.6.1 (released 2015-07-31)
- liboath: Fix 'make check' on 32-bit systems.
- Version 2.6.0 (released 2015-05-19)
- liboath: Support TOTP with HMAC-SHA256 and HMAC-SHA512.
This adds new APIs oath_totp_generate2, oath_totp_validate4 and
oath_totp_validate4_callback.
- oathtool: The --totp parameter now take an optional argument to specify MAC.
For example use --totp=sha256 to use HMAC-SHA256. When --totp is used
the default HMAC-SHA1 is used, as before.
- pam_oath: Mention in README that you shouldn't use insecure keys.
- pam_oath: Check return value from strdup.
- The files 'gdoc' and 'expect.oath' are now included in the tarball.
-------------------------------------------------------------------
Sat Jan 24 10:29:53 UTC 2015 - mardnh@gmx.de
- Update to version 2.4.1:
+ liboath: Fix usersfile bug that caused it to update the wrong line.
When an usersfile contain multiple lines for the same user but with an
unparseable token type (e.g., HOTP vs TOTP), the code would update the
wrong line of the file. Since the then updated line could be a
commented out line, this can lead to the same OTP being accepted
multiple times which is a security vulnerability. Reported by Bas van
Schaik <bas@sj-vs.net> and patch provided by Ilkka Virta
<itvirta@iki.fi>. CVE-2013-7322
-------------------------------------------------------------------
Fri Jul 11 18:14:17 UTC 2014 - darin@darins.net
- Ran through spec-cleaner
-------------------------------------------------------------------
Wed Oct 23 09:41:19 UTC 2013 - vuntz@opensuse.org
- Update to version 2.4.0:
+ liboath: Add new API methods for validating TOTP OTPs
- Changes from version 2.2.0:
+ libpskc: Add functions for setting PSKC data.
+ liboath: Permit different passwords for different tokens for
the same user.
+ liboath: Make header file usable from C++ (extern "C" guard).
+ build: Improve building from git with most recent automake and
gengetopt.
+ build: Valgrind is not enabled by default.
- Fix license: libraries are LGPL-2.1+ and everything else is
GPL-3.0+. Also properly package the COPYING files.
- Prepare build libpskc, hidden under a %{build_pskc} define:
+ Add libxml2-devel and pkgconfig(xmlsec1) BuildRequires.
+ Create libpskc0 and libpskc-devel subpackages.
+ Define %{build_pskc} to 0 since we don't have libxmlsec1 yet.
- Rework summaries and descriptions.
-------------------------------------------------------------------
Sat Jun 15 18:46:27 UTC 2013 - bwiedemann@suse.com
- Update to version 2.0.2
-------------------------------------------------------------------
Fri Feb 11 00:04:02 UTC 2011 - cristian.rodriguez@opensuse.org
- Update to version 1.4.6
-------------------------------------------------------------------
Sat Feb 5 18:41:54 UTC 2011 - cristian.rodriguez@opensuse.org
- Use libgcrypt for crypto
-------------------------------------------------------------------
Sat Feb 5 14:46:45 UTC 2011 - cristian.rodriguez@opensuse.org
- Initial version

23
oath-toolkit.keyring Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEXJLOtBYJKwYBBAHaRw8BAQdACIcrZIvhrxDBkK9fV+QlTmXxo2naObDuGtw5
8YaxlOu0JVNpbW9uIEpvc2Vmc3NvbiA8c2ltb25Aam9zZWZzc29uLm9yZz6IlgQT
FggAPgIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgBYhBLHSvRN1vst4TPT4xNc8
9jjFPAa+BQJezg00BQkDekmAAAoJENc89jjFPAa+7QMBAKyq5ZypvFOXgcwlNtQd
f6F+SP9LnCNSreQRYo4RxSwAAQD7A+O56xFPB1DIM74lpvaExNJFHbJXCIfFGifJ
ycR0A7gzBFySz3UWCSsGAQQB2kcPAQEHQLzCFcHHrKzVSPDDarZPYqn89H5TPaxw
cORgRg+4DagEiH4EGBYIACYCGyAWIQSx0r0Tdb7LeEz0+MTXPPY4xTwGvgUCXs4N
RwUJA3pI0gAKCRDXPPY4xTwGvgxBAQCyHr8nGeaoOAmhPPOGDObOoa6/Dps+WBpm
vFw8J/Z5AAEAtE/pypHisMHmF4cy5S/kHVzLZvfxaTAlGqtoZGHShAa4MwRcks+B
FgkrBgEEAdpHDwEBB0DsUwiDmnlwMSNoSF+ByvW0E6TVXou9PKDa9SpZvKghioj1
BBgWCAAmAhsCFiEEsdK9E3W+y3hM9PjE1zz2OMU8Br4FAl7ODUwFCQN6SMsAgXYg
BBkWCAAdFiEEo8ychwudMQq61M8vUXIrCP5HRaIFAlySz4EACgkQUXIrCP5HRaKn
TAEAoB+OWrHmYCK8Cjr1DgPUH7JnhPBmR2DbhR5jPRREEugA+gOMeWmL6GOpaPfK
YLcNhzw4ZnAlxSLY1wq1eANBpiQOCRDXPPY4xTwGvuQiAPwKnKAbzegaSATxN1cd
Fia4m80uJNFHMQL679WSBG3FIAEA8uLgxGud6SqFgIaFR4wrzrIgzVWqHxDuu56f
JSf/iAe4OARcks9qEgorBgEEAZdVAQUBAQdAMZUbpg1up2WOwPlQn3pPVaRMejyZ
nScmD7d5TRzHehwDAQgHiH4EGBYIACYCGwwWIQSx0r0Tdb7LeEz0+MTXPPY4xTwG
vgUCXs4NQAUJA3pI1gAKCRDXPPY4xTwGvu8QAP9Ln136hLt/yLfx4KYjBxPAdfd9
oRYd3xqWFBxNZmn+BgD/XZrhNaY3MEBV4yIx4ts6JT7dJfXGcbNjxK1T2BlXdQE=
=moUA
-----END PGP PUBLIC KEY BLOCK-----

204
oath-toolkit.spec Normal file
View File

@ -0,0 +1,204 @@
#
# spec file for package oath-toolkit
#
# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%{!?_pam_moduledir: %define _pam_moduledir /%{_lib}/security}
Name: oath-toolkit
Version: 2.6.11.12
Release: 0
Summary: Toolkit for one-time password authentication systems
License: GPL-3.0-or-later AND LGPL-2.1-or-later
Group: Productivity/Networking/Security
URL: https://www.nongnu.org/oath-toolkit/
Source: https://download-mirror.savannah.gnu.org/releases/%{name}/%{name}-2.6.11.tar.gz
Source1: https://download-mirror.savannah.gnu.org/releases/%{name}/%{name}-2.6.11.tar.gz.sig
Source99: %{name}.keyring
Patch001: 0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch
# https://gitlab.com/oath-toolkit/oath-toolkit/-/merge_requests/42
Patch002: 42-null_usersfile_okay.patch
BuildRequires: bison
BuildRequires: gengetopt
BuildRequires: libgcrypt-devel
BuildRequires: libtool
BuildRequires: pam-devel
BuildRequires: pkgconfig
BuildRequires: pkgconfig(gtk-doc)
BuildRequires: pkgconfig(libxml-2.0)
BuildRequires: pkgconfig(xmlsec1)
%description
The OATH Toolkit makes it possible to build one-time password
authentication systems. It contains shared libraries, command line
tools and a PAM module. Supported technologies include the
event-based HOTP algorithm (RFC4226) and the time-based TOTP algorithm
(RFC6238). OATH stands for Open AuTHentication, which is the
organization that specify the algorithms. For managing secret key
files, the Portable Symmetric Key Container (PSKC) format described in
RFC6030 is supported.
%package -n pam_oath
Summary: PAM module for pluggable login authentication for OATH
License: GPL-3.0-or-later
Group: Productivity/Networking/Security
%description -n pam_oath
The OATH Toolkit makes it possible to build one-time password
authentication systems.
This subpackage contains a module to integrate OATH into PAM.
%package -n liboath0
Summary: Library for Open AuTHentication (OATH) HOTP support
License: LGPL-2.1-or-later
Group: System/Libraries
Requires: %{name}-xml >= %{version}
%description -n liboath0
The OATH Toolkit makes it possible to build one-time password
authentication systems. Supported technologies include the
event-based HOTP algorithm (RFC4226) and the time-based TOTP algorithm
(RFC6238).
%package xml
Summary: XML data files needed by liboath
License: GPL-3.0-or-later AND LGPL-2.1-or-later
Group: Productivity/Networking/Security
BuildArch: noarch
%description xml
The OATH Toolkit makes it possible to build one-time password
authentication systems. It contains shared libraries, command line
tools and a PAM module. Supported technologies include the
event-based HOTP algorithm (RFC4226) and the time-based TOTP algorithm
(RFC6238). OATH stands for Open AuTHentication, which is the
organization that specify the algorithms. For managing secret key
files, the Portable Symmetric Key Container (PSKC) format described in
RFC6030 is supported.
%package -n liboath-devel
Summary: Development files for the Open AuTHentication (OATH) HOTP support library
License: LGPL-2.1-or-later
Group: Development/Libraries/C and C++
Requires: glibc-devel
Requires: liboath0 = %{version}
%description -n liboath-devel
The OATH Toolkit makes it possible to build one-time password
authentication systems.
This subpackage contains the header files for the HOTP/TOTP library.
%package -n libpskc0
Summary: Library for Portable Symmetric Key Container
License: LGPL-2.1-or-later
Group: System/Libraries
%description -n libpskc0
The OATH Toolkit makes it possible to build one-time password
authentication systems.
For managing secret key files, the Portable Symmetric Key Container
(PSKC) format described in RFC6030 is supported.
%package -n libpskc-devel
Summary: Development files for the Portable Symmetric Key Container library
License: LGPL-2.1-or-later
Group: Development/Libraries/C and C++
Requires: glibc-devel
Requires: libpskc0 = %{version}
%description -n libpskc-devel
The OATH Toolkit makes it possible to build one-time password
authentication systems.
For managing secret key files, the Portable Symmetric Key Container
(PSKC) format described in RFC6030 is supported.
This subpackage contains the headers for this library.
%prep
%setup -q -n %{name}-2.6.11
%patch -P 001 -p1
%patch -P 002 -p1
%build
autoreconf -fiv
%configure \
--with-pam-dir=%{_pam_moduledir} \
--with-libgcrypt \
--disable-silent-rules \
--disable-static
# Only SLE and openSUSE >= 15.0 are using rpm >= 4.12
# See https://en.opensuse.org/openSUSE:Build_system_recipes#automake
%if 0%{?sle_version} >= 150000
%make_build
%else
make %{?_smp_mflags}
%endif
%install
%make_install
mv COPYING COPYING.summary
find %{buildroot} -type f -name "*.la" -delete -print
%post -n liboath0 -p /sbin/ldconfig
%postun -n liboath0 -p /sbin/ldconfig
%post -n libpskc0 -p /sbin/ldconfig
%postun -n libpskc0 -p /sbin/ldconfig
%files
%license COPYING.summary
%doc ChangeLog NEWS README
%license oathtool/COPYING
%{_bindir}/oathtool
%{_mandir}/man1/oathtool.*
%{_bindir}/pskctool
%{_mandir}/man1/pskctool.*
%files -n pam_oath
%doc pam_oath/README
%license pam_oath/COPYING
%{_pam_moduledir}/pam_oath.so
%files -n liboath0
%license liboath/COPYING
%{_libdir}/liboath.so.*
%files xml
%{_datadir}/xml/pskc/
%files -n liboath-devel
%{_libdir}/liboath.so
%{_includedir}/liboath/
%{_libdir}/pkgconfig/liboath.pc
%doc %{_datadir}/gtk-doc/html/liboath
%{_mandir}/man3/oath_*
%files -n libpskc0
# there's no COPYING for libpskc, but it's LGPL, like liboath
%doc libpskc/README
%license liboath/COPYING
%{_libdir}/libpskc.so.*
%files -n libpskc-devel
%{_libdir}/libpskc.so
%{_includedir}/pskc/
%{_libdir}/pkgconfig/libpskc.pc
%doc %{_datadir}/gtk-doc/html/libpskc
%{_mandir}/man3/pskc_*
%changelog