Module Name:    src
Committed By:   christos
Date:           Wed Jan 15 15:30:49 UTC 2025

Modified Files:
        src/external/bsd/pam-u2f/bin/pamu2fcfg: Makefile pamu2fcfg.1
        src/external/bsd/pam-u2f/dist: expand.c pam-u2f.c util.c util.h
        src/external/bsd/pam-u2f/dist/pamu2fcfg: pamu2fcfg.c
        src/external/bsd/pam-u2f/lib/security/pam-u2f: Makefile pam_u2f.8
Added Files:
        src/external/bsd/pam-u2f: Makefile.inc README
Removed Files:
        src/external/bsd/pam-u2f/dist: drop_privs.c
        src/external/bsd/pam-u2f/dist/.github/workflows: scan.yml
        src/external/bsd/pam-u2f/dist/pamu2fcfg: cmdline.ggo
        src/external/bsd/pam-u2f/dist/tests: basic.c

Log Message:
merge changes between 1.2.0 and 1.3.1


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/external/bsd/pam-u2f/Makefile.inc \
    src/external/bsd/pam-u2f/README
cvs rdiff -u -r1.5 -r1.6 src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile
cvs rdiff -u -r1.1 -r1.2 src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1
cvs rdiff -u -r1.1.1.1 -r0 src/external/bsd/pam-u2f/dist/drop_privs.c
cvs rdiff -u -r1.1.1.1 -r1.2 src/external/bsd/pam-u2f/dist/expand.c
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/pam-u2f/dist/pam-u2f.c \
    src/external/bsd/pam-u2f/dist/util.h
cvs rdiff -u -r1.4 -r1.5 src/external/bsd/pam-u2f/dist/util.c
cvs rdiff -u -r1.1.1.1 -r0 \
    src/external/bsd/pam-u2f/dist/.github/workflows/scan.yml
cvs rdiff -u -r1.1.1.2 -r0 \
    src/external/bsd/pam-u2f/dist/pamu2fcfg/cmdline.ggo
cvs rdiff -u -r1.2 -r1.3 src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c
cvs rdiff -u -r1.1.1.2 -r0 src/external/bsd/pam-u2f/dist/tests/basic.c
cvs rdiff -u -r1.6 -r1.7 \
    src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile
cvs rdiff -u -r1.1 -r1.2 \
    src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile
diff -u src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile:1.5 src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile:1.6
--- src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile:1.5	Tue May  9 17:08:34 2023
+++ src/external/bsd/pam-u2f/bin/pamu2fcfg/Makefile	Wed Jan 15 10:30:48 2025
@@ -1,18 +1,15 @@
-# $NetBSD: Makefile,v 1.5 2023/05/09 21:08:34 christos Exp $
+# $NetBSD: Makefile,v 1.6 2025/01/15 15:30:48 christos Exp $
 
 .include <bsd.own.mk>
+.include "${.PARSEDIR}/../../Makefile.inc"
 
 DIST=${NETBSDSRCDIR}/external/bsd/pam-u2f/dist
 
 .PATH: ${DIST} ${DIST}/pamu2fcfg
 
 CFFILE=${DIST}/configure.ac
-PACKAGE!= ${TOOL_SED} -ne 's/AC_INIT(\[\([a-zA-Z0-9_-]*\)\].*/\1/p' ${CFFILE}
-VERSION!= ${TOOL_SED} -ne 's/AC_INIT(\[[a-zA-Z0-9_-]*\], \[\([0-9.]*\)\].*/\1/p' ${CFFILE}
 
 CPPFLAGS+= -I${DIST} -I${.CURDIR}
-CPPFLAGS+= -DPACKAGE='"${PACKAGE}"' -DVERSION='"${VERSION}"'
-CPPFLAGS+= -DHAVE_UNISTD_H
 
 BINDIR=/usr/bin
 PROG=pamu2fcfg

Index: src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1
diff -u src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1:1.1 src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1:1.2
--- src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1:1.1	Mon Nov  2 01:40:11 2020
+++ src/external/bsd/pam-u2f/bin/pamu2fcfg/pamu2fcfg.1	Wed Jan 15 10:30:48 2025
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: pamu2fcfg
 .\"    Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author]
-.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\"      Date: 11/01/2020
+.\" Generator: DocBook XSL Stylesheets v1.79.2 <http://docbook.sf.net/>
+.\"      Date: Version 1.3.1
 .\"    Manual: PAM U2F Configuration Tool
 .\"    Source: pamu2fcfg
 .\"  Language: English
 .\"
-.TH "PAMU2FCFG" "1" "11/01/2020" "pamu2fcfg" "PAM U2F Configuration Tool"
+.TH "PAMU2FCFG" "1" "Version 1\&.3\&.1" "pamu2fcfg" "PAM U2F Configuration Tool"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -34,7 +34,7 @@ pamu2fcfg \- Configuration tool for the 
 \fBpamu2fcfg\fR [\fIOPTION\fR]\&...
 .SH "DESCRIPTION"
 .sp
-Perform a U2F registration procedure using a connected U2F token and output a configuration line that can be used with the U2F PAM module\&.
+Perform a FIDO2/U2F registration procedure using a connected authenticator and output a configuration line that can be used with the U2F PAM module\&.
 .SH "OPTIONS"
 .PP
 \fB\-d\fR, \fB\-\-debug\fR
@@ -49,13 +49,13 @@ Print help and exit
 .PP
 \fB\-o\fR, \fB\-\-origin\fR=\fISTRING\fR
 .RS 4
-Origin URL to use during registration\&. Defaults to pam://hostname
+Set the FIDO2 relying party ID to use during registration\&. Defaults to pam://hostname\&. Before pamu2fcfg v1\&.1\&.0, this set the U2F origin URL\&.
 .RE
 .PP
 \fB\-i\fR, \fB\-\-appid\fR=\fISTRING\fR
 .RS 4
-Application ID to use during registration\&. Defaults to
-\fBorigin\fR
+Set the FIDO2 relying party name to use during registration\&. Defaults to
+\fBorigin\fR\&. Before pamu2fcfg v1\&.1\&.0, this set the U2F application ID\&.
 .RE
 .PP
 \fB\-r\fR, \fB\-\-resident\fR
@@ -65,7 +65,12 @@ Generate a resident credential\&. Defaul
 .PP
 \fB\-t\fR, \fB\-\-type\fR=\fISTRING\fR
 .RS 4
-COSE type to use during registration (ES256 or RS256)\&. Defaults to ES256\&.
+COSE type to use during registration (ES256, EDDSA, or RS256)\&. Defaults to ES256\&.
+.RE
+.PP
+\fB\-P\fR, \fB\-\-no\-user\-presence\fR
+.RS 4
+Allow using the credential without ensuring the user\(cqs presence\&. Defaults to off\&.
 .RE
 .PP
 \fB\-N\fR, \fB\-\-pin\-verification\fR
@@ -79,25 +84,23 @@ Require user verification during authent
 .RE
 .sp
 \fB\-\-version\fR: \fBPrint version and exit\fR
-.sp
-Group: user (mutually exclusive)
 .PP
 \fB\-u\fR, \fB\-\-username\fR=\fISTRING\fR
 .RS 4
-The name of the user registering the device\&. Defaults to the current user name
+The name of the user registering the device\&. Defaults to the current user name\&.
 .RE
 .PP
 \fB\-n\fR, \fB\-\-nouser\fR
 .RS 4
-Print only registration information (keyHandle and public key)\&. Useful for appending
+Print only registration information (key handle, public key, and options)\&. Useful for appending\&.
 .RE
 .SH "BUGS"
 .sp
-Report pamu2fcfg bugs in the issue tracker: \m[blue]\fBhttps://github\&.com/Yubico/pam\-u2f/issues\fR\m[]
+Report pamu2fcfg bugs in the issue tracker: https://github\&.com/Yubico/pam\-u2f/issues
 .SH "SEE ALSO"
 .sp
-\fBpam_u2f\fR(8), \fBpam\fR(7)
+\fBpam_u2f\fR(8), \fBpam\fR(7), \fBfido2\-token\fR(1)
 .sp
-The pam\-u2f home page: \m[blue]\fBhttps://developers\&.yubico\&.com/pam\-u2f/\fR\m[]
+The pam\-u2f home page: https://developers\&.yubico\&.com/pam\-u2f/
 .sp
-YubiKeys can be obtained from Yubico: \m[blue]\fBhttps://www\&.yubico\&.com/\fR\m[]
+YubiKeys can be obtained from Yubico: https://www\&.yubico\&.com/

Index: src/external/bsd/pam-u2f/dist/expand.c
diff -u src/external/bsd/pam-u2f/dist/expand.c:1.1.1.1 src/external/bsd/pam-u2f/dist/expand.c:1.2
--- src/external/bsd/pam-u2f/dist/expand.c:1.1.1.1	Wed Jan 15 09:22:48 2025
+++ src/external/bsd/pam-u2f/dist/expand.c	Wed Jan 15 10:30:48 2025
@@ -54,7 +54,7 @@ char *expand_variables(const char *str, 
           buf_write(&head, &size, value, strlen(value)) != 0) {
         goto fail;
       }
-    } else if (buf_write_byte(&head, &size, *str) != 0) {
+    } else if (buf_write_byte(&head, &size, (uint8_t)*str) != 0) {
       goto fail;
     }
   }

Index: src/external/bsd/pam-u2f/dist/pam-u2f.c
diff -u src/external/bsd/pam-u2f/dist/pam-u2f.c:1.3 src/external/bsd/pam-u2f/dist/pam-u2f.c:1.4
--- src/external/bsd/pam-u2f/dist/pam-u2f.c:1.3	Fri Sep 24 09:11:38 2021
+++ src/external/bsd/pam-u2f/dist/pam-u2f.c	Wed Jan 15 10:30:48 2025
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2014-2019 Yubico AB - See COPYING
+ *  Copyright (C) 2014-2023 Yubico AB - See COPYING
  */
 
 /* Define which PAM interfaces we provide */
@@ -20,8 +20,11 @@
 #include <string.h>
 #include <errno.h>
 
-#include "util.h"
+#include "debug.h"
 #include "drop_privs.h"
+#include "util.h"
+
+#define free_const(a) free((void *) (uintptr_t) (a))
 
 /* If secure_getenv is not defined, define it here */
 #ifndef HAVE_SECURE_GETENV
@@ -33,133 +36,135 @@ char *secure_getenv(const char *name) {
 #endif
 
 static void parse_cfg(int flags __unused, int argc, const char **argv, cfg_t *cfg) {
-#ifndef WITH_FUZZING
-  struct stat st;
-#endif
-  FILE *file = NULL;
-  int fd = -1;
   int i;
 
   memset(cfg, 0, sizeof(cfg_t));
-  cfg->debug_file = stderr;
+  cfg->debug_file = DEFAULT_DEBUG_FILE;
   cfg->userpresence = -1;
   cfg->userverification = -1;
   cfg->pinverification = -1;
 
   for (i = 0; i < argc; i++) {
-    if (strncmp(argv[i], "max_devices=", 12) == 0)
+    if (strncmp(argv[i], "max_devices=", 12) == 0) {
       sscanf(argv[i], "max_devices=%u", &cfg->max_devs);
-    if (strcmp(argv[i], "manual") == 0)
+    } else if (strcmp(argv[i], "manual") == 0) {
       cfg->manual = 1;
-    if (strcmp(argv[i], "debug") == 0)
+    } else if (strcmp(argv[i], "debug") == 0) {
       cfg->debug = 1;
-    if (strcmp(argv[i], "nouserok") == 0)
+    } else if (strcmp(argv[i], "nouserok") == 0) {
       cfg->nouserok = 1;
-    if (strcmp(argv[i], "openasuser") == 0)
+    } else if (strcmp(argv[i], "openasuser") == 0) {
       cfg->openasuser = 1;
-    if (strcmp(argv[i], "alwaysok") == 0)
+    } else if (strcmp(argv[i], "alwaysok") == 0) {
       cfg->alwaysok = 1;
-    if (strcmp(argv[i], "interactive") == 0)
+    } else if (strcmp(argv[i], "interactive") == 0) {
       cfg->interactive = 1;
-    if (strcmp(argv[i], "cue") == 0)
+    } else if (strcmp(argv[i], "cue") == 0) {
       cfg->cue = 1;
-    if (strcmp(argv[i], "nodetect") == 0)
+    } else if (strcmp(argv[i], "nodetect") == 0) {
       cfg->nodetect = 1;
-    if (strncmp(argv[i], "userpresence=", 13) == 0)
+    } else if (strcmp(argv[i], "expand") == 0) {
+      cfg->expand = 1;
+    } else if (strncmp(argv[i], "userpresence=", 13) == 0) {
       sscanf(argv[i], "userpresence=%d", &cfg->userpresence);
-    if (strncmp(argv[i], "userverification=", 17) == 0)
+    } else if (strncmp(argv[i], "userverification=", 17) == 0) {
       sscanf(argv[i], "userverification=%d", &cfg->userverification);
-    if (strncmp(argv[i], "pinverification=", 16) == 0)
+    } else if (strncmp(argv[i], "pinverification=", 16) == 0) {
       sscanf(argv[i], "pinverification=%d", &cfg->pinverification);
-    if (strncmp(argv[i], "authfile=", 9) == 0)
+    } else if (strncmp(argv[i], "authfile=", 9) == 0) {
       cfg->auth_file = argv[i] + 9;
-    if (strncmp(argv[i], "sshformat", 9) == 0)
+    } else if (strcmp(argv[i], "sshformat") == 0) {
       cfg->sshformat = 1;
-    if (strncmp(argv[i], "authpending_file=", 17) == 0)
+    } else if (strncmp(argv[i], "authpending_file=", 17) == 0) {
       cfg->authpending_file = argv[i] + 17;
-    if (strncmp(argv[i], "origin=", 7) == 0)
+    } else if (strncmp(argv[i], "origin=", 7) == 0) {
       cfg->origin = argv[i] + 7;
-    if (strncmp(argv[i], "appid=", 6) == 0)
+    } else if (strncmp(argv[i], "appid=", 6) == 0) {
       cfg->appid = argv[i] + 6;
-    if (strncmp(argv[i], "prompt=", 7) == 0)
+    } else if (strncmp(argv[i], "prompt=", 7) == 0) {
       cfg->prompt = argv[i] + 7;
-    if (strncmp(argv[i], "cue_prompt=", 11) == 0)
+    } else if (strncmp(argv[i], "cue_prompt=", 11) == 0) {
       cfg->cue_prompt = argv[i] + 11;
-    if (strncmp(argv[i], "debug_file=", 11) == 0) {
-      if (cfg->is_custom_debug_file)
-        fclose(cfg->debug_file);
-      cfg->debug_file = stderr;
-      cfg->is_custom_debug_file = 0;
+    } else if (strncmp(argv[i], "debug_file=", 11) == 0) {
       const char *filename = argv[i] + 11;
-      if (strncmp(filename, "stdout", 6) == 0) {
-        cfg->debug_file = stdout;
-      } else if (strncmp(filename, "stderr", 6) == 0) {
-        cfg->debug_file = stderr;
-      } else if (strncmp(filename, "syslog", 6) == 0) {
-        cfg->debug_file = (FILE *) -1;
-      } else {
-        fd = open(filename,
-                  O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_NOCTTY);
-#ifndef WITH_FUZZING
-        if (fd >= 0 && (fstat(fd, &st) == 0) && S_ISREG(st.st_mode)) {
-#else
-        if (fd >= 0) {
-#endif
-          file = fdopen(fd, "a");
-          if (file != NULL) {
-            cfg->debug_file = file;
-            cfg->is_custom_debug_file = 1;
-            file = NULL;
-            fd = -1;
-          }
-        }
-      }
+      debug_close(cfg->debug_file);
+      cfg->debug_file = debug_open(filename);
     }
   }
 
-  if (cfg->debug) {
-    D(cfg->debug_file, "called.");
-    D(cfg->debug_file, "flags %d argc %d", flags, argc);
-    for (i = 0; i < argc; i++) {
-      D(cfg->debug_file, "argv[%d]=%s", i, argv[i]);
-    }
-    D(cfg->debug_file, "max_devices=%d", cfg->max_devs);
-    D(cfg->debug_file, "debug=%d", cfg->debug);
-    D(cfg->debug_file, "interactive=%d", cfg->interactive);
-    D(cfg->debug_file, "cue=%d", cfg->cue);
-    D(cfg->debug_file, "nodetect=%d", cfg->nodetect);
-    D(cfg->debug_file, "userpresence=%d", cfg->userpresence);
-    D(cfg->debug_file, "userverification=%d", cfg->userverification);
-    D(cfg->debug_file, "pinverification=%d", cfg->pinverification);
-    D(cfg->debug_file, "manual=%d", cfg->manual);
-    D(cfg->debug_file, "nouserok=%d", cfg->nouserok);
-    D(cfg->debug_file, "openasuser=%d", cfg->openasuser);
-    D(cfg->debug_file, "alwaysok=%d", cfg->alwaysok);
-    D(cfg->debug_file, "sshformat=%d", cfg->sshformat);
-    D(cfg->debug_file, "authfile=%s",
-      cfg->auth_file ? cfg->auth_file : "(null)");
-    D(cfg->debug_file, "authpending_file=%s",
-      cfg->authpending_file ? cfg->authpending_file : "(null)");
-    D(cfg->debug_file, "origin=%s", cfg->origin ? cfg->origin : "(null)");
-    D(cfg->debug_file, "appid=%s", cfg->appid ? cfg->appid : "(null)");
-    D(cfg->debug_file, "prompt=%s", cfg->prompt ? cfg->prompt : "(null)");
+  debug_dbg(cfg, "called.");
+  debug_dbg(cfg, "flags %d argc %d", flags, argc);
+  for (i = 0; i < argc; i++) {
+    debug_dbg(cfg, "argv[%d]=%s", i, argv[i]);
   }
+  debug_dbg(cfg, "max_devices=%d", cfg->max_devs);
+  debug_dbg(cfg, "debug=%d", cfg->debug);
+  debug_dbg(cfg, "interactive=%d", cfg->interactive);
+  debug_dbg(cfg, "cue=%d", cfg->cue);
+  debug_dbg(cfg, "nodetect=%d", cfg->nodetect);
+  debug_dbg(cfg, "userpresence=%d", cfg->userpresence);
+  debug_dbg(cfg, "userverification=%d", cfg->userverification);
+  debug_dbg(cfg, "pinverification=%d", cfg->pinverification);
+  debug_dbg(cfg, "manual=%d", cfg->manual);
+  debug_dbg(cfg, "nouserok=%d", cfg->nouserok);
+  debug_dbg(cfg, "openasuser=%d", cfg->openasuser);
+  debug_dbg(cfg, "alwaysok=%d", cfg->alwaysok);
+  debug_dbg(cfg, "sshformat=%d", cfg->sshformat);
+  debug_dbg(cfg, "expand=%d", cfg->expand);
+  debug_dbg(cfg, "authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)");
+  debug_dbg(cfg, "authpending_file=%s",
+            cfg->authpending_file ? cfg->authpending_file : "(null)");
+  debug_dbg(cfg, "origin=%s", cfg->origin ? cfg->origin : "(null)");
+  debug_dbg(cfg, "appid=%s", cfg->appid ? cfg->appid : "(null)");
+  debug_dbg(cfg, "prompt=%s", cfg->prompt ? cfg->prompt : "(null)");
+}
 
-  if (fd != -1)
-    close(fd);
+static void interactive_prompt(pam_handle_t *pamh, const cfg_t *cfg) {
+  char *tmp = NULL;
 
-  if (file != NULL)
-    fclose(file);
+  tmp = converse(pamh, PAM_PROMPT_ECHO_ON,
+                 cfg->prompt != NULL ? cfg->prompt : DEFAULT_PROMPT);
+
+  free(tmp);
 }
 
-#ifdef DBG
-#undef DBG
-#endif
-#define DBG(...)                                                               \
-  if (cfg->debug) {                                                            \
-    D(cfg->debug_file, __VA_ARGS__);                                           \
+static char *resolve_authfile_path(const cfg_t *cfg, const struct passwd *user,
+                                   int *openasuser) {
+  char *authfile = NULL;
+  const char *dir = NULL;
+  const char *path = NULL;
+
+  *openasuser = geteuid() == 0; /* user files, drop privileges */
+
+  if (cfg->auth_file == NULL) {
+    if ((dir = secure_getenv(DEFAULT_AUTHFILE_DIR_VAR)) == NULL) {
+      debug_dbg(cfg, "Variable %s is not set, using default",
+                DEFAULT_AUTHFILE_DIR_VAR);
+      dir = user->pw_dir;
+      path = cfg->sshformat ? DEFAULT_AUTHFILE_DIR_SSH "/" DEFAULT_AUTHFILE_SSH
+                            : DEFAULT_AUTHFILE_DIR "/" DEFAULT_AUTHFILE;
+    } else {
+      debug_dbg(cfg, "Variable %s set to %s", DEFAULT_AUTHFILE_DIR_VAR, dir);
+      *openasuser = 0; /* documented exception, require explicit openasuser */
+      path = cfg->sshformat ? DEFAULT_AUTHFILE_SSH : DEFAULT_AUTHFILE;
+      if (!cfg->openasuser) {
+        debug_dbg(cfg, "WARNING: not dropping privileges when reading the "
+                       "authentication file, please consider setting "
+                       "openasuser=1 in the module configuration");
+      }
+    }
+  } else {
+    dir = user->pw_dir;
+    path = cfg->auth_file;
   }
 
+  if (dir == NULL || *dir != '/' || path == NULL ||
+      asprintf(&authfile, "%s/%s", dir, path) == -1)
+    authfile = NULL;
+
+  return authfile;
+}
+
 /* PAM entry point for authentication verification */
 int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
                         const char **argv) {
@@ -170,13 +175,8 @@ int pam_sm_authenticate(pam_handle_t *pa
   cfg_t cfg_st;
   cfg_t *cfg = &cfg_st;
   char buffer[BUFSIZE];
-  char *buf = NULL;
-  char *authfile_dir;
-  size_t authfile_dir_len;
-  const char *default_authfile;
-  const char *default_authfile_dir;
   int pgu_ret, gpn_ret;
-  int retval = PAM_IGNORE;
+  int retval = PAM_ABORT;
   device_t *devices = NULL;
   unsigned n_devices = 0;
   int openasuser = 0;
@@ -192,19 +192,20 @@ int pam_sm_authenticate(pam_handle_t *pa
   if (!cfg->origin) {
     if (!cfg->sshformat) {
       strcpy(buffer, DEFAULT_ORIGIN_PREFIX);
-
       if (gethostname(buffer + strlen(DEFAULT_ORIGIN_PREFIX),
                       BUFSIZE - strlen(DEFAULT_ORIGIN_PREFIX)) == -1) {
-        DBG("Unable to get host name");
+        debug_dbg(cfg, "Unable to get host name");
+        retval = PAM_SYSTEM_ERR;
         goto done;
       }
     } else {
       strcpy(buffer, SSH_ORIGIN);
     }
-    DBG("Origin not specified, using \"%s\"", buffer);
+    debug_dbg(cfg, "Origin not specified, using \"%s\"", buffer);
     cfg->origin = strdup(buffer);
     if (!cfg->origin) {
-      DBG("Unable to allocate memory");
+      debug_dbg(cfg, "Unable to allocate memory");
+      retval = PAM_BUF_ERR;
       goto done;
     } else {
       should_free_origin = 1;
@@ -212,11 +213,12 @@ int pam_sm_authenticate(pam_handle_t *pa
   }
 
   if (!cfg->appid) {
-    DBG("Appid not specified, using the same value of origin (%s)",
-        cfg->origin);
+    debug_dbg(cfg, "Appid not specified, using the value of origin (%s)",
+              cfg->origin);
     cfg->appid = strdup(cfg->origin);
     if (!cfg->appid) {
-      DBG("Unable to allocate memory")
+      debug_dbg(cfg, "Unable to allocate memory");
+      retval = PAM_BUF_ERR;
       goto done;
     } else {
       should_free_appid = 1;
@@ -224,7 +226,8 @@ int pam_sm_authenticate(pam_handle_t *pa
   }
 
   if (cfg->max_devs == 0) {
-    DBG("Maximum devices number not set. Using default (%d)", MAX_DEVS);
+    debug_dbg(cfg, "Maximum number of devices not set. Using default (%d)",
+              MAX_DEVS);
     cfg->max_devs = MAX_DEVS;
   }
 #if WITH_FUZZING
@@ -234,155 +237,83 @@ int pam_sm_authenticate(pam_handle_t *pa
 
   devices = calloc(cfg->max_devs, sizeof(device_t));
   if (!devices) {
-    DBG("Unable to allocate memory");
-    retval = PAM_IGNORE;
+    debug_dbg(cfg, "Unable to allocate memory");
+    retval = PAM_BUF_ERR;
     goto done;
   }
 
   pgu_ret = pam_get_user(pamh, &user, NULL);
   if (pgu_ret != PAM_SUCCESS || user == NULL) {
-    DBG("Unable to access user %s", user);
+    debug_dbg(cfg, "Unable to get username from PAM");
     retval = PAM_CONV_ERR;
     goto done;
   }
 
-  DBG("Requesting authentication for user %s", user);
+  debug_dbg(cfg, "Requesting authentication for user %s", user);
 
   gpn_ret = getpwnam_r(user, &pw_s, buffer, sizeof(buffer), &pw);
   if (gpn_ret != 0 || pw == NULL || pw->pw_dir == NULL ||
       pw->pw_dir[0] != '/') {
-    DBG("Unable to retrieve credentials for user %s, (%s)", user,
-        strerror(errno));
-    retval = PAM_USER_UNKNOWN;
+    debug_dbg(cfg, "Unable to retrieve credentials for user %s, (%s)", user,
+              strerror(errno));
+    retval = PAM_SYSTEM_ERR;
     goto done;
   }
 
-  DBG("Found user %s", user);
-  DBG("Home directory for %s is %s", user, pw->pw_dir);
-
-  if (!cfg->sshformat) {
-    default_authfile = DEFAULT_AUTHFILE;
-    default_authfile_dir = DEFAULT_AUTHFILE_DIR;
-  } else {
-    default_authfile = DEFAULT_AUTHFILE_SSH;
-    default_authfile_dir = DEFAULT_AUTHFILE_DIR_SSH;
-  }
-
-  if (!cfg->auth_file) {
-    buf = NULL;
-    authfile_dir = secure_getenv(DEFAULT_AUTHFILE_DIR_VAR);
-    if (!authfile_dir) {
-      DBG("Variable %s is not set. Using default value ($HOME%s/)",
-          DEFAULT_AUTHFILE_DIR_VAR, default_authfile_dir);
-      authfile_dir_len = strlen(pw->pw_dir) + strlen(default_authfile_dir) +
-                         strlen(default_authfile) + 1;
-      buf = malloc(sizeof(char) * (authfile_dir_len));
-
-      if (!buf) {
-        DBG("Unable to allocate memory");
-        retval = PAM_IGNORE;
-        goto done;
-      }
-
-      /* Opening a file in a users $HOME, need to drop privs for security */
-      openasuser = geteuid() == 0 ? 1 : 0;
-
-      snprintf(buf, authfile_dir_len, "%s%s%s", pw->pw_dir,
-               default_authfile_dir, default_authfile);
-    } else {
-      DBG("Variable %s set to %s", DEFAULT_AUTHFILE_DIR_VAR, authfile_dir);
-      authfile_dir_len = strlen(authfile_dir) + strlen(default_authfile) + 1;
-      buf = malloc(sizeof(char) * (authfile_dir_len));
-
-      if (!buf) {
-        DBG("Unable to allocate memory");
-        retval = PAM_IGNORE;
-        goto done;
-      }
-
-      snprintf(buf, authfile_dir_len, "%s%s", authfile_dir, default_authfile);
+  debug_dbg(cfg, "Found user %s", user);
+  debug_dbg(cfg, "Home directory for %s is %s", user, pw->pw_dir);
 
-      if (!cfg->openasuser) {
-        DBG("WARNING: not dropping privileges when reading %s, please "
-            "consider setting openasuser=1 in the module configuration",
-            buf);
-      }
+  // Perform variable expansion.
+  if (cfg->expand && cfg->auth_file) {
+    if ((cfg->auth_file = expand_variables(cfg->auth_file, user)) == NULL) {
+      debug_dbg(cfg, "Failed to perform variable expansion");
+      retval = PAM_BUF_ERR;
+      goto done;
     }
-
-    DBG("Using authentication file %s", buf);
-
-    cfg->auth_file = buf; /* cfg takes ownership */
     should_free_auth_file = 1;
-    buf = NULL;
-  } else {
-    if (cfg->auth_file[0] != '/') {
-      /* Individual authorization mapping by user: auth_file is not
-          absolute path, so prepend user home dir. */
-      openasuser = geteuid() == 0 ? 1 : 0;
-
-      authfile_dir_len =
-        strlen(pw->pw_dir) + strlen("/") + strlen(cfg->auth_file) + 1;
-      buf = malloc(sizeof(char) * (authfile_dir_len));
-
-      if (!buf) {
-        DBG("Unable to allocate memory");
-        retval = PAM_IGNORE;
-        goto done;
-      }
-
-      snprintf(buf, authfile_dir_len, "%s/%s", pw->pw_dir, cfg->auth_file);
-
-      cfg->auth_file = buf; /* update cfg */
-      should_free_auth_file = 1;
-      buf = NULL;
+  }
+  // Resolve default or relative paths.
+  if (!cfg->auth_file || cfg->auth_file[0] != '/') {
+    char *tmp = resolve_authfile_path(cfg, pw, &openasuser);
+    if (tmp == NULL) {
+      debug_dbg(cfg, "Could not resolve authfile path");
+      retval = PAM_BUF_ERR;
+      goto done;
     }
-
-    DBG("Using authentication file %s", cfg->auth_file);
+    if (should_free_auth_file) {
+      free_const(cfg->auth_file);
+    }
+    cfg->auth_file = tmp;
+    should_free_auth_file = 1;
   }
 
+  debug_dbg(cfg, "Using authentication file %s", cfg->auth_file);
+
   if (!openasuser) {
     openasuser = geteuid() == 0 && cfg->openasuser;
   }
   if (openasuser) {
-    DBG("Dropping privileges");
+    debug_dbg(cfg, "Dropping privileges");
     if (pam_modutil_drop_priv(pamh, &privs, pw)) {
-      DBG("Unable to switch user to uid %i", pw->pw_uid);
-      retval = PAM_IGNORE;
+      debug_dbg(cfg, "Unable to switch user to uid %i", pw->pw_uid);
+      retval = PAM_SYSTEM_ERR;
       goto done;
     }
-    DBG("Switched to uid %i", pw->pw_uid);
+    debug_dbg(cfg, "Switched to uid %i", pw->pw_uid);
   }
   retval = get_devices_from_authfile(cfg, user, devices, &n_devices);
 
   if (openasuser) {
     if (pam_modutil_regain_priv(pamh, &privs)) {
-      DBG("could not restore privileges");
-      retval = PAM_IGNORE;
+      debug_dbg(cfg, "could not restore privileges");
+      retval = PAM_SYSTEM_ERR;
       goto done;
     }
-    DBG("Restored privileges");
+    debug_dbg(cfg, "Restored privileges");
   }
 
-  if (retval != 1) {
-    // for nouserok; make sure errors in get_devices_from_authfile don't
-    // result in valid devices
-    n_devices = 0;
-  }
-
-  if (n_devices == 0) {
-    if (cfg->nouserok) {
-      DBG("Found no devices but nouserok specified. Skipping authentication");
-      retval = PAM_SUCCESS;
-      goto done;
-    } else if (retval != 1) {
-      DBG("Unable to get devices from file %s", cfg->auth_file);
-      retval = PAM_AUTHINFO_UNAVAIL;
-      goto done;
-    } else {
-      DBG("Found no devices. Aborting.");
-      retval = PAM_AUTHINFO_UNAVAIL;
-      goto done;
-    }
+  if (retval != PAM_SUCCESS) {
+    goto done;
   }
 
   // Determine the full path for authpending_file in order to emit touch request
@@ -394,23 +325,23 @@ int pam_sm_authenticate(pam_handle_t *pa
       cfg->authpending_file = strdup(buffer);
     }
     if (!cfg->authpending_file) {
-      DBG("Unable to allocate memory for the authpending_file, touch request "
-          "notifications will not be emitted");
+      debug_dbg(cfg, "Unable to allocate memory for the authpending_file, "
+                     "touch request notifications will not be emitted");
     } else {
       should_free_authpending_file = 1;
     }
   } else {
     if (strlen(cfg->authpending_file) == 0) {
-      DBG("authpending_file is set to an empty value, touch request "
-          "notifications will be disabled");
+      debug_dbg(cfg, "authpending_file is set to an empty value, touch request "
+                     "notifications will be disabled");
       cfg->authpending_file = NULL;
     }
   }
 
   int authpending_file_descriptor = -1;
   if (cfg->authpending_file) {
-    DBG("Using file '%s' for emitting touch request notifications",
-        cfg->authpending_file);
+    debug_dbg(cfg, "Touch request notifications will be emitted via '%s'",
+              cfg->authpending_file);
 
     // Open (or create) the authpending_file to indicate that we start waiting
     // for a touch
@@ -418,20 +349,15 @@ int pam_sm_authenticate(pam_handle_t *pa
       open(cfg->authpending_file,
            O_RDONLY | O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_NOCTTY, 0664);
     if (authpending_file_descriptor < 0) {
-      DBG("Unable to emit 'authentication started' notification by opening the "
-          "file '%s', (%s)",
-          cfg->authpending_file, strerror(errno));
+      debug_dbg(cfg, "Unable to emit 'authentication started' notification: %s",
+                strerror(errno));
     }
   }
 
   if (cfg->manual == 0) {
     if (cfg->interactive) {
-      buf = converse(pamh, PAM_PROMPT_ECHO_ON,
-                     cfg->prompt != NULL ? cfg->prompt : DEFAULT_PROMPT);
-      free(buf);
-      buf = NULL;
+      interactive_prompt(pamh, cfg);
     }
-
     retval = do_authentication(cfg, devices, n_devices, pamh);
   } else {
     retval = do_manual_authentication(cfg, devices, n_devices, pamh);
@@ -440,28 +366,14 @@ int pam_sm_authenticate(pam_handle_t *pa
   // Close the authpending_file to indicate that we stop waiting for a touch
   if (authpending_file_descriptor >= 0) {
     if (close(authpending_file_descriptor) < 0) {
-      DBG("Unable to emit 'authentication stopped' notification by closing the "
-          "file '%s', (%s)",
-          cfg->authpending_file, strerror(errno));
+      debug_dbg(cfg, "Unable to emit 'authentication stopped' notification: %s",
+                strerror(errno));
     }
   }
 
-  if (retval != 1) {
-    DBG("do_authentication returned %d", retval);
-    retval = PAM_AUTH_ERR;
-    goto done;
-  }
-
-  retval = PAM_SUCCESS;
-
 done:
   free_devices(devices, n_devices);
 
-  if (buf) {
-    free(buf);
-    buf = NULL;
-  }
-#define free_const(a) free((void *) (uintptr_t)(a))
   if (should_free_origin) {
     free_const(cfg->origin);
     cfg->origin = NULL;
@@ -483,14 +395,13 @@ done:
   }
 
   if (cfg->alwaysok && retval != PAM_SUCCESS) {
-    DBG("alwaysok needed (otherwise return with %d)", retval);
+    debug_dbg(cfg, "alwaysok needed (otherwise return with %d)", retval);
     retval = PAM_SUCCESS;
   }
-  DBG("done. [%s]", pam_strerror(pamh, retval));
+  debug_dbg(cfg, "done. [%s]", pam_strerror(pamh, retval));
 
-  if (cfg->is_custom_debug_file) {
-    fclose(cfg->debug_file);
-  }
+  debug_close(cfg->debug_file);
+  cfg->debug_file = DEFAULT_DEBUG_FILE;
 
   return retval;
 }
Index: src/external/bsd/pam-u2f/dist/util.h
diff -u src/external/bsd/pam-u2f/dist/util.h:1.3 src/external/bsd/pam-u2f/dist/util.h:1.4
--- src/external/bsd/pam-u2f/dist/util.h:1.3	Fri Sep 24 09:11:38 2021
+++ src/external/bsd/pam-u2f/dist/util.h	Wed Jan 15 10:30:48 2025
@@ -10,27 +10,18 @@
 
 #define BUFSIZE 1024
 #define MAX_DEVS 24
-#define PK_LEN 130 // Public key
-#define KH_LEN 86  // Key handle
-#define RD_LEN 40  // Rounding
-#define DEVSIZE (((PK_LEN) + (KH_LEN) + (RD_LEN)))
 #define DEFAULT_AUTHFILE_DIR_VAR "XDG_CONFIG_HOME"
-#define DEFAULT_AUTHFILE "/Yubico/u2f_keys"
-#define DEFAULT_AUTHFILE_SSH "/id_ecdsa_sk"
-#define DEFAULT_AUTHFILE_DIR "/.config"
-#define DEFAULT_AUTHFILE_DIR_SSH "/.ssh"
+#define DEFAULT_AUTHFILE "Yubico/u2f_keys"
+#define DEFAULT_AUTHFILE_SSH "id_ecdsa_sk"
+#define DEFAULT_AUTHFILE_DIR ".config"
+#define DEFAULT_AUTHFILE_DIR_SSH ".ssh"
 #define DEFAULT_AUTHPENDING_FILE_PATH "/var/run/user/%d/pam-u2f-authpending"
 #define DEFAULT_PROMPT "Insert your U2F device, then press ENTER."
 #define DEFAULT_CUE "Please touch the device."
 #define DEFAULT_ORIGIN_PREFIX "pam://"
 #define SSH_ORIGIN "ssh:"
-#define DEBUG_STR "debug(pam_u2f): %s:%d (%s): "
 
-#if defined(DEBUG_PAM)
-#define D(file, ...) _debug(file, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#else
-#define D(file, ...) ((void) 0)
-#endif /* DEBUG_PAM */
+#define DEVLIST_LEN 64
 
 typedef struct {
   unsigned max_devs;
@@ -46,6 +37,7 @@ typedef struct {
   int userverification;
   int pinverification;
   int sshformat;
+  int expand;
   const char *auth_file;
   const char *authpending_file;
   const char *origin;
@@ -53,7 +45,6 @@ typedef struct {
   const char *prompt;
   const char *cue_prompt;
   FILE *debug_file;
-  int is_custom_debug_file;
 } cfg_t;
 
 typedef struct {
@@ -76,13 +67,7 @@ char *converse(pam_handle_t *pamh, int e
 int random_bytes(void *, size_t);
 int cose_type(const char *, int *);
 const char *cose_string(int);
-
-#ifdef __GNUC__
-void _debug(FILE *, const char *, int, const char *, const char *, ...)
-  __attribute__((__format__(printf, 5, 6)));
-#else
-void _debug(FILE *, const char *, int, const char *, const char *, ...);
-#endif /* __GNUC__ */
+char *expand_variables(const char *, const char *);
 
 #if !defined(HAVE_EXPLICIT_BZERO)
 void explicit_bzero(void *, size_t);

Index: src/external/bsd/pam-u2f/dist/util.c
diff -u src/external/bsd/pam-u2f/dist/util.c:1.4 src/external/bsd/pam-u2f/dist/util.c:1.5
--- src/external/bsd/pam-u2f/dist/util.c:1.4	Fri Sep 24 09:08:16 2021
+++ src/external/bsd/pam-u2f/dist/util.c	Wed Jan 15 10:30:48 2025
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2019 Yubico AB - See COPYING
+ * Copyright (C) 2014-2022 Yubico AB - See COPYING
  */
 
 #include <fido.h>
@@ -15,8 +15,6 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <stdarg.h>
-#include <syslog.h>
 #include <pwd.h>
 #include <errno.h>
 #include <unistd.h>
@@ -24,8 +22,10 @@
 #include <arpa/inet.h>
 
 #include "b64.h"
+#include "debug.h"
 #include "util.h"
 
+#define SSH_MAX_SIZE 8192
 #define SSH_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n"
 #define SSH_HEADER_LEN (sizeof(SSH_HEADER) - 1)
 #define SSH_TRAILER "-----END OPENSSH PRIVATE KEY-----\n"
@@ -166,27 +166,69 @@ static void reset_device(device_t *devic
   memset(device, 0, sizeof(*device));
 }
 
+static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) {
+  const char *delim = ",";
+  const char *kh, *pk, *type, *attr;
+  char *saveptr = NULL;
+
+  memset(cred, 0, sizeof(*cred));
+
+  if ((kh = strtok_r(s, delim, &saveptr)) == NULL) {
+    debug_dbg(cfg, "Missing key handle");
+    goto fail;
+  }
+
+  if ((pk = strtok_r(NULL, delim, &saveptr)) == NULL) {
+    debug_dbg(cfg, "Missing public key");
+    goto fail;
+  }
+
+  if ((type = strtok_r(NULL, delim, &saveptr)) == NULL) {
+    debug_dbg(cfg, "Old format, assume es256 and +presence");
+    cred->old_format = 1;
+    type = "es256";
+    attr = "+presence";
+  } else if ((attr = strtok_r(NULL, delim, &saveptr)) == NULL) {
+    debug_dbg(cfg, "Empty attributes");
+    attr = "";
+  }
+
+  cred->keyHandle = cred->old_format ? normal_b64(kh) : strdup(kh);
+  if (cred->keyHandle == NULL || (cred->publicKey = strdup(pk)) == NULL ||
+      (cred->coseType = strdup(type)) == NULL ||
+      (cred->attributes = strdup(attr)) == NULL) {
+    debug_dbg(cfg, "Unable to allocate memory for credential components");
+    goto fail;
+  }
+
+  return 1;
+
+fail:
+  reset_device(cred);
+  return 0;
+}
+
 static int parse_native_format(const cfg_t *cfg, const char *username,
-                               char *buf, FILE *opwfile, device_t *devices,
+                               FILE *opwfile, device_t *devices,
                                unsigned *n_devs) {
 
   char *s_user, *s_credential;
-  const char *s_token;
+  char *buf = NULL;
+  size_t bufsiz = 0;
+  ssize_t len;
   unsigned i;
+  int r = 0;
 
-  while (fgets(buf, (int) (DEVSIZE * (cfg->max_devs - 1)), opwfile)) {
+  while ((len = getline(&buf, &bufsiz, opwfile)) != -1) {
     char *saveptr = NULL;
-    size_t len = strlen(buf);
     if (len > 0 && buf[len - 1] == '\n')
       buf[len - 1] = '\0';
 
-    if (cfg->debug)
-      D(cfg->debug_file, "Authorization line: %s", buf);
+    debug_dbg(cfg, "Read %zu bytes", len);
 
     s_user = strtok_r(buf, ":", &saveptr);
     if (s_user && strcmp(username, s_user) == 0) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Matched user: %s", s_user);
+      debug_dbg(cfg, "Matched user: %s", s_user);
 
       // only keep last line for this user
       for (i = 0; i < *n_devs; i++) {
@@ -195,197 +237,96 @@ static int parse_native_format(const cfg
       *n_devs = 0;
 
       i = 0;
-      while ((s_credential = strtok_r(NULL, ":", &saveptr)) != NULL) {
-        // s_credential is the whole line now
-        char *credsaveptr = NULL;
-
+      while ((s_credential = strtok_r(NULL, ":", &saveptr))) {
         if ((*n_devs)++ > cfg->max_devs - 1) {
           *n_devs = cfg->max_devs;
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Found more than %d devices, ignoring the remaining ones",
-              cfg->max_devs);
-          }
+          debug_dbg(cfg,
+                    "Found more than %d devices, ignoring the remaining ones",
+                    cfg->max_devs);
           break;
         }
 
-        reset_device(&devices[i]);
-
-        s_token = strtok_r(s_credential, ",", &credsaveptr);
-
-        if (!s_token) {
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Unable to retrieve keyHandle for device %d", i + 1);
-          }
-          return -1;
-        }
-
-        if (cfg->debug) {
-          D(cfg->debug_file, "KeyHandle for device number %d: %s", i + 1,
-            s_token);
-        }
-
-        devices[i].keyHandle = strdup(s_token);
-
-        if (!devices[i].keyHandle) {
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Unable to allocate memory for keyHandle number %d", i);
-          }
-          return -1;
-        }
-
-        if (is_resident(devices[i].keyHandle) && cfg->debug) {
-          D(cfg->debug_file, "Credential is resident");
-        }
-
-        s_token = strtok_r(NULL, ",", &credsaveptr);
-
-        if (!s_token) {
-          if (cfg->debug) {
-            D(cfg->debug_file, "Unable to retrieve publicKey number %d", i + 1);
-          }
-          return -1;
-        }
-
-        if (cfg->debug) {
-          D(cfg->debug_file, "publicKey for device number %d: %s", i + 1,
-            s_token);
-        }
-
-        devices[i].publicKey = strdup(s_token);
-
-        if (!devices[i].publicKey) {
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Unable to allocate memory for publicKey number %d", i);
-          }
-          return -1;
-        }
-
-        s_token = strtok_r(NULL, ",", &credsaveptr);
-
-        if (!s_token) {
-          if (cfg->debug) {
-            D(cfg->debug_file, "Unable to retrieve COSE type %d", i + 1);
-            D(cfg->debug_file, "Assuming ES256 (backwards compatibility)");
-          }
-          devices[i].old_format = 1;
-          devices[i].coseType = strdup("es256");
-        } else {
-          if (cfg->debug) {
-            D(cfg->debug_file, "COSE type for device number %d: %s", i + 1,
-              s_token);
-          }
-          devices[i].coseType = strdup(s_token);
-        }
-
-        if (!devices[i].coseType) {
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Unable to allocate memory for COSE type number %d", i);
-          }
-          return -1;
-        }
-
-        s_token = strtok_r(NULL, ",", &credsaveptr);
-
-        if (devices[i].old_format == 1) {
-          if (cfg->debug) {
-            D(cfg->debug_file, "Old format for device %d, no attributes",
-              i + 1);
-            D(cfg->debug_file, "Assuming 'presence' (backwards compatibility)");
-          }
-          s_token = "+presence";
-        } else if (!s_token) {
-          s_token = "";
-        }
-
-        if (cfg->debug) {
-          D(cfg->debug_file, "Attributes for device number %d: %s", i + 1,
-            s_token);
-        }
-        devices[i].attributes = strdup(s_token);
-
-        if (!devices[i].attributes) {
-          if (cfg->debug) {
-            D(cfg->debug_file,
-              "Unable to allocate memory for attributes number %d", i);
-          }
-          return -1;
-        }
-
-        if (devices[i].old_format) {
-          char *websafe_b64 = devices[i].keyHandle;
-          devices[i].keyHandle = normal_b64(websafe_b64);
-          free(websafe_b64);
-          if (!devices[i].keyHandle) {
-            if (cfg->debug) {
-              D(cfg->debug_file,
-                "Unable to allocate memory for keyHandle number %d", i);
-            }
-            return -1;
-          }
+        if (!parse_native_credential(cfg, s_credential, &devices[i])) {
+          debug_dbg(cfg, "Failed to parse credential");
+          goto fail;
         }
 
+        debug_dbg(cfg, "KeyHandle for device number %u: %s", i + 1,
+                  devices[i].keyHandle);
+        debug_dbg(cfg, "publicKey for device number %u: %s", i + 1,
+                  devices[i].publicKey);
+        debug_dbg(cfg, "COSE type for device number %u: %s", i + 1,
+                  devices[i].coseType);
+        debug_dbg(cfg, "Attributes for device number %u: %s", i + 1,
+                  devices[i].attributes);
         i++;
       }
     }
   }
 
-  return 1;
+  if (!feof(opwfile)) {
+    debug_dbg(cfg, "authfile parsing ended before eof (%d)", errno);
+    goto fail;
+  }
+
+  r = 1;
+fail:
+  free(buf);
+  return r;
 }
 
-static int load_ssh_key(const cfg_t *cfg, char *buf, size_t buf_size,
-                        FILE *opwfile, size_t opwfile_size) {
-  char *cp = buf;
+static int load_ssh_key(const cfg_t *cfg, char **out, FILE *opwfile,
+                        size_t opwfile_size) {
+  size_t buf_size;
+  char *buf = NULL;
+  char *cp = NULL;
+  int r = 0;
   int ch;
 
-  if (opwfile_size > buf_size ||
-      opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (length)");
-    }
-    return 0;
+  *out = NULL;
+
+  if (opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) {
+    debug_dbg(cfg, "Malformed SSH key (length)");
+    goto fail;
+  }
+
+  buf_size = opwfile_size > SSH_MAX_SIZE ? SSH_MAX_SIZE : opwfile_size;
+  if ((cp = buf = calloc(1, buf_size)) == NULL) {
+    debug_dbg(cfg, "Failed to allocate buffer for SSH key");
+    goto fail;
   }
 
   // NOTE(adma): +1 for \0
   if (fgets(buf, (int)(SSH_HEADER_LEN + 1), opwfile) == NULL ||
       strlen(buf) != SSH_HEADER_LEN ||
       strncmp(buf, SSH_HEADER, SSH_HEADER_LEN) != 0) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (header)");
-    }
-    return 0;
+    debug_dbg(cfg, "Malformed SSH key (header)");
+    goto fail;
   }
 
   while (opwfile_size > 0 && buf_size > 1) {
     ch = fgetc(opwfile);
     if (ch == EOF) {
-      if (cfg->debug) {
-        D(cfg->debug_file, "Unexpected authfile termination");
-      }
-      return 0;
+      debug_dbg(cfg, "Unexpected authfile termination");
+      goto fail;
     }
 
     opwfile_size--;
-    buf_size--;
 
     if (ch != '\n' && ch != '\r') {
       *cp = (char) ch;
+      buf_size--;
       if (ch == '-') {
         // NOTE(adma): no +1 here since we already read one '-'
         if (buf_size < SSH_TRAILER_LEN ||
             fgets(cp + 1, (int)SSH_TRAILER_LEN, opwfile) == NULL ||
             strlen(cp) != SSH_TRAILER_LEN ||
             strncmp(cp, SSH_TRAILER, SSH_TRAILER_LEN) != 0) {
-          if (cfg->debug) {
-            D(cfg->debug_file, "Malformed SSH key (trailer)");
-          }
-          return 0;
+          debug_dbg(cfg, "Malformed SSH key (trailer)");
+          goto fail;
         }
 
+        r = 1;
         *(cp) = '\0';
         break;
       } else {
@@ -394,11 +335,15 @@ static int load_ssh_key(const cfg_t *cfg
     }
   }
 
-  if (cfg->debug) { // TODO(adma): too verbose? Delete?
-    D(cfg->debug_file, "Credential is \"%s\"", buf);
+fail:
+  if (r != 1) {
+    free(buf);
+    buf = NULL;
   }
 
-  return 1;
+  *out = buf;
+
+  return r;
 }
 
 static int ssh_get(const unsigned char **buf, size_t *size, unsigned char *dst,
@@ -464,12 +409,10 @@ static int ssh_log_cstring(const cfg_t *
   (void) name; // silence compiler warnings if PAM_DEBUG disabled
 
   if (!ssh_get_cstring(buf, size, &str, &len)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Malformed SSH key (%s)", name);
+    debug_dbg(cfg, "Malformed SSH key (%s)", name);
     return 0;
   }
-  if (cfg->debug)
-    D(cfg->debug_file, "%s (%zu) \"%s\"", name, len, str);
+  debug_dbg(cfg, "%s (%zu) \"%s\"", name, len, str);
 
   free(str);
   return 1;
@@ -483,28 +426,21 @@ static int ssh_get_attrs(const cfg_t *cf
 
   // flags
   if (!ssh_get_u8(buf, size, &flags)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (flags)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (flags)");
     return 0;
   }
-  if (cfg->debug) {
-    D(cfg->debug_file, "flags: %02x", flags);
-  }
+  debug_dbg(cfg, "flags: %02x", flags);
 
   r = snprintf(tmp, sizeof(tmp), "%s%s",
                flags & SSH_SK_USER_PRESENCE_REQD ? "+presence" : "",
                flags & SSH_SK_USER_VERIFICATION_REQD ? "+verification" : "");
   if (r < 0 || (size_t) r >= sizeof(tmp)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to prepare flags");
+    debug_dbg(cfg, "Unable to prepare flags");
     return 0;
   }
 
   if ((*attrs = strdup(tmp)) == NULL) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Unable to allocate attributes");
-    }
+    debug_dbg(cfg, "Unable to allocate attributes");
     return 0;
   }
 
@@ -526,9 +462,7 @@ static int ssh_get_pubkey(const cfg_t *c
 
   // key type
   if (!ssh_get_cstring(buf, size, &ssh_type, &len)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (keytype)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (keytype)");
     goto err;
   }
 
@@ -540,60 +474,44 @@ static int ssh_get_pubkey(const cfg_t *c
     type = COSE_EDDSA;
     point_len = SSH_EDDSA_POINT_LEN;
   } else {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Unknown key type %s", ssh_type);
-    }
+    debug_dbg(cfg, "Unknown key type %s", ssh_type);
     goto err;
   }
 
-  if (cfg->debug) {
-    D(cfg->debug_file, "keytype (%zu) \"%s\"", len, ssh_type);
-  }
+  debug_dbg(cfg, "keytype (%zu) \"%s\"", len, ssh_type);
 
   if (type == COSE_ES256) {
     // curve name
     if (!ssh_get_cstring(buf, size, &ssh_curve, &len)) {
-      if (cfg->debug) {
-        D(cfg->debug_file, "Malformed SSH key (curvename)");
-      }
+      debug_dbg(cfg, "Malformed SSH key (curvename)");
       goto err;
     }
 
     if (len == SSH_P256_NAME_LEN &&
         memcmp(ssh_curve, SSH_P256_NAME, SSH_P256_NAME_LEN) == 0) {
-      if (cfg->debug) {
-        D(cfg->debug_file, "curvename (%zu) \"%s\"", len, ssh_curve);
-      }
+      debug_dbg(cfg, "curvename (%zu) \"%s\"", len, ssh_curve);
     } else {
-      if (cfg->debug) {
-        D(cfg->debug_file, "Unknown curve %s", ssh_curve);
-      }
+      debug_dbg(cfg, "Unknown curve %s", ssh_curve);
       goto err;
     }
   }
 
   // point
   if (!ssh_get_string_ref(buf, size, &blob, &len)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (point)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (point)");
     goto err;
   }
 
   if (len != point_len) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Invalid point length, should be %zu, found %zu",
-        point_len, len);
-    }
+    debug_dbg(cfg, "Invalid point length, should be %zu, found %zu", point_len,
+              len);
     goto err;
   }
 
   if (type == COSE_ES256) {
     // Skip the initial '04'
     if (len < 1) {
-      if (cfg->debug) {
-        D(cfg->debug_file, "Failed to skip initial '04'");
-      }
+      debug_dbg(cfg, "Failed to skip initial '04'");
       goto err;
     }
     blob++;
@@ -601,16 +519,12 @@ static int ssh_get_pubkey(const cfg_t *c
   }
 
   if (!b64_encode(blob, len, pubkey_p)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Unable to allocate public key");
-    }
+    debug_dbg(cfg, "Unable to allocate public key");
     goto err;
   }
 
   if ((*type_p = strdup(cose_string(type))) == NULL) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Unable to allocate COSE type");
-    }
+    debug_dbg(cfg, "Unable to allocate COSE type");
     goto err;
   }
 
@@ -628,26 +542,26 @@ err:
   return ok;
 }
 
-static int parse_ssh_format(const cfg_t *cfg, char *buf, size_t buf_size,
-                            FILE *opwfile, size_t opwfile_size,
-                            device_t *devices, unsigned *n_devs) {
-
+static int parse_ssh_format(const cfg_t *cfg, FILE *opwfile,
+                            size_t opwfile_size, device_t *devices,
+                            unsigned *n_devs) {
+  char *b64 = NULL;
   const unsigned char *decoded;
   unsigned char *decoded_initial = NULL;
   size_t decoded_len;
   const unsigned char *blob;
   uint32_t check1, check2, tmp;
   size_t len;
+  int r = 0;
 
   // The logic below is inspired by
   // how ssh parses its own keys. See sshkey.c
   reset_device(&devices[0]);
+  *n_devs = 0;
 
-  if (!load_ssh_key(cfg, buf, buf_size, opwfile, opwfile_size) ||
-      !b64_decode(buf, (void **) &decoded_initial, &decoded_len)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Unable to decode credential");
-    }
+  if (!load_ssh_key(cfg, &b64, opwfile, opwfile_size) ||
+      !b64_decode(b64, (void **) &decoded_initial, &decoded_len)) {
+    debug_dbg(cfg, "Unable to decode credential");
     goto out;
   }
 
@@ -656,9 +570,7 @@ static int parse_ssh_format(const cfg_t 
   // magic
   if (decoded_len < SSH_AUTH_MAGIC_LEN ||
       memcmp(decoded, SSH_AUTH_MAGIC, SSH_AUTH_MAGIC_LEN) != 0) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (magic)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (magic)");
     goto out;
   }
 
@@ -671,54 +583,40 @@ static int parse_ssh_format(const cfg_t 
     goto out;
 
   if (!ssh_get_u32(&decoded, &decoded_len, &tmp)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (nkeys)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (nkeys)");
     goto out;
   }
-  if (cfg->debug) {
-    D(cfg->debug_file, "nkeys: %" PRIu32, tmp);
-  }
+  debug_dbg(cfg, "nkeys: %" PRIu32, tmp);
   if (tmp != 1) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Multiple keys not supported");
-    }
+    debug_dbg(cfg, "Multiple keys not supported");
     goto out;
   }
 
   // public_key (skip)
   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (pubkey)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (pubkey)");
     goto out;
   }
 
   // private key (consume length)
   if (!ssh_get_u32(&decoded, &decoded_len, &tmp) || decoded_len < tmp) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (pvtkey length)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (pvtkey length)");
     goto out;
   }
 
   // check1, check2
   if (!ssh_get_u32(&decoded, &decoded_len, &check1) ||
       !ssh_get_u32(&decoded, &decoded_len, &check2)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (check1, check2)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (check1, check2)");
     goto out;
   }
-  if (cfg->debug) {
-    D(cfg->debug_file, "check1: %" PRIu32, check1);
-    D(cfg->debug_file, "check2: %" PRIu32, check2);
-  }
+
+  debug_dbg(cfg, "check1: %" PRIu32, check1);
+  debug_dbg(cfg, "check2: %" PRIu32, check2);
+
   if (check1 != check2) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Mismatched check values");
-      goto out;
-    }
+    debug_dbg(cfg, "Mismatched check values");
+    goto out;
   }
 
   if (!ssh_get_pubkey(cfg, &decoded, &decoded_len, &devices[0].coseType,
@@ -730,28 +628,18 @@ static int parse_ssh_format(const cfg_t 
   // keyhandle
   if (!ssh_get_string_ref(&decoded, &decoded_len, &blob, &len) ||
       !b64_encode(blob, len, &devices[0].keyHandle)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (keyhandle)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (keyhandle)");
     goto out;
   }
 
-  if (cfg->debug) {
-    D(cfg->debug_file, "KeyHandle for device number 1: %s",
-      devices[0].keyHandle);
-    D(cfg->debug_file, "publicKey for device number 1: %s",
-      devices[0].publicKey);
-    D(cfg->debug_file, "COSE type for device number 1: %s",
-      devices[0].coseType);
-    D(cfg->debug_file, "Attributes for device number 1: %s",
-      devices[0].attributes);
-  }
+  debug_dbg(cfg, "KeyHandle for device number 1: %s", devices[0].keyHandle);
+  debug_dbg(cfg, "publicKey for device number 1: %s", devices[0].publicKey);
+  debug_dbg(cfg, "COSE type for device number 1: %s", devices[0].coseType);
+  debug_dbg(cfg, "Attributes for device number 1: %s", devices[0].attributes);
 
   // reserved (skip)
   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (reserved)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (reserved)");
     goto out;
   }
 
@@ -761,44 +649,36 @@ static int parse_ssh_format(const cfg_t 
 
   // padding
   if (decoded_len >= 255) {
-    if (cfg->debug) {
-      D(cfg->debug_file, "Malformed SSH key (padding length)");
-    }
+    debug_dbg(cfg, "Malformed SSH key (padding length)");
     goto out;
   }
 
   for (int i = 1; (unsigned) i <= decoded_len; i++) {
     if (decoded[i - 1] != i) {
-      if (cfg->debug) {
-        D(cfg->debug_file, "Malformed SSH key (padding)");
-      }
+      debug_dbg(cfg, "Malformed SSH key (padding)");
       goto out;
     }
   }
 
-  free(decoded_initial);
-  decoded_initial = NULL;
-
   *n_devs = 1;
-
-  return 1;
+  r = 1;
 
 out:
-  reset_device(&devices[0]);
-
-  if (decoded_initial) {
-    free(decoded_initial);
-    decoded_initial = NULL;
+  if (r != 1) {
+    reset_device(&devices[0]);
+    *n_devs = 0;
   }
 
-  return -1;
+  free(decoded_initial);
+  free(b64);
+
+  return r;
 }
 
 int get_devices_from_authfile(const cfg_t *cfg, const char *username,
                               device_t *devices, unsigned *n_devs) {
 
-  char *buf = NULL;
-  int retval = 0;
+  int r = PAM_AUTHINFO_UNAVAIL;
   int fd = -1;
   struct stat st;
   struct passwd *pw = NULL, pw_s;
@@ -813,95 +693,77 @@ int get_devices_from_authfile(const cfg_
 
   fd = open(cfg->auth_file, O_RDONLY | O_CLOEXEC | O_NOCTTY);
   if (fd < 0) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Cannot open file: %s (%s)", cfg->auth_file,
-        strerror(errno));
+    if (errno == ENOENT && cfg->nouserok) {
+      r = PAM_IGNORE;
+    }
+    debug_dbg(cfg, "Cannot open authentication file: %s", strerror(errno));
     goto err;
   }
 
   if (fstat(fd, &st) < 0) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Cannot stat file: %s (%s)", cfg->auth_file,
-        strerror(errno));
+    debug_dbg(cfg, "Cannot stat authentication file: %s", strerror(errno));
     goto err;
   }
 
   if (!S_ISREG(st.st_mode)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "%s is not a regular file", cfg->auth_file);
+    debug_dbg(cfg, "Authentication file is not a regular file");
     goto err;
   }
 
-  if (st.st_size == 0) {
-    if (cfg->debug)
-      D(cfg->debug_file, "File %s is empty", cfg->auth_file);
+  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+    debug_dbg(cfg, "Authentication file has insecure permissions");
     goto err;
   }
+
   opwfile_size = (size_t)st.st_size;
 
   gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
   if (gpu_ret != 0 || pw == NULL) {
-    D(cfg->debug_file, "Unable to retrieve credentials for uid %u, (%s)",
-      st.st_uid, strerror(errno));
+    debug_dbg(cfg, "Unable to retrieve credentials for uid %u, (%s)", st.st_uid,
+              strerror(errno));
     goto err;
   }
 
   if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
     if (strcmp(username, "root") != 0) {
-      D(cfg->debug_file,
-        "The owner of the authentication file is neither %s nor root",
-        username);
+      debug_dbg(cfg,
+                "The owner of the authentication file is neither %s nor root",
+                username);
     } else {
-      D(cfg->debug_file, "The owner of the authentication file is not root");
+      debug_dbg(cfg, "The owner of the authentication file is not root");
     }
     goto err;
   }
 
   opwfile = fdopen(fd, "r");
   if (opwfile == NULL) {
-    if (cfg->debug)
-      D(cfg->debug_file, "fdopen: %s", strerror(errno));
+    debug_dbg(cfg, "fdopen: %s", strerror(errno));
     goto err;
   } else {
     fd = -1; /* fd belongs to opwfile */
   }
 
-  buf = calloc(1, (DEVSIZE * cfg->max_devs));
-  if (!buf) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate memory");
-    goto err;
-  }
-
   if (cfg->sshformat == 0) {
-    retval = parse_native_format(cfg, username, buf, opwfile, devices, n_devs);
+    if (parse_native_format(cfg, username, opwfile, devices, n_devs) != 1) {
+      goto err;
+    }
   } else {
-    retval = parse_ssh_format(cfg, buf, DEVSIZE * cfg->max_devs, opwfile,
-                              opwfile_size, devices, n_devs);
-  }
-
-  if (retval != 1) {
-    // NOTE(adma): error message is logged by the previous function
-    goto err;
+    if (parse_ssh_format(cfg, opwfile, opwfile_size, devices, n_devs) != 1) {
+      goto err;
+    }
   }
 
-  if (cfg->debug)
-    D(cfg->debug_file, "Found %d device(s) for user %s", *n_devs, username);
-
-  retval = 1;
-  goto out;
+  debug_dbg(cfg, "Found %d device(s) for user %s", *n_devs, username);
+  r = PAM_SUCCESS;
 
 err:
-  for (i = 0; i < *n_devs; i++) {
-    reset_device(&devices[i]);
-  }
-
-  *n_devs = 0;
-
-out:
-  if (buf) {
-    free(buf);
-    buf = NULL;
+  if (r != PAM_SUCCESS) {
+    for (i = 0; i < *n_devs; i++) {
+      reset_device(&devices[i]);
+    }
+    *n_devs = 0;
+  } else if (*n_devs == 0) {
+    r = cfg->nouserok ? PAM_IGNORE : PAM_USER_UNKNOWN;
   }
 
   if (opwfile)
@@ -910,7 +772,7 @@ out:
   if (fd != -1)
     close(fd);
 
-  return retval;
+  return r;
 }
 
 void free_devices(device_t *devices, const unsigned n_devs) {
@@ -936,35 +798,29 @@ static int get_authenticators(const cfg_
   size_t i;
   size_t j;
 
-  if (cfg->debug)
-    D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len);
+  debug_dbg(cfg, "Working with %zu authenticator(s)", devlist_len);
 
   for (i = 0, j = 0; i < devlist_len; i++) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i);
+    debug_dbg(cfg, "Checking whether key exists in authenticator %zu", i);
 
     di = fido_dev_info_ptr(devlist, i);
     if (!di) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to get device pointer");
+      debug_dbg(cfg, "Unable to get device pointer");
       continue;
     }
 
-    if (cfg->debug)
-      D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di));
+    debug_dbg(cfg, "Authenticator path: %s", fido_dev_info_path(di));
 
     dev = fido_dev_new();
     if (!dev) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to allocate device type");
+      debug_dbg(cfg, "Unable to allocate device type");
       continue;
     }
 
     r = fido_dev_open(dev, fido_dev_info_path(di));
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to open authenticator: %s (%d)",
-          fido_strerr(r), r);
+      debug_dbg(cfg, "Failed to open authenticator: %s (%d)", fido_strerr(r),
+                r);
       fido_dev_free(&dev);
       continue;
     }
@@ -977,12 +833,10 @@ static int get_authenticators(const cfg_
       if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
           (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
         authlist[j++] = dev;
-        if (cfg->debug)
-          D(cfg->debug_file, "Found key in authenticator %zu", i);
+        debug_dbg(cfg, "Found key in authenticator %zu", i);
         return (1);
       }
-      if (cfg->debug)
-        D(cfg->debug_file, "Key not found in authenticator %zu", i);
+      debug_dbg(cfg, "Key not found in authenticator %zu", i);
 
       fido_dev_close(dev);
       fido_dev_free(&dev);
@@ -992,8 +846,7 @@ static int get_authenticators(const cfg_
   if (j != 0)
     return (1);
   else {
-    if (cfg->debug)
-      D(cfg->debug_file, "Key not found");
+    debug_dbg(cfg, "Key not found");
     return (0);
   }
 }
@@ -1084,13 +937,11 @@ static int match_device_opts(fido_dev_t 
 static int set_opts(const cfg_t *cfg, const struct opts *opts,
                     fido_assert_t *assert) {
   if (fido_assert_set_up(assert, opts->up) != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set UP");
+    debug_dbg(cfg, "Failed to set UP");
     return 0;
   }
   if (fido_assert_set_uv(assert, opts->uv) != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set UV");
+    debug_dbg(cfg, "Failed to set UV");
     return 0;
   }
 
@@ -1102,15 +953,13 @@ static int set_cdh(const cfg_t *cfg, fid
   int r;
 
   if (!random_bytes(cdh, sizeof(cdh))) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to generate challenge");
+    debug_dbg(cfg, "Failed to generate challenge");
     return 0;
   }
 
   r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to set challenge: %s (%d)", fido_strerr(r), r);
+    debug_dbg(cfg, "Unable to set challenge: %s (%d)", fido_strerr(r), r);
     return 0;
   }
 
@@ -1126,52 +975,43 @@ static fido_assert_t *prepare_assert(con
   int r;
 
   if ((assert = fido_assert_new()) == NULL) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate assertion");
+    debug_dbg(cfg, "Unable to allocate assertion");
     goto err;
   }
 
-  if (device->old_format && strcmp(cfg->origin, cfg->appid) != 0)
+  if (device->old_format)
     r = fido_assert_set_rp(assert, cfg->appid);
   else
     r = fido_assert_set_rp(assert, cfg->origin);
 
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
+    debug_dbg(cfg, "Unable to set origin: %s (%d)", fido_strerr(r), r);
     goto err;
   }
 
   if (is_resident(device->keyHandle)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Credential is resident");
+    debug_dbg(cfg, "Credential is resident");
   } else {
-    if (cfg->debug)
-      D(cfg->debug_file, "Key handle: %s", device->keyHandle);
+    debug_dbg(cfg, "Key handle: %s", device->keyHandle);
     if (!b64_decode(device->keyHandle, (void **) &buf, &buf_len)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to decode key handle");
+      debug_dbg(cfg, "Failed to decode key handle");
       goto err;
     }
 
     r = fido_assert_allow_cred(assert, buf, buf_len);
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
-          r);
+      debug_dbg(cfg, "Unable to set keyHandle: %s (%d)", fido_strerr(r), r);
       goto err;
     }
   }
 
   if (!set_opts(cfg, opts, assert)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set assert options");
+    debug_dbg(cfg, "Failed to set assert options");
     goto err;
   }
 
   if (!set_cdh(cfg, assert)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set client data hash");
+    debug_dbg(cfg, "Failed to set client data hash");
     goto err;
   }
 
@@ -1236,21 +1076,18 @@ static int parse_pk(const cfg_t *cfg, in
 
   if (old) {
     if (!hex_decode(pk, &buf, &buf_len)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to decode public key");
+      debug_dbg(cfg, "Failed to decode public key");
       goto err;
     }
   } else {
     if (!b64_decode(pk, (void **) &buf, &buf_len)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to decode public key");
+      debug_dbg(cfg, "Failed to decode public key");
       goto err;
     }
   }
 
   if (!cose_type(type, &out->type)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unknown COSE type '%s'", type);
+    debug_dbg(cfg, "Unknown COSE type '%s'", type);
     goto err;
   }
 
@@ -1258,8 +1095,7 @@ static int parse_pk(const cfg_t *cfg, in
   // returned as an error.  Instead, it is handled by fido_verify_assert().
   if (out->type == COSE_ES256) {
     if ((out->ptr = es256_pk_new()) == NULL) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to allocate ES256 public key");
+      debug_dbg(cfg, "Failed to allocate ES256 public key");
       goto err;
     }
     if (old) {
@@ -1268,34 +1104,28 @@ static int parse_pk(const cfg_t *cfg, in
       r = es256_pk_from_ptr(out->ptr, buf, buf_len);
     }
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to convert ES256 public key");
+      debug_dbg(cfg, "Failed to convert ES256 public key");
     }
   } else if (out->type == COSE_RS256) {
     if ((out->ptr = rs256_pk_new()) == NULL) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to allocate RS256 public key");
+      debug_dbg(cfg, "Failed to allocate RS256 public key");
       goto err;
     }
     r = rs256_pk_from_ptr(out->ptr, buf, buf_len);
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to convert RS256 public key");
+      debug_dbg(cfg, "Failed to convert RS256 public key");
     }
   } else if (out->type == COSE_EDDSA) {
     if ((out->ptr = eddsa_pk_new()) == NULL) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to allocate EDDSA public key");
+      debug_dbg(cfg, "Failed to allocate EDDSA public key");
       goto err;
     }
     r = eddsa_pk_from_ptr(out->ptr, buf, buf_len);
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to convert EDDSA public key");
+      debug_dbg(cfg, "Failed to convert EDDSA public key");
     }
   } else {
-    if (cfg->debug)
-      D(cfg->debug_file, "COSE type '%s' not handled", type);
+    debug_dbg(cfg, "COSE type '%s' not handled", type);
     goto err;
   }
 
@@ -1313,7 +1143,7 @@ int do_authentication(const cfg_t *cfg, 
   fido_dev_t **authlist = NULL;
   int cued = 0;
   int r;
-  int retval = -2;
+  int retval = PAM_AUTH_ERR;
   size_t ndevs = 0;
   size_t ndevs_prev = 0;
   unsigned i = 0;
@@ -1329,57 +1159,46 @@ int do_authentication(const cfg_t *cfg, 
 #endif
   memset(&pk, 0, sizeof(pk));
 
-  devlist = fido_dev_info_new(64);
+  devlist = fido_dev_info_new(DEVLIST_LEN);
   if (!devlist) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate devlist");
+    debug_dbg(cfg, "Unable to allocate devlist");
     goto out;
   }
 
-  r = fido_dev_info_manifest(devlist, 64, &ndevs);
+  r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
-        fido_strerr(r), r);
+    debug_dbg(cfg, "Unable to discover device(s), %s (%d)", fido_strerr(r), r);
     goto out;
   }
 
   ndevs_prev = ndevs;
 
-  if (cfg->debug)
-    D(cfg->debug_file, "Device max index is %zu", ndevs);
+  debug_dbg(cfg, "Device max index is %zu", ndevs);
 
-  authlist = calloc(64 + 1, sizeof(fido_dev_t *));
+  authlist = calloc(DEVLIST_LEN + 1, sizeof(fido_dev_t *));
   if (!authlist) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Unable to allocate authenticator list");
+    debug_dbg(cfg, "Unable to allocate authenticator list");
     goto out;
   }
 
-  if (cfg->nodetect && cfg->debug)
-    D(cfg->debug_file,
-      "nodetect option specified, suitable key detection will be skipped");
+  if (cfg->nodetect)
+    debug_dbg(cfg, "nodetect option specified, suitable key detection will be "
+                   "skipped");
 
   i = 0;
   while (i < n_devs) {
-    retval = -2;
-
-    if (cfg->debug)
-      D(cfg->debug_file, "Attempting authentication with device number %d",
-        i + 1);
+    debug_dbg(cfg, "Attempting authentication with device number %d", i + 1);
 
     init_opts(&opts); /* used during authenticator discovery */
     assert = prepare_assert(cfg, &devices[i], &opts);
     if (assert == NULL) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to prepare assert");
+      debug_dbg(cfg, "Failed to prepare assert");
       goto out;
     }
 
     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
                   devices[i].publicKey, &pk)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to parse public key");
+      debug_dbg(cfg, "Failed to parse public key");
       goto out;
     }
 
@@ -1391,30 +1210,26 @@ int do_authentication(const cfg_t *cfg, 
 
         r = match_device_opts(authlist[j], &opts);
         if (r != 1) {
-          if (cfg->debug) {
-            D(cfg->debug_file, "%s, skipping authenticator",
-              r < 0 ? "Failed to query supported options"
-                    : "Unsupported options");
-          }
+          debug_dbg(cfg, "%s, skipping authenticator",
+                    r < 0 ? "Failed to query supported options"
+                          : "Unsupported options");
           continue;
         }
 
         if (!set_opts(cfg, &opts, assert)) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to set assert options");
+          debug_dbg(cfg, "Failed to set assert options");
           goto out;
         }
 
         if (!set_cdh(cfg, assert)) {
-          if (cfg->debug)
-            D(cfg->debug_file, "Failed to reset client data hash");
+          debug_dbg(cfg, "Failed to reset client data hash");
           goto out;
         }
 
         if (opts.pin == FIDO_OPT_TRUE) {
           pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: ");
           if (pin == NULL) {
-            D(cfg->debug_file, "converse() returned NULL");
+            debug_dbg(cfg, "converse() returned NULL");
             goto out;
           }
         }
@@ -1435,46 +1250,42 @@ int do_authentication(const cfg_t *cfg, 
           if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
             r = fido_assert_set_uv(assert, FIDO_OPT_TRUE);
             if (r != FIDO_OK) {
-              D(cfg->debug_file, "Failed to set UV");
+              debug_dbg(cfg, "Failed to set UV");
               goto out;
             }
           }
           r = fido_assert_verify(assert, 0, pk.type, pk.ptr);
           if (r == FIDO_OK) {
-            retval = 1;
+            retval = PAM_SUCCESS;
             goto out;
           }
         }
       }
     } else {
-      if (cfg->debug)
-        D(cfg->debug_file, "Device for this keyhandle is not present");
+      debug_dbg(cfg, "Device for this keyhandle is not present");
     }
 
     i++;
 
     fido_dev_info_free(&devlist, ndevs);
 
-    devlist = fido_dev_info_new(64);
+    devlist = fido_dev_info_new(DEVLIST_LEN);
     if (!devlist) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to allocate devlist");
+      debug_dbg(cfg, "Unable to allocate devlist");
       goto out;
     }
 
-    r = fido_dev_info_manifest(devlist, 64, &ndevs);
+    r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
     if (r != FIDO_OK) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
-          fido_strerr(r), r);
+      debug_dbg(cfg, "Unable to discover device(s), %s (%d)", fido_strerr(r),
+                r);
       goto out;
     }
 
     if (ndevs > ndevs_prev) {
-      if (cfg->debug)
-        D(cfg->debug_file,
-          "Devices max_index has changed: %zu (was %zu). Starting over", ndevs,
-          ndevs_prev);
+      debug_dbg(cfg,
+                "Devices max_index has changed: %zu (was %zu). Starting over",
+                ndevs, ndevs_prev);
       ndevs_prev = ndevs;
       i = 0;
     }
@@ -1524,35 +1335,30 @@ static int manual_get_assert(const cfg_t
   b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
 
   if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to decode authenticator data");
+    debug_dbg(cfg, "Failed to decode authenticator data");
     goto err;
   }
 
   if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to decode signature");
+    debug_dbg(cfg, "Failed to decode signature");
     goto err;
   }
 
   r = fido_assert_set_count(assert, 1);
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set signature count of assertion");
+    debug_dbg(cfg, "Failed to set signature count of assertion");
     goto err;
   }
 
   r = fido_assert_set_authdata(assert, 0, authdata, authdata_len);
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set authdata of assertion");
+    debug_dbg(cfg, "Failed to set authdata of assertion");
     goto err;
   }
 
   r = fido_assert_set_sig(assert, 0, sig, sig_len);
   if (r != FIDO_OK) {
-    if (cfg->debug)
-      D(cfg->debug_file, "Failed to set signature of assertion");
+    debug_dbg(cfg, "Failed to set signature of assertion");
     goto err;
   }
 
@@ -1575,7 +1381,7 @@ int do_manual_authentication(const cfg_t
   char *b64_challenge = NULL;
   char prompt[MAX_PROMPT_LEN];
   char buf[MAX_PROMPT_LEN];
-  int retval = -2;
+  int retval = PAM_AUTH_ERR;
   int n;
   int r;
   unsigned i = 0;
@@ -1600,37 +1406,30 @@ int do_manual_authentication(const cfg_t
     parse_opts(cfg, devices[i].attributes, &opts);
     assert[i] = prepare_assert(cfg, &devices[i], &opts);
     if (assert[i] == NULL) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to prepare assert");
+      debug_dbg(cfg, "Failed to prepare assert");
       goto out;
     }
 
-    if (cfg->debug)
-      D(cfg->debug_file, "Attempting authentication with device number %d",
-        i + 1);
+    debug_dbg(cfg, "Attempting authentication with device number %d", i + 1);
 
     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
                   devices[i].publicKey, &pk[i])) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Unable to parse public key %u", i);
+      debug_dbg(cfg, "Unable to parse public key %u", i);
       goto out;
     }
 
     if (!b64_encode(fido_assert_clientdata_hash_ptr(assert[i]),
                     fido_assert_clientdata_hash_len(assert[i]),
                     &b64_challenge)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to encode challenge");
+      debug_dbg(cfg, "Failed to encode challenge");
       goto out;
     }
 
-    if (cfg->debug)
-      D(cfg->debug_file, "Challenge: %s", b64_challenge);
+    debug_dbg(cfg, "Challenge: %s", b64_challenge);
 
     n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1);
     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to print challenge prompt");
+      debug_dbg(cfg, "Failed to print challenge prompt");
       goto out;
     }
 
@@ -1639,8 +1438,7 @@ int do_manual_authentication(const cfg_t
     n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin,
                  devices[i].keyHandle);
     if (n <= 0 || (size_t) n >= sizeof(buf)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to print fido2-assert input string");
+      debug_dbg(cfg, "Failed to print fido2-assert input string");
       goto out;
     }
 
@@ -1654,25 +1452,21 @@ int do_manual_authentication(const cfg_t
            "Please pass the challenge(s) above to fido2-assert, and "
            "paste the results in the prompt below.");
 
-  retval = -1;
-
   for (i = 0; i < n_devs; ++i) {
     n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1);
     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to print response prompt");
+      debug_dbg(cfg, "Failed to print response prompt");
       goto out;
     }
 
     if (!manual_get_assert(cfg, prompt, pamh, assert[i])) {
-      if (cfg->debug)
-        D(cfg->debug_file, "Failed to get assert %u", i);
+      debug_dbg(cfg, "Failed to get assert %u", i);
       goto out;
     }
 
     r = fido_assert_verify(assert[i], 0, pk[i].type, pk[i].ptr);
     if (r == FIDO_OK) {
-      retval = 1;
+      retval = PAM_SUCCESS;
       break;
     }
   }
@@ -1733,34 +1527,6 @@ char *converse(pam_handle_t *pamh, int e
   return ret;
 }
 
-#if defined(PAM_DEBUG)
-void _debug(FILE *debug_file, const char *file, int line, const char *func,
-            const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-
-#if defined(WITH_FUZZING)
-  (void) debug_file;
-  snprintf(NULL, 0, DEBUG_STR, file, line, func);
-  vsnprintf(NULL, 0, fmt, ap);
-#elif defined(LOG_DEBUG)
-  if (debug_file == (FILE *) -1) {
-    syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func);
-    vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap);
-  } else {
-    fprintf(debug_file, DEBUG_STR, file, line, func);
-    vfprintf(debug_file, fmt, ap);
-    fprintf(debug_file, "\n");
-  }
-#else  /* Windows, MAC */
-  fprintf(debug_file, DEBUG_STR, file, line, func);
-  vfprintf(debug_file, fmt, ap);
-  fprintf(debug_file, "\n");
-#endif /* __linux__ */
-  va_end(ap);
-}
-#endif /* PAM_DEBUG */
-
 #ifndef RANDOM_DEV
 #define RANDOM_DEV "/dev/urandom"
 #endif

Index: src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c
diff -u src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c:1.2 src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c:1.3
--- src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c:1.2	Wed Oct  6 05:06:21 2021
+++ src/external/bsd/pam-u2f/dist/pamu2fcfg/pamu2fcfg.c	Wed Jan 15 10:30:48 2025
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2021 Yubico AB - See COPYING
+ * Copyright (C) 2014-2022 Yubico AB - See COPYING
  */
 
 #define BUFSIZE 1024
@@ -7,6 +7,13 @@
 #define TIMEOUT 15
 #define FREQUENCY 1
 
+#define PIN_SET 0x01
+#define PIN_UNSET 0x02
+#define UV_SET 0x04
+#define UV_UNSET 0x08
+#define UV_REQD 0x10
+#define UV_NOT_REQD 0x20
+
 #include <fido.h>
 
 #include <stdio.h>
@@ -16,18 +23,35 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <pwd.h>
+#include <err.h>
 
 #include "b64.h"
-#include "cmdline.h"
 #include "util.h"
 
 #include "openbsd-compat.h"
 
-static fido_cred_t *prepare_cred(const struct gengetopt_args_info *const args) {
+#ifndef FIDO_ERR_UV_BLOCKED /* XXX: compat libfido2 <1.5.0 */
+#define FIDO_ERR_UV_BLOCKED 0x3c
+#endif
+
+struct args {
+  const char *appid;
+  const char *origin;
+  const char *type;
+  const char *username;
+  int resident;
+  int no_user_presence;
+  int pin_verification;
+  int user_verification;
+  int debug;
+  int verbose;
+  int nouser;
+};
+
+static fido_cred_t *prepare_cred(const struct args *const args) {
   fido_cred_t *cred = NULL;
-  fido_opt_t resident_key;
-  char *appid = NULL;
-  char *user = NULL;
+  const char *appid = NULL;
+  const char *user = NULL;
   struct passwd *passwd;
   unsigned char userid[32];
   unsigned char cdh[32];
@@ -43,11 +67,9 @@ static fido_cred_t *prepare_cred(const s
   }
 
   type = COSE_ES256; /* default */
-  if (args->type_given) {
-    if (!cose_type(args->type_arg, &type)) {
-      fprintf(stderr, "Unknown COSE type '%s'.\n", args->type_arg);
-      goto err;
-    }
+  if (args->type && !cose_type(args->type, &type)) {
+    fprintf(stderr, "Unknown COSE type '%s'.\n", args->type);
+    goto err;
   }
 
   if ((r = fido_cred_set_type(cred, type)) != FIDO_OK) {
@@ -66,8 +88,8 @@ static fido_cred_t *prepare_cred(const s
     goto err;
   }
 
-  if (args->origin_given) {
-    if (strlcpy(origin, args->origin_arg, sizeof(origin)) >= sizeof(origin)) {
+  if (args->origin) {
+    if (strlcpy(origin, args->origin, sizeof(origin)) >= sizeof(origin)) {
       fprintf(stderr, "error: strlcpy failed\n");
       goto err;
     }
@@ -82,13 +104,13 @@ static fido_cred_t *prepare_cred(const s
     }
   }
 
-  if (args->appid_given) {
-    appid = args->appid_arg;
+  if (args->appid) {
+    appid = args->appid;
   } else {
     appid = origin;
   }
 
-  if (args->verbose_given) {
+  if (args->verbose) {
     fprintf(stderr, "Setting origin to %s\n", origin);
     fprintf(stderr, "Setting appid to %s\n", appid);
   }
@@ -98,8 +120,8 @@ static fido_cred_t *prepare_cred(const s
     goto err;
   }
 
-  if (args->username_given) {
-    user = args->username_arg;
+  if (args->username) {
+    user = args->username;
   } else {
     if ((passwd = getpwuid(getuid())) == NULL) {
       perror("getpwuid");
@@ -113,7 +135,7 @@ static fido_cred_t *prepare_cred(const s
     goto err;
   }
 
-  if (args->verbose_given) {
+  if (args->verbose) {
     fprintf(stderr, "Setting user to %s\n", user);
     fprintf(stderr, "Setting user id to ");
     for (size_t i = 0; i < sizeof(userid); i++)
@@ -127,13 +149,8 @@ static fido_cred_t *prepare_cred(const s
     goto err;
   }
 
-  if (args->resident_given) {
-    resident_key = FIDO_OPT_TRUE;
-  } else {
-    resident_key = FIDO_OPT_OMIT;
-  }
-
-  if ((r = fido_cred_set_rk(cred, resident_key)) != FIDO_OK) {
+  if ((r = fido_cred_set_rk(cred, args->resident ? FIDO_OPT_TRUE
+                                                 : FIDO_OPT_OMIT)) != FIDO_OK) {
     fprintf(stderr, "error: fido_cred_set_rk (%d) %s\n", r, fido_strerr(r));
     goto err;
   }
@@ -153,7 +170,8 @@ err:
   return cred;
 }
 
-static int make_cred(const char *path, fido_dev_t *dev, fido_cred_t *cred) {
+static int make_cred(const struct args *args, const char *path, fido_dev_t *dev,
+                     fido_cred_t *cred, int devopts) {
   char prompt[BUFSIZE];
   char pin[BUFSIZE];
   int n;
@@ -164,8 +182,25 @@ static int make_cred(const char *path, f
     return -1;
   }
 
-  r = fido_dev_make_cred(dev, cred, NULL);
-  if (r == FIDO_ERR_PIN_REQUIRED) {
+  /* Some form of UV required; built-in UV is available. */
+  if (args->user_verification || (devopts & (UV_SET | UV_NOT_REQD)) == UV_SET) {
+    if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) {
+      fprintf(stderr, "error: fido_cred_set_uv: %s (%d)\n", fido_strerr(r), r);
+      return -1;
+    }
+  }
+
+  /* Let built-in UV have precedence over PIN. No UV also handled here. */
+  if (args->user_verification || !args->pin_verification) {
+    r = fido_dev_make_cred(dev, cred, NULL);
+  } else {
+    r = FIDO_ERR_PIN_REQUIRED;
+  }
+
+  /* Some form of UV required; built-in UV failed or is not available. */
+  if ((devopts & PIN_SET) &&
+      (r == FIDO_ERR_PIN_REQUIRED || r == FIDO_ERR_UV_BLOCKED ||
+       r == FIDO_ERR_PIN_BLOCKED)) {
     n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path);
     if (n < 0 || (size_t) n >= sizeof(prompt)) {
       fprintf(stderr, "error: snprintf prompt");
@@ -212,7 +247,7 @@ static int verify_cred(const fido_cred_t
   return 0;
 }
 
-static int print_authfile_line(const struct gengetopt_args_info *const args,
+static int print_authfile_line(const struct args *const args,
                                const fido_cred_t *const cred) {
   const unsigned char *kh = NULL;
   const unsigned char *pk = NULL;
@@ -253,7 +288,7 @@ static int print_authfile_line(const str
     goto err;
   }
 
-  if (!args->nouser_given) {
+  if (!args->nouser) {
     if ((user = fido_cred_user_name(cred)) == NULL) {
       fprintf(stderr, "error: fido_cred_user_name returned NULL\n");
       goto err;
@@ -261,11 +296,11 @@ static int print_authfile_line(const str
     printf("%s", user);
   }
 
-  printf(":%s,%s,%s,%s%s%s\n", args->resident_given ? "*" : b64_kh, b64_pk,
+  printf(":%s,%s,%s,%s%s%s", args->resident ? "*" : b64_kh, b64_pk,
          cose_string(fido_cred_type(cred)),
-         !args->no_user_presence_given ? "+presence" : "",
-         args->user_verification_given ? "+verification" : "",
-         args->pin_verification_given ? "+pin" : "");
+         !args->no_user_presence ? "+presence" : "",
+         args->user_verification ? "+verification" : "",
+         args->pin_verification ? "+pin" : "");
 
   ok = 0;
 
@@ -276,40 +311,169 @@ err:
   return ok;
 }
 
+static int get_device_options(fido_dev_t *dev, int *devopts) {
+  char *const *opts;
+  const bool *vals;
+  fido_cbor_info_t *info;
+  int r;
+
+  *devopts = 0;
+
+  if (!fido_dev_is_fido2(dev))
+    return 0;
+
+  if ((info = fido_cbor_info_new()) == NULL) {
+    fprintf(stderr, "fido_cbor_info_new failed\n");
+    return -1;
+  }
+  if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) {
+    fprintf(stderr, "fido_dev_get_cbor_info: %s (%d)\n", fido_strerr(r), r);
+    fido_cbor_info_free(&info);
+    return -1;
+  }
+
+  opts = fido_cbor_info_options_name_ptr(info);
+  vals = fido_cbor_info_options_value_ptr(info);
+  for (size_t i = 0; i < fido_cbor_info_options_len(info); i++) {
+    if (strcmp(opts[i], "clientPin") == 0) {
+      *devopts |= vals[i] ? PIN_SET : PIN_UNSET;
+    } else if (strcmp(opts[i], "uv") == 0) {
+      *devopts |= vals[i] ? UV_SET : UV_UNSET;
+    } else if (strcmp(opts[i], "makeCredUvNotRqd") == 0) {
+      *devopts |= vals[i] ? UV_NOT_REQD : UV_REQD;
+    }
+  }
+
+  fido_cbor_info_free(&info);
+
+  return 0;
+}
+
+static void parse_args(int argc, char *argv[], struct args *args) {
+  int c;
+  enum {
+    OPT_VERSION = 0x100,
+  };
+  /* clang-format off */
+  static const struct option options[] = {
+    { "help",              no_argument,       NULL, 'h'         },
+    { "version",           no_argument,       NULL, OPT_VERSION },
+    { "origin",            required_argument, NULL, 'o'         },
+    { "appid",             required_argument, NULL, 'i'         },
+    { "type",              required_argument, NULL, 't'         },
+    { "resident",          no_argument,       NULL, 'r'         },
+    { "no-user-presence",  no_argument,       NULL, 'P'         },
+    { "pin-verification",  no_argument,       NULL, 'N'         },
+    { "user-verification", no_argument,       NULL, 'V'         },
+    { "debug",             no_argument,       NULL, 'd'         },
+    { "verbose",           no_argument,       NULL, 'v'         },
+    { "username",          required_argument, NULL, 'u'         },
+    { "nouser",            no_argument,       NULL, 'n'         },
+    { 0,                   0,                 0,    0           }
+  };
+  const char *usage =
+"Usage: pamu2fcfg [OPTION]...\n"
+"Perform a FIDO2/U2F registration operation and print a configuration line that\n"
+"can be used with the pam_u2f module.\n"
+"\n"
+"  -h, --help               Print help and exit\n"
+"      --version            Print version and exit\n"
+"  -o, --origin=STRING      Relying party ID to use during registration,\n"
+"                             defaults to pam://hostname\n"
+"  -i, --appid=STRING       Relying party name to use during registration,\n"
+"                             defaults to the value of origin\n"
+"  -t, --type=STRING        COSE type to use during registration (ES256, EDDSA,\n"
+"                             or RS256), defaults to ES256\n"
+"  -r, --resident           Generate a resident (discoverable) credential\n"
+"  -P, --no-user-presence   Allow the credential to be used without ensuring the\n"
+"                             user's presence\n"
+"  -N, --pin-verification   Require PIN verification during authentication\n"
+"  -V, --user-verification  Require user verification during authentication\n"
+"  -d, --debug              Print debug information\n"
+"  -v, --verbose            Print information about chosen origin and appid\n"
+"  -u, --username=STRING    The name of the user registering the device,\n"
+"                             defaults to the current user name\n"
+"  -n, --nouser             Print only registration information (key handle,\n"
+"                             public key, and options), useful for appending\n"
+"\n"
+"Report bugs at <" PACKAGE_BUGREPORT ">.\n";
+  /* clang-format on */
+
+  while ((c = getopt_long(argc, argv, "ho:i:t:rPNVdvu:n", options, NULL)) !=
+         -1) {
+    switch (c) {
+      case 'h':
+        printf("%s", usage);
+        exit(EXIT_SUCCESS);
+      case 'o':
+        args->origin = optarg;
+        break;
+      case 'i':
+        args->appid = optarg;
+        break;
+      case 't':
+        args->type = optarg;
+        break;
+      case 'u':
+        args->username = optarg;
+        break;
+      case 'r':
+        args->resident = 1;
+        break;
+      case 'P':
+        args->no_user_presence = 1;
+        break;
+      case 'N':
+        args->pin_verification = 1;
+        break;
+      case 'V':
+        args->user_verification = 1;
+        break;
+      case 'd':
+        args->debug = 1;
+        break;
+      case 'v':
+        args->verbose = 1;
+        break;
+      case 'n':
+        args->nouser = 1;
+        break;
+      case OPT_VERSION:
+        printf("pamu2fcfg " PACKAGE_VERSION "\n");
+        exit(EXIT_SUCCESS);
+      case '?':
+        exit(EXIT_FAILURE);
+      default:
+        errx(EXIT_FAILURE, "unknown option 0x%x", c);
+    }
+  }
+
+  if (optind != argc)
+    errx(EXIT_FAILURE, "unsupported positional argument(s)");
+}
+
 int main(int argc, char *argv[]) {
   int exit_code = EXIT_FAILURE;
-  struct gengetopt_args_info args_info;
+  struct args args = {0};
   fido_cred_t *cred = NULL;
   fido_dev_info_t *devlist = NULL;
   fido_dev_t *dev = NULL;
   const fido_dev_info_t *di = NULL;
   const char *path = NULL;
   size_t ndevs = 0;
+  int devopts = 0;
   int r;
 
-  /* NOTE: initializes args_info. on error, frees args_info and calls exit() */
-  if (cmdline_parser(argc, argv, &args_info) != 0)
-    goto err;
+  parse_args(argc, argv, &args);
+  fido_init(args.debug ? FIDO_DEBUG : 0);
 
-  if (args_info.help_given) {
-    cmdline_parser_print_help();
-    printf("\nReport bugs at <https://github.com/Yubico/pam-u2f>.\n");
-    exit_code = EXIT_SUCCESS;
-    goto err;
-  }
-
-  fido_init(args_info.debug_flag ? FIDO_DEBUG : 0);
-
-  if ((cred = prepare_cred(&args_info)) == NULL)
-    goto err;
-
-  devlist = fido_dev_info_new(64);
+  devlist = fido_dev_info_new(DEVLIST_LEN);
   if (!devlist) {
     fprintf(stderr, "error: fido_dev_info_new failed\n");
     goto err;
   }
 
-  r = fido_dev_info_manifest(devlist, 64, &ndevs);
+  r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
   if (r != FIDO_OK) {
     fprintf(stderr, "Unable to discover device(s), %s (%d)\n", fido_strerr(r),
             r);
@@ -325,7 +489,7 @@ int main(int argc, char *argv[]) {
       fflush(stderr);
       sleep(FREQUENCY);
 
-      r = fido_dev_info_manifest(devlist, 64, &ndevs);
+      r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
       if (r != FIDO_OK) {
         fprintf(stderr, "\nUnable to discover device(s), %s (%d)",
                 fido_strerr(r), r);
@@ -369,8 +533,30 @@ int main(int argc, char *argv[]) {
     goto err;
   }
 
-  if (make_cred(path, dev, cred) != 0 || verify_cred(cred) != 0 ||
-      print_authfile_line(&args_info, cred) != 0)
+  if (get_device_options(dev, &devopts) != 0) {
+    goto err;
+  }
+  if (args.pin_verification && !(devopts & PIN_SET)) {
+    warnx("%s", devopts & PIN_UNSET ? "device has no PIN"
+                                    : "device does not support PIN");
+    goto err;
+  }
+  if (args.user_verification && !(devopts & UV_SET)) {
+    warnx("%s", devopts & UV_UNSET
+                  ? "device has no built-in user verification configured"
+                  : "device does not support built-in user verification");
+    goto err;
+  }
+  if ((devopts & (UV_REQD | PIN_SET | UV_SET)) == UV_REQD) {
+    warnx("%s", "some form of user verification required but none configured");
+    goto err;
+  }
+
+  if ((cred = prepare_cred(&args)) == NULL)
+    goto err;
+
+  if (make_cred(&args, path, dev, cred, devopts) != 0 ||
+      verify_cred(cred) != 0 || print_authfile_line(&args, cred) != 0)
     goto err;
 
   exit_code = EXIT_SUCCESS;
@@ -382,7 +568,5 @@ err:
   fido_cred_free(&cred);
   fido_dev_free(&dev);
 
-  cmdline_parser_free(&args_info);
-
   exit(exit_code);
 }

Index: src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile
diff -u src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile:1.6 src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile:1.7
--- src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile:1.6	Tue May  9 11:02:59 2023
+++ src/external/bsd/pam-u2f/lib/security/pam-u2f/Makefile	Wed Jan 15 10:30:48 2025
@@ -1,13 +1,14 @@
-# $NetBSD: Makefile,v 1.6 2023/05/09 15:02:59 christos Exp $
+# $NetBSD: Makefile,v 1.7 2025/01/15 15:30:48 christos Exp $
 
 .include <bsd.own.mk>
+.include "${.PARSEDIR}/../../../Makefile.inc"
 .PATH: ${NETBSDSRCDIR}/external/bsd/pam-u2f/dist
 
 CPPFLAGS+= -DHAVE_SECURITY_PAM_APPL_H #-DHAVE_SECURITY_PAM_MODULES_H
 #CPPFLAGS+= -DPAM_DEBUG -DDEBUG_PAM
 
 LIB=	pam_u2f
-SRCS=	pam-u2f.c drop_privs.c util.c b64.c explicit_bzero.c
+SRCS=	pam-u2f.c util.c b64.c explicit_bzero.c expand.c debug.c
 MAN=	pam_u2f.8
 
 COPTS.util.c += -Wno-error=stack-protector -Wno-error=deprecated-declarations

Index: src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8
diff -u src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8:1.1 src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8:1.2
--- src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8:1.1	Mon Nov  2 01:40:11 2020
+++ src/external/bsd/pam-u2f/lib/security/pam-u2f/pam_u2f.8	Wed Jan 15 10:30:48 2025
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: pam_u2f
 .\"    Author: [FIXME: author] [see http://www.docbook.org/tdg5/en/html/author]
-.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\"      Date: 11/01/2020
+.\" Generator: DocBook XSL Stylesheets v1.79.2 <http://docbook.sf.net/>
+.\"      Date: Version 1.3.1
 .\"    Manual: PAM U2F Module Manual
 .\"    Source: pam-u2f
 .\"  Language: English
 .\"
-.TH "PAM_U2F" "8" "11/01/2020" "pam\-u2f" "PAM U2F Module Manual"
+.TH "PAM_U2F" "8" "Version 1\&.3\&.1" "pam\-u2f" "PAM U2F Module Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -54,17 +54,28 @@ for anything getting logged to it\&. It 
 .PP
 \fBorigin\fR=\fIorigin\fR
 .RS 4
-Set the origin for the U2F authentication procedure\&. If no value is specified, the origin "pam://$HOSTNAME" is used\&.
+Set the relying party ID for the FIDO authentication procedure\&. If no value is specified, the identifier "pam://$HOSTNAME" is used\&.
 .RE
 .PP
 \fBappid\fR=\fIappid\fR
 .RS 4
-Set the application ID for the U2F authentication procedure\&. If no value is specified, the same value used for origin is taken ("pam://$HOSTNAME" if also origin is not specified)\&.
+Set the application ID for the U2F authentication procedure\&. If no value is specified, the same value used for origin is taken ("pam://$HOSTNAME" if also origin is not specified)\&. This setting is only applicable for U2F credentials created with pamu2fcfg versions v1\&.0\&.8 or earlier\&. Note that on v1\&.1\&.0 and v1\&.1\&.1 of pam\-u2f, handling of this setting was temporarily broken if the value was not the same as the value of origin\&.
 .RE
 .PP
 \fBauthfile\fR=\fIfile\fR
 .RS 4
-Set the location of the file that holds the mappings of user names to keyHandles and user keys\&. The format is username:keyHandle1,public_key1:keyHandle2,public_key2:\&... the default location of the file is $XDG_CONFIG_HOME/Yubico/u2f_keys\&. If the environment variable is not set, $HOME/\&.config/Yubico/u2f_keys is used\&. An individual (per user) file may be configured relative to the users\*(Aq home dirs, i\&.e\&. "\&.ssh/u2f_keys"\&.
+Set the location of the file that holds the mappings of user names to keyHandles and user keys\&. An individual (per user) file may be configured relative to the users\*(Aq home dirs, e\&.g\&. "\&.ssh/u2f_keys"\&. If not specified, the location defaults to $XDG_CONFIG_HOME/Yubico/u2f_keys\&. If $XDG_CONFIG_HOME is not set, $HOME/\&.config/Yubico/u2f_keys is used\&. The authfile format is <username>:<KeyHandle1>,<UserKey1>,<CoseType1>,<Options1>:<KeyHandle2>,<UserKey2>,<CoseType2>,<Options2>:\&...
+.RE
+.PP
+\fBexpand\fR
+.RS 4
+Enables variable expansion within the authfile path:
+%u
+is expanded to the local user name (PAM_USER) and
+%%
+is expanded to
+%\&. Unknown expansion sequences result in an authentication error\&. See also
+openasuser\&.
 .RE
 .PP
 \fBauthpending_file\fR=\fIfile\fR
@@ -79,7 +90,7 @@ Set to enable authentication attempts to
 .PP
 \fBopenasuser\fR
 .RS 4
-Setuid to the authenticating user when opening the authfile\&. Useful when the user\(cqs home is stored on an NFS volume mounted with the root_squash option (which maps root to nobody which will not be able to read the file)\&. Note that after release 1\&.0\&.8 this is done by default when no global authfile or XDG_CONFIG_HOME environment variable has been set\&.
+Setuid to the authenticating user when opening the authfile\&. Useful when the user\(cqs home is stored on an NFS volume mounted with the root_squash option (which maps root to nobody which will not be able to read the file)\&. Note that after release 1\&.0\&.8 this is done by default when no global authfile (path is absolute) or XDG_CONFIG_HOME environment variable has been set\&.
 .RE
 .PP
 \fBalwaysok\fR
@@ -89,7 +100,7 @@ Set to enable all authentication attempt
 .PP
 \fBmax_devices\fR=\fIn_devices\fR
 .RS 4
-Maximum number of devices allowed per user (default is 24)\&. Devices specified in the authentication file that exceed this value will be ignored\&.
+Maximum number of devices (credentials) allowed per user (default is 24)\&. Devices specified in the authorization mapping file that exceed this value will be ignored\&.
 .RE
 .PP
 \fBinteractive\fR
@@ -126,42 +137,104 @@ below\&.
 .PP
 \fBuserpresence\fR=\fIint\fR
 .RS 4
-If 1, require user presence during authentication\&. If 0, do not request user presence during authentication\&. Otherwise, fallback to the authenticator\(cqs default behaviour\&.
+If 1, require user presence during authentication\&. If 0, do not request user presence during authentication\&. If omitted, fallback to the authenticator\(cqs default behaviour\&.
 .RE
 .PP
 \fBuserverification\fR=\fIint\fR
 .RS 4
-If 1, require user verification during authentication\&. If 0, do not request user verification during authentication\&. Otherwise, fallback to the authenticator\(cqs default behaviour\&.
+If 1, require user verification during authentication (e\&.g\&. biometrics)\&. If 0, do not request user verification during authentication\&. If omitted, fallback to the authenticator\(cqs default behaviour\&. If enabled, an authenticator with support for FIDO2 user verification is required\&.
 .RE
 .PP
 \fBpinverification\fR=\fIint\fR
 .RS 4
-If 1, require PIN verification during authentication\&. If 0, do not request PIN verification during authentication\&. Otherwise, fallback to the authenticator\(cqs default behaviour\&.
+If 1, require PIN verification during authentication\&. If 0, do not request PIN verification during authentication\&. If omitted, fallback to the authenticator\(cqs default behaviour\&. If enabled, an authenticator with support for a FIDO2 PIN is required\&.
+.RE
+.PP
+\fBsshformat\fR
+.RS 4
+Use credentials produced by versions of OpenSSH that have support for FIDO devices\&. It is not possible to mix native credentials and SSH credentials\&. Once this option is enabled all credentials will be parsed as SSH\&.
 .RE
 .SH "EXAMPLES"
 .sp
-auth sufficient pam_u2f\&.so debug origin=pam://$HOSTNAME appid=pam://$HOSTNAME
+Second factor authentication deferring user verification configuration to the authorization mapping file\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+auth required pam_u2f\&.so authfile=/etc/u2f_mappings cue
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Passwordless authentication enforcing PIN verification\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+auth sufficient pam_u2f\&.so authfile=/etc/u2f_mappings cue pinverification=1 userverification=0
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Passwordless authentication using built\-in UV (e\&.g\&. biometrics) with a fallback to PIN\&.
 .sp
-auth required pam_u2f\&.so origin=http://example\&.com appid=http://example\&.com authfile=/etc/yubikey_mappings
+.if n \{\
+.RS 4
+.\}
+.nf
+auth sufficient pam_u2f\&.so authfile=/etc/u2f_mappings cue pinverification=0 userverification=1
+auth sufficient pam_u2f\&.so authfile=/etc/u2f_mappings cue pinverification=1 userverification=0
+.fi
+.if n \{\
+.RE
+.\}
 .SH "CAVEATS"
 .sp
-By default the mapping file inside a home directory will be opened as the target user, whereas the central file will be opened as "root"\&. If the "XDG_CONFIG_HOME" variable is set, privileges will not be dropped unless the "openasuser" configuration setting is set\&.
+By default, relative paths to the authfile will be opened as the target user, whereas absolute paths will be opened as "root"\&. If the "XDG_CONFIG_HOME" variable is set, privileges will not be dropped unless the "openasuser" configuration setting is set\&.
 .sp
 Using pam\-u2f to secure the login to a computer while storing the mapping file in an encrypted home directory, will result in the impossibility of logging into the system\&. The partition is decrypted after login and the mapping file can not be accessed\&.
 .SH "NOTES"
 .sp
+\fBNodetect\fR
+.sp
 The "nodetect" option should be used with caution\&. pam_u2f checks that a key configured for the user is inserted before performing the full tactile authentication\&. This detection is done by sending a "check\-only" authentication request to all inserted tokens to so see if at least one of them responds affirmatively to one or more of the keyhandles configured for the user\&. By doing this, pam_u2f can avoid emitting the "cue" prompt (if configured), which can cause some confusing UI issues if the cue is emitted followed by the underlying library immediately failing the tactile authentication\&. This option is also useful to avoid an unintended 1\-second delay prior to the tactile authentication caused by versions of libu2f\-host <= 1\&.1\&.5\&.
 .sp
 If pam_u2f is configured to "cue" and "nodetect", an attacker can determine that pam_u2f is part of the authentication stack by inserting any random U2F token and performing an authentication attempt\&. In this scenario, the attacker would see the cue message followed by an immediate failure, whereas with detection enabled, the U2F authentication will fail silently\&. Understand that an attacker could choose a U2F token that alerts him or her in some way to the "check\-only" authentication attempt, so this precaution only pushes the issue back a step\&.
 .sp
 In summary, the detection feature was added to avoid confusing UI issues and to prevent leaking information about the authentication stack in very specific scenario when "cue" is configured\&. The "nodetect" option was added to avoid buggy sleep behavior in older versions of libu2f\-host and for hypothetical tokens that do not tolerate the double authentication\&. Detection is performed, and likewise "nodetect" honored, regardless of whether "cue" is also specified\&.
+.sp
+\fBSELinux\fR
+.sp
+Due to an issue with Fedora Linux, and possibly with other distributions that use SELinux, a system configured with pam\-u2f may end up in a situation where access to the credentials file is denied\&. If the nouserok option is also set, this will result in a successful authentication within the module, without using the FIDO authenticator\&.
+.sp
+In order to correctly update the security context the command \fBfixfiles onboot\fR should be used on existing installations
+.sp
+Moreover, to allow read access to an authfile or directory placed in a non\-standard location, the command
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# chcon \-R \-t auth_home_t /path/to/authfile
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+should be used\&.
+.sp
+For more information see https://access\&.redhat\&.com/security/cve/CVE\-2020\-24612\&;.
 .SH "BUGS"
 .sp
-Report pam\-u2f bugs in the issue tracker: \m[blue]\fBhttps://github\&.com/Yubico/pam\-u2f/issues\fR\m[]
+Report pam\-u2f bugs in the issue tracker: https://github\&.com/Yubico/pam\-u2f/issues
 .SH "SEE ALSO"
 .sp
 \fBpam\fR(7)
 .sp
-The pam\-u2f home page: \m[blue]\fBhttps://developers\&.yubico\&.com/pam\-u2f/\fR\m[]
+The pam\-u2f home page: https://developers\&.yubico\&.com/pam\-u2f/
 .sp
-YubiKeys can be obtained from Yubico: \m[blue]\fBhttp://www\&.yubico\&.com/\fR\m[]
+YubiKeys can be obtained from Yubico: http://www\&.yubico\&.com/

Added files:

Index: src/external/bsd/pam-u2f/Makefile.inc
diff -u /dev/null src/external/bsd/pam-u2f/Makefile.inc:1.1
--- /dev/null	Wed Jan 15 10:30:49 2025
+++ src/external/bsd/pam-u2f/Makefile.inc	Wed Jan 15 10:30:47 2025
@@ -0,0 +1,46 @@
+# $NetBSD: Makefile.inc,v 1.1 2025/01/15 15:30:47 christos Exp $
+
+CPPFLAGS+= \
+-DPACKAGE_VERSION=\"1.3.1\" \
+-DPACKAGE_STRING=\"pam_u2f\ 1.3.1\" \
+-DPACKAGE_BUGREPORT=\"https://github.com/Yubico/pam-u2f/issues\"; \
+-DPACKAGE_URL=\"https://developers.yubico.com/pam-u2f/\"; \
+-DPACKAGE=\"pam_u2f\" \
+-DVERSION=\"1.3.1\" \
+-DHAVE_STDIO_H=1 \
+-DHAVE_STDLIB_H=1 \
+-DHAVE_STRING_H=1 \
+-DHAVE_INTTYPES_H=1 \
+-DHAVE_STDINT_H=1 \
+-DHAVE_STRINGS_H=1 \
+-DHAVE_SYS_STAT_H=1 \
+-DHAVE_SYS_TYPES_H=1 \
+-DHAVE_UNISTD_H=1 \
+-DHAVE_WCHAR_H=1 \
+-DSTDC_HEADERS=1 \
+-D_ALL_SOURCE=1 \
+-D_DARWIN_C_SOURCE=1 \
+-D_GNU_SOURCE=1 \
+-D_HPUX_ALT_XOPEN_SOCKET_API=1 \
+-D_NETBSD_SOURCE=1 \
+-D_OPENBSD_SOURCE=1 \
+-D_POSIX_PTHREAD_SEMANTICS=1 \
+-D__STDC_WANT_IEC_60559_ATTRIBS_EXT__=1 \
+-D__STDC_WANT_IEC_60559_BFP_EXT__=1 \
+-D__STDC_WANT_IEC_60559_DFP_EXT__=1 \
+-D__STDC_WANT_IEC_60559_EXT__=1 \
+-D__STDC_WANT_IEC_60559_FUNCS_EXT__=1 \
+-D__STDC_WANT_IEC_60559_TYPES_EXT__=1 \
+-D__STDC_WANT_LIB_EXT2__=1 \
+-D__STDC_WANT_MATH_SPEC_FUNCS__=1 \
+-D_TANDEM_SOURCE=1 \
+-D__EXTENSIONS__=1 \
+-DHAVE_DLFCN_H=1 \
+-DLT_OBJDIR=\".libs/\" \
+-DHAVE_SECURITY_PAM_APPL_H=1 \
+-DHAVE_SECURITY_PAM_MODULES_H=1 \
+-DHAVE_SECURITY_OPENPAM_H=1 \
+-DHAVE_LIBPAM=1 \
+-DHAVE_OPENPAM_BORROW_CRED=1 \
+-DOPENSSL_API_COMPAT=0x10100000L \
+-DHAVE_STRLCPY=1
Index: src/external/bsd/pam-u2f/README
diff -u /dev/null src/external/bsd/pam-u2f/README:1.1
--- /dev/null	Wed Jan 15 10:30:49 2025
+++ src/external/bsd/pam-u2f/README	Wed Jan 15 10:30:47 2025
@@ -0,0 +1,5 @@
+# $NetBSD: README,v 1.1 2025/01/15 15:30:47 christos Exp $
+- Run autoreconf -f -i and build to see what defines the compiler generates
+  in the Makefiles and copy them to Makefile.inc
+- To regen the catalog pages: 
+    export XML_CATALOG_FILES=/usr/pkg/share/xml/catalog 

Reply via email to