From f3299b3ea78d268d2418f3b7792754bfafa43fe2d0e0b2d943c20e797a079adf Mon Sep 17 00:00:00 2001 From: Torsten Gruner Date: Wed, 16 Oct 2024 17:33:55 +0000 Subject: [PATCH] - 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 --- .gitattributes | 23 + .gitignore | 1 + ...tential-security-issues-in-PAM-modul.patch | 914 ++++++++++++++++++ 42-null_usersfile_okay.patch | 135 +++ oath-toolkit-2.6.11.tar.gz | 3 + oath-toolkit-2.6.11.tar.gz.sig | Bin 0 -> 119 bytes oath-toolkit.changes | 249 +++++ oath-toolkit.keyring | 23 + oath-toolkit.spec | 204 ++++ 9 files changed, 1552 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch create mode 100644 42-null_usersfile_okay.patch create mode 100644 oath-toolkit-2.6.11.tar.gz create mode 100644 oath-toolkit-2.6.11.tar.gz.sig create mode 100644 oath-toolkit.changes create mode 100644 oath-toolkit.keyring create mode 100644 oath-toolkit.spec diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch b/0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch new file mode 100644 index 0000000..2e02203 --- /dev/null +++ b/0001-usersfile-fix-potential-security-issues-in-PAM-modul.patch @@ -0,0 +1,914 @@ +From 345ae06e0f698bdb1e9b4529e5a882f12df04426 Mon Sep 17 00:00:00 2001 +From: Matthias Gerstner +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 /* For ssize_t. */ + #include /* For fcntl. */ + #include /* For errno. */ ++#include /* For PATH_MAX & friends. */ + #include /* For S_IRUSR, S_IWUSR. */ ++#include /* For wait */ ++#include /* For stat */ ++#include /* 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/, 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/, 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 + diff --git a/42-null_usersfile_okay.patch b/42-null_usersfile_okay.patch new file mode 100644 index 0000000..0426d8a --- /dev/null +++ b/42-null_usersfile_okay.patch @@ -0,0 +1,135 @@ +From f69897e6e8b3881b9e470a384cefc41a851b2475 Mon Sep 17 00:00:00 2001 +From: Luna +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 +Co-authored-by: Miika Alikirri +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 + diff --git a/oath-toolkit-2.6.11.tar.gz b/oath-toolkit-2.6.11.tar.gz new file mode 100644 index 0000000..433faf4 --- /dev/null +++ b/oath-toolkit-2.6.11.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc512a4a5b46f4c43ab0586c3189fece4d54f7e649397d6fa1e23428431e2cb4 +size 4699215 diff --git a/oath-toolkit-2.6.11.tar.gz.sig b/oath-toolkit-2.6.11.tar.gz.sig new file mode 100644 index 0000000000000000000000000000000000000000000000000000000000000000..69784df1015ba40e0938730bdc7a0fad7f273dbcb939937de5416664b523faf7 GIT binary patch literal 119 zcmeAuWnmEGV2~A4WLbP>PCNHpL#|y{&g%yjX> + +- 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 + +- 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 + +- 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 + +- 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 + +- url -> https + +------------------------------------------------------------------- +Sun May 2 14:36:13 UTC 2021 - Martin Hauke + +- 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 + +- 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 + +- 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 and patch provided by Ilkka Virta + . 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 + diff --git a/oath-toolkit.keyring b/oath-toolkit.keyring new file mode 100644 index 0000000..a724c87 --- /dev/null +++ b/oath-toolkit.keyring @@ -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----- diff --git a/oath-toolkit.spec b/oath-toolkit.spec new file mode 100644 index 0000000..8d3a0dd --- /dev/null +++ b/oath-toolkit.spec @@ -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