summaryrefslogtreecommitdiffstats
path: root/plugins/sudoers/auth
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 12:52:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 12:52:13 +0000
commitf8e5c55a036f0e2e2a958e30456270f3f9eba933 (patch)
tree4a06ff510774a7a3373e492df4e2984d7b0664b1 /plugins/sudoers/auth
parentInitial commit. (diff)
downloadsudo-f8e5c55a036f0e2e2a958e30456270f3f9eba933.tar.xz
sudo-f8e5c55a036f0e2e2a958e30456270f3f9eba933.zip
Adding upstream version 1.9.5p2.upstream/1.9.5p2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/sudoers/auth')
-rw-r--r--plugins/sudoers/auth/API136
-rw-r--r--plugins/sudoers/auth/afs.c80
-rw-r--r--plugins/sudoers/auth/aix_auth.c308
-rw-r--r--plugins/sudoers/auth/bsdauth.c201
-rw-r--r--plugins/sudoers/auth/dce.c194
-rw-r--r--plugins/sudoers/auth/fwtk.c150
-rw-r--r--plugins/sudoers/auth/kerb5.c332
-rw-r--r--plugins/sudoers/auth/pam.c765
-rw-r--r--plugins/sudoers/auth/passwd.c122
-rw-r--r--plugins/sudoers/auth/rfc1938.c139
-rw-r--r--plugins/sudoers/auth/secureware.c109
-rw-r--r--plugins/sudoers/auth/securid5.c225
-rw-r--r--plugins/sudoers/auth/sia.c152
-rw-r--r--plugins/sudoers/auth/sudo_auth.c477
-rw-r--r--plugins/sudoers/auth/sudo_auth.h104
15 files changed, 3494 insertions, 0 deletions
diff --git a/plugins/sudoers/auth/API b/plugins/sudoers/auth/API
new file mode 100644
index 0000000..9d58827
--- /dev/null
+++ b/plugins/sudoers/auth/API
@@ -0,0 +1,136 @@
+NOTE: the Sudo auth API is subject to change
+
+Purpose: to provide a simple API for authentication methods that
+ encapsulates things nicely without turning into a maze
+ of #ifdef's
+
+The sudo_auth struct looks like this:
+
+typedef struct sudo_auth {
+ int flags; /* various flags, see below */
+ int status; /* status from verify routine */
+ char *name; /* name of the method in string form */
+ void *data; /* method-specific data pointer */
+
+ int (*init)(struct passwd *pw, sudo_auth *auth);
+ int (*setup)(struct passwd *pw, char **prompt, sudo_auth *auth);
+ int (*verify)(struct passwd *pw, char *p, sudo_auth *auth, struct sudo_conv_callback *callback);
+ int (*approval)(struct passwd *pw, sudo_auth *auth);
+ int (*cleanup)(struct passwd *pw, sudo_auth *auth, bool force);
+ int (*begin_session)(struct passwd *pw, char **user_env[], struct sudo_auth *auth);
+ int (*end_session)(struct passwd *pw, struct sudo_auth *auth);
+} sudo_auth;
+
+The variables in the struct are as follows:
+ flags Bitwise binary flags, see below.
+
+ status Contains the return value from the last run of
+ the "verify" function. Starts out as AUTH_FAILURE.
+
+ name The name of the authentication method as a C string.
+
+ data A pointer to method-specific data. This is passed to
+ all the functions of an auth method and is usually
+ initialized in the "init" or "setup" routines.
+
+Possible values of sudo_auth.flags:
+ FLAG_DISABLED Set if an "init" or "setup" function fails.
+
+ FLAG_STANDALONE If set, this indicates that the method must
+ be the only auth method configured, and that
+ it will prompt for the password itself.
+
+ FLAG_ONEANDONLY If set, this indicates that the method is the
+ only one in use. Can be used by auth functions
+ to determine whether to return a fatal or nonfatal
+ error.
+
+The member functions can return the following values:
+ AUTH_SUCCESS Function succeeded. For a ``verify'' function
+ this means the user correctly authenticated.
+
+ AUTH_FAILURE Function failed. If this is an ``init'' or
+ ``setup'' routine, the auth method will be
+ marked as !configured.
+
+ AUTH_FATAL A fatal error occurred. The routine should have
+ written an error message to stderr and optionally
+ sent mail to the administrator.
+ When verify_user() gets AUTH_FATAL from an auth
+ function it does an exit(1).
+
+The functions in the struct are as follows:
+
+ int init(struct passwd *pw, sudo_auth *auth)
+ Function to do any one-time initialization for the auth
+ method. All of the "init" functions are run before anything
+ else.
+
+ int setup(struct passwd *pw, char **prompt, sudo_auth *auth)
+ Function to do method-specific setup. All the "setup"
+ routines are run before any of the "verify" routines. A
+ pointer to the prompt string may be used to add method-specific
+ info to the prompt.
+
+ int verify(struct passwd *pw, char *p, sudo_auth *auth, struct sudo_conv_callback *callback)
+ Function to do user verification for this auth method. For
+ standalone auth methods ``p'' is the prompt string. For
+ normal auth methods, ``p'' is the password the user entered.
+ The callback should be passed to auth_getpass() to allow sudoers
+ to unlock the ticket file when sudo is suspended.
+ Note that standalone auth methods are responsible for
+ rerading the password themselves.
+
+ int approval(struct passwd *pw, struct sudo_auth *auth)
+ Function to perform account management and approval *after*
+ the user has authenticated successfully. This function may
+ check for expired accounts, perform time of day restrictions, etc.
+ For PAM, this calls pam_acct_mgmt(). For BSD auth, it calls
+ auth_approval().
+
+ int cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+ Function to do per-auth method cleanup. This is only run
+ at the end of the authentication process, after the user
+ has completely failed or succeeded to authenticate.
+ The ``auth->status'' variable contains the result of the
+ last authentication attempt which may be interesting.
+ If the force flag is set, cleanup should happen immediately.
+
+ int begin_session(struct passwd *pw, char **user_env[], struct sudo_auth *auth)
+ Function to begin a user session. This is used for session handling
+ in PAM and SIA.
+
+ int end_session(struct passwd *pw, struct sudo_auth *auth)
+ Function to end a user session. This is used for session handling
+ in PAM and SIA.
+
+A note about standalone methods. Some authentication methods can't
+coexist with any others. This may be because they encapsulate other
+methods (pam, sia) or because they have a special way of interacting
+with the user (securid).
+
+Adding a new authentication method:
+
+Each method should live in its own file. Add prototypes for the functions
+in sudo_auth.h.
+
+Add the method to the ``auth_switch'' in sudo_auth.c. Note that
+standalone methods must go first. If ``fooauth'' is a normal auth
+method, its entry would look like:
+
+#ifdef HAVE_FOOAUTH
+AUTH_ENTRY("foo", 0, foo_init, foo_setup, foo_verify,
+ foo_cleanup, foo_begin_session, foo_end_session)
+#endif
+
+If this is a standalone method, it would be:
+
+#ifdef HAVE_FOOAUTH
+AUTH_ENTRY("foo", FLAG_STANDALONE, foo_init, foo_setup, foo_verify,
+ foo_cleanup, foo_begin_session, foo_end_session)
+#endif
+
+If the method needs to run as the user, not root, add FLAG_USER to
+the second argument in the AUTH_ENTRY line. If you don't have an
+init/setup/cleanup/begin/end routine, just use a NULL for that
+field.
diff --git a/plugins/sudoers/auth/afs.c b/plugins/sudoers/auth/afs.c
new file mode 100644
index 0000000..616a8a8
--- /dev/null
+++ b/plugins/sudoers/auth/afs.c
@@ -0,0 +1,80 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999, 2001-2005, 2007, 2010-2012, 2014-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_AFS
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <afs/stds.h>
+#include <afs/kautils.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+int
+sudo_afs_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ struct ktc_encryptionKey afs_key;
+ struct ktc_token afs_token;
+ debug_decl(sudo_afs_verify, SUDOERS_DEBUG_AUTH);
+
+ /* Try to just check the password */
+ ka_StringToKey(pass, NULL, &afs_key);
+ if (ka_GetAdminToken(pw->pw_name, /* name */
+ NULL, /* instance */
+ NULL, /* realm */
+ &afs_key, /* key (contains password) */
+ 0, /* lifetime */
+ &afs_token, /* token */
+ 0) == 0) /* new */
+ debug_return_int(AUTH_SUCCESS);
+
+ /* Fall back on old method XXX - needed? */
+ setpag();
+ if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
+ pw->pw_name, /* name */
+ NULL, /* instance */
+ NULL, /* realm */
+ pass, /* password */
+ 0, /* lifetime */
+ NULL, /* expiration ptr (unused) */
+ 0, /* spare */
+ NULL) == 0) /* reason */
+ debug_return_int(AUTH_SUCCESS);
+
+ debug_return_int(AUTH_FAILURE);
+}
+
+#endif HAVE_AFS
diff --git a/plugins/sudoers/auth/aix_auth.c b/plugins/sudoers/auth/aix_auth.c
new file mode 100644
index 0000000..6272811
--- /dev/null
+++ b/plugins/sudoers/auth/aix_auth.c
@@ -0,0 +1,308 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_AIXAUTH
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <usersec.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+/*
+ * For a description of the AIX authentication API, see
+ * http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/basetrf1/authenticate.htm
+ */
+
+#ifdef HAVE_PAM
+# define AIX_AUTH_UNKNOWN 0
+# define AIX_AUTH_STD 1
+# define AIX_AUTH_PAM 2
+
+static int
+sudo_aix_authtype(void)
+{
+ size_t linesize = 0;
+ ssize_t len;
+ char *cp, *line = NULL;
+ bool in_stanza = false;
+ int authtype = AIX_AUTH_UNKNOWN;
+ FILE *fp;
+ debug_decl(sudo_aix_authtype, SUDOERS_DEBUG_AUTH);
+
+ if ((fp = fopen("/etc/security/login.cfg", "r")) == NULL)
+ debug_return_int(AIX_AUTH_UNKNOWN);
+
+ while ((len = getdelim(&line, &linesize, '\n', fp)) != -1) {
+ /* First remove comments. */
+ if ((cp = strchr(line, '#')) != NULL) {
+ *cp = '\0';
+ len = (ssize_t)(cp - line);
+ }
+
+ /* Next remove trailing newlines and whitespace. */
+ while (len > 0 && isspace((unsigned char)line[len - 1]))
+ line[--len] = '\0';
+
+ /* Skip blank lines. */
+ if (len == 0)
+ continue;
+
+ /* Match start of the usw stanza. */
+ if (!in_stanza) {
+ if (strncmp(line, "usw:", 4) == 0)
+ in_stanza = true;
+ continue;
+ }
+
+ /* Check for end of the usw stanza. */
+ if (!isblank((unsigned char)line[0])) {
+ in_stanza = false;
+ break;
+ }
+
+ /* Skip leading blanks. */
+ cp = line;
+ do {
+ cp++;
+ } while (isblank((unsigned char)*cp));
+
+ /* Match "auth_type = (PAM_AUTH|STD_AUTH)". */
+ if (strncmp(cp, "auth_type", 9) != 0)
+ continue;
+ cp += 9;
+ while (isblank((unsigned char)*cp))
+ cp++;
+ if (*cp++ != '=')
+ continue;
+ while (isblank((unsigned char)*cp))
+ cp++;
+ if (strcmp(cp, "PAM_AUTH") == 0) {
+ authtype = AIX_AUTH_PAM;
+ break;
+ }
+ if (strcmp(cp, "STD_AUTH") == 0) {
+ authtype = AIX_AUTH_STD;
+ break;
+ }
+ }
+ free(line);
+ fclose(fp);
+
+ debug_return_int(authtype);
+}
+#endif /* HAVE_PAM */
+
+int
+sudo_aix_init(struct passwd *pw, sudo_auth *auth)
+{
+ debug_decl(sudo_aix_init, SUDOERS_DEBUG_AUTH);
+
+#ifdef HAVE_PAM
+ /* Check auth_type in /etc/security/login.cfg. */
+ if (sudo_aix_authtype() == AIX_AUTH_PAM) {
+ if (sudo_pam_init_quiet(pw, auth) == AUTH_SUCCESS) {
+ /* Fail AIX authentication so we can use PAM instead. */
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+#endif
+ debug_return_int(AUTH_SUCCESS);
+}
+
+/* Ignore AIX password incorrect message */
+static bool
+sudo_aix_valid_message(const char *message)
+{
+ const char *cp;
+ const char badpass_msgid[] = "3004-300";
+ debug_decl(sudo_aix_valid_message, SUDOERS_DEBUG_AUTH);
+
+ if (message == NULL || message[0] == '\0')
+ debug_return_bool(false);
+
+ /* Match "3004-300: You entered an invalid login name or password" */
+ for (cp = message; *cp != '\0'; cp++) {
+ if (isdigit((unsigned char)*cp)) {
+ if (strncmp(cp, badpass_msgid, strlen(badpass_msgid)) == 0)
+ debug_return_bool(false);
+ break;
+ }
+ }
+ debug_return_bool(true);
+}
+
+/*
+ * Change the user's password. If root changes the user's password
+ * the ADMCHG flag is set on the account (and the user must change
+ * it again) so we run passwd(1) as the user. This does mean that
+ * the user will need to re-enter their original password again,
+ * unlike with su(1). We may consider using pwdadm(1) as root to
+ * change the password and then clear the flag in the future.
+ */
+static bool
+sudo_aix_change_password(const char *user)
+{
+ struct sigaction sa, savechld;
+ pid_t child, pid;
+ bool ret = false;
+ sigset_t mask;
+ int status;
+ debug_decl(sudo_aix_change_password, SUDOERS_DEBUG_AUTH);
+
+ /* Set SIGCHLD handler to default since we call waitpid() below. */
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_DFL;
+ (void) sigaction(SIGCHLD, &sa, &savechld);
+
+ switch (child = sudo_debug_fork()) {
+ case -1:
+ /* error */
+ sudo_warn("%s", U_("unable to fork"));
+ break;
+ case 0:
+ /* child, run passwd(1) */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGQUIT);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ set_perms(PERM_USER);
+ execl("/usr/bin/passwd", "passwd", user, (char *)NULL);
+ sudo_warn("passwd");
+ _exit(127);
+ /* NOTREACHED */
+ default:
+ /* parent */
+ break;
+ }
+
+ /* Wait for passwd(1) to complete. */
+ do {
+ pid = waitpid(child, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "child (%d) exit value %d", (int)child, status);
+ if (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ ret = true;
+
+ /* Restore saved SIGCHLD handler. */
+ (void) sigaction(SIGCHLD, &savechld, NULL);
+
+ debug_return_bool(ret);
+}
+
+int
+sudo_aix_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char *pass, *message = NULL;
+ int result = 1, reenter = 0;
+ int ret = AUTH_SUCCESS;
+ debug_decl(sudo_aix_verify, SUDOERS_DEBUG_AUTH);
+
+ do {
+ pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ if (pass == NULL)
+ break;
+ free(message);
+ message = NULL;
+ result = authenticate(pw->pw_name, pass, &reenter, &message);
+ freezero(pass, strlen(pass));
+ prompt = message;
+ } while (reenter);
+
+ if (result != 0) {
+ /* Display error message, if any. */
+ if (sudo_aix_valid_message(message))
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "%s", message);
+ ret = pass ? AUTH_FAILURE : AUTH_INTR;
+ }
+ free(message);
+ message = NULL;
+
+ /* Check if password expired and allow user to change it if possible. */
+ if (ret == AUTH_SUCCESS) {
+ result = passwdexpired(pw->pw_name, &message);
+ if (message != NULL && message[0] != '\0') {
+ int msg_type = SUDO_CONV_PREFER_TTY;
+ msg_type |= result ? SUDO_CONV_ERROR_MSG : SUDO_CONV_INFO_MSG,
+ sudo_printf(msg_type, "%s", message);
+ free(message);
+ message = NULL;
+ }
+ switch (result) {
+ case 0:
+ /* password not expired. */
+ break;
+ case 1:
+ /* password expired, user must change it */
+ if (!sudo_aix_change_password(pw->pw_name)) {
+ sudo_warnx(U_("unable to change password for %s"), pw->pw_name);
+ ret = AUTH_FATAL;
+ }
+ break;
+ case 2:
+ /* password expired, only admin can change it */
+ ret = AUTH_FATAL;
+ break;
+ default:
+ /* error (-1) */
+ sudo_warn("passwdexpired");
+ ret = AUTH_FATAL;
+ break;
+ }
+ }
+
+ debug_return_int(ret);
+}
+
+int
+sudo_aix_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ debug_decl(sudo_aix_cleanup, SUDOERS_DEBUG_AUTH);
+
+ /* Unset AUTHSTATE as it may not be correct for the runas user. */
+ if (sudo_unsetenv("AUTHSTATE") == -1)
+ debug_return_int(AUTH_FAILURE);
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+#endif /* HAVE_AIXAUTH */
diff --git a/plugins/sudoers/auth/bsdauth.c b/plugins/sudoers/auth/bsdauth.c
new file mode 100644
index 0000000..fec7402
--- /dev/null
+++ b/plugins/sudoers/auth/bsdauth.c
@@ -0,0 +1,201 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2000-2005, 2007-2008, 2010-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_BSD_AUTH_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <signal.h>
+
+#include <login_cap.h>
+#include <bsd_auth.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+# ifndef LOGIN_DEFROOTCLASS
+# define LOGIN_DEFROOTCLASS "daemon"
+# endif
+
+struct bsdauth_state {
+ auth_session_t *as;
+ login_cap_t *lc;
+};
+
+int
+bsdauth_init(struct passwd *pw, sudo_auth *auth)
+{
+ static struct bsdauth_state state;
+ debug_decl(bsdauth_init, SUDOERS_DEBUG_AUTH);
+
+ /* Get login class based on auth user, which may not be invoking user. */
+ if (pw->pw_class && *pw->pw_class)
+ state.lc = login_getclass(pw->pw_class);
+ else
+ state.lc = login_getclass(pw->pw_uid ? LOGIN_DEFCLASS : LOGIN_DEFROOTCLASS);
+ if (state.lc == NULL) {
+ log_warning(0,
+ N_("unable to get login class for user %s"), pw->pw_name);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ if ((state.as = auth_open()) == NULL) {
+ log_warning(0, N_("unable to begin bsd authentication"));
+ login_close(state.lc);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ /* XXX - maybe check the auth style earlier? */
+ login_style = login_getstyle(state.lc, login_style, "auth-sudo");
+ if (login_style == NULL) {
+ log_warningx(0, N_("invalid authentication type"));
+ auth_close(state.as);
+ login_close(state.lc);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ if (auth_setitem(state.as, AUTHV_STYLE, login_style) < 0 ||
+ auth_setitem(state.as, AUTHV_NAME, pw->pw_name) < 0 ||
+ auth_setitem(state.as, AUTHV_CLASS, login_class) < 0) {
+ log_warningx(0, N_("unable to initialize BSD authentication"));
+ auth_close(state.as);
+ login_close(state.lc);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ auth->data = (void *) &state;
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+bsdauth_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char *pass;
+ char *s;
+ size_t len;
+ int authok = 0;
+ struct sigaction sa, osa;
+ auth_session_t *as = ((struct bsdauth_state *) auth->data)->as;
+ debug_decl(bsdauth_verify, SUDOERS_DEBUG_AUTH);
+
+ /* save old signal handler */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_DFL;
+ (void) sigaction(SIGCHLD, &sa, &osa);
+
+ /*
+ * If there is a challenge then print that instead of the normal
+ * prompt. If the user just hits return we prompt again with echo
+ * turned on, which is useful for challenge/response things like
+ * S/Key.
+ */
+ if ((s = auth_challenge(as)) == NULL) {
+ pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ } else {
+ pass = auth_getpass(s, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ if (pass && *pass == '\0') {
+ if ((prompt = strrchr(s, '\n')))
+ prompt++;
+ else
+ prompt = s;
+
+ /*
+ * Append '[echo on]' to the last line of the challenge and
+ * reprompt with echo turned on.
+ */
+ len = strlen(prompt) - 1;
+ while (isspace(prompt[len]) || prompt[len] == ':')
+ prompt[len--] = '\0';
+ if (asprintf(&s, "%s [echo on]: ", prompt) == -1) {
+ log_warningx(0, N_("unable to allocate memory"));
+ debug_return_int(AUTH_FATAL);
+ }
+ free(pass);
+ pass = auth_getpass(s, SUDO_CONV_PROMPT_ECHO_ON, callback);
+ free(s);
+ }
+ }
+
+ if (pass) {
+ authok = auth_userresponse(as, pass, 1);
+ freezero(pass, strlen(pass));
+ }
+
+ /* restore old signal handler */
+ (void) sigaction(SIGCHLD, &osa, NULL);
+
+ if (authok)
+ debug_return_int(AUTH_SUCCESS);
+
+ if (!pass)
+ debug_return_int(AUTH_INTR);
+
+ if ((s = auth_getvalue(as, "errormsg")) != NULL)
+ log_warningx(0, "%s", s);
+ debug_return_int(AUTH_FAILURE);
+}
+
+int
+bsdauth_approval(struct passwd *pw, sudo_auth *auth, bool exempt)
+{
+ struct bsdauth_state *state = auth->data;
+ debug_decl(bsdauth_approval, SUDOERS_DEBUG_AUTH);
+
+ if (auth_approval(state->as, state->lc, pw->pw_name, "auth-sudo") == 0) {
+ if (auth_getstate(state->as) & AUTH_EXPIRED)
+ log_warningx(0, "%s", N_("your account has expired"));
+ else
+ log_warningx(0, "%s", N_("approval failed"));
+ debug_return_int(AUTH_FAILURE);
+ }
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+bsdauth_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ struct bsdauth_state *state = auth->data;
+ debug_decl(bsdauth_cleanup, SUDOERS_DEBUG_AUTH);
+
+ if (state != NULL) {
+ auth_close(state->as);
+ login_close(state->lc);
+ }
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+#endif /* HAVE_BSD_AUTH_H */
diff --git a/plugins/sudoers/auth/dce.c b/plugins/sudoers/auth/dce.c
new file mode 100644
index 0000000..e1d911c
--- /dev/null
+++ b/plugins/sudoers/auth/dce.c
@@ -0,0 +1,194 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1996, 1998-2005, 2010-2012, 2014-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+/*
+ * The code below basically comes from the examples supplied on
+ * the OSF DCE 1.0.3 manpages for the sec_login routines, with
+ * enough additional polishing to make the routine work with the
+ * rest of sudo.
+ *
+ * This code is known to work on HP 700 and 800 series systems
+ * running HP-UX 9.X and 10.X, with either HP's version 1.2.1 of DCE.
+ * (aka, OSF DCE 1.0.3) or with HP's version 1.4 of DCE (aka, OSF
+ * DCE 1.1).
+ */
+
+#include <config.h>
+
+#ifdef HAVE_DCE
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <dce/rpc.h>
+#include <dce/sec_login.h>
+#include <dce/dce_error.h> /* required to call dce_error_inq_text routine */
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+static int check_dce_status(error_status_t, char *);
+
+int
+sudo_dce_verify(struct passwd *pw, char *plain_pw, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ struct passwd temp_pw;
+ sec_passwd_rec_t password_rec;
+ sec_login_handle_t login_context;
+ boolean32 reset_passwd;
+ sec_login_auth_src_t auth_src;
+ error_status_t status;
+ debug_decl(sudo_dce_verify, SUDOERS_DEBUG_AUTH);
+
+ /*
+ * Create the local context of the DCE principal necessary
+ * to perform authenticated network operations. The network
+ * identity set up by this operation cannot be used until it
+ * is validated via sec_login_validate_identity().
+ */
+ if (sec_login_setup_identity((unsigned_char_p_t) pw->pw_name,
+ sec_login_no_flags, &login_context, &status)) {
+
+ if (check_dce_status(status, "sec_login_setup_identity(1):"))
+ debug_return_int(AUTH_FAILURE);
+
+ password_rec.key.key_type = sec_passwd_plain;
+ password_rec.key.tagged_union.plain = (idl_char *) plain_pw;
+ password_rec.pepper = NULL;
+ password_rec.version_number = sec_passwd_c_version_none;
+
+ /* Validate the login context with the password */
+ if (sec_login_validate_identity(login_context, &password_rec,
+ &reset_passwd, &auth_src, &status)) {
+
+ if (check_dce_status(status, "sec_login_validate_identity(1):"))
+ debug_return_int(AUTH_FAILURE);
+
+ /*
+ * Certify that the DCE Security Server used to set
+ * up and validate a login context is legitimate. Makes
+ * sure that we didn't get spoofed by another DCE server.
+ */
+ if (!sec_login_certify_identity(login_context, &status)) {
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "Whoa! Bogus authentication server!\n");
+ (void) check_dce_status(status,"sec_login_certify_identity(1):");
+ debug_return_int(AUTH_FAILURE);
+ }
+ if (check_dce_status(status, "sec_login_certify_identity(2):"))
+ debug_return_int(AUTH_FAILURE);
+
+ /*
+ * Sets the network credentials to those specified
+ * by the now validated login context.
+ */
+ sec_login_set_context(login_context, &status);
+ if (check_dce_status(status, "sec_login_set_context:"))
+ debug_return_int(AUTH_FAILURE);
+
+ /*
+ * Oops, your credentials were no good. Possibly
+ * caused by clock times out of adjustment between
+ * DCE client and DCE security server...
+ */
+ if (auth_src != sec_login_auth_src_network) {
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "You have no network credentials.\n");
+ debug_return_int(AUTH_FAILURE);
+ }
+ /* Check if the password has aged and is thus no good */
+ if (reset_passwd) {
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "Your DCE password needs resetting.\n");
+ debug_return_int(AUTH_FAILURE);
+ }
+
+ /*
+ * We should be a valid user by this point. Pull the
+ * user's password structure from the DCE security
+ * server just to make sure. If we get it with no
+ * problems, then we really are legitimate...
+ */
+ sec_login_get_pwent(login_context, (sec_login_passwd_t) &temp_pw,
+ &status);
+ if (check_dce_status(status, "sec_login_get_pwent:"))
+ debug_return_int(AUTH_FAILURE);
+
+ /*
+ * If we get to here, then the pwent above properly fetched
+ * the password structure from the DCE registry, so the user
+ * must be valid. We don't really care what the user's
+ * registry password is, just that the user could be
+ * validated. In fact, if we tried to compare the local
+ * password to the DCE entry at this point, the operation
+ * would fail if the hidden password feature is turned on,
+ * because the password field would contain an asterisk.
+ * Also go ahead and destroy the user's DCE login context
+ * before we leave here (and don't bother checking the
+ * status), in order to clean up credentials files in
+ * /opt/dcelocal/var/security/creds. By doing this, we are
+ * assuming that the user will not need DCE authentication
+ * later in the program, only local authentication. If this
+ * is not true, then the login_context will have to be
+ * returned to the calling program, and the context purged
+ * somewhere later in the program.
+ */
+ sec_login_purge_context(&login_context, &status);
+ debug_return_int(AUTH_SUCCESS);
+ } else {
+ if(check_dce_status(status, "sec_login_validate_identity(2):"))
+ debug_return_int(AUTH_FAILURE);
+ sec_login_purge_context(&login_context, &status);
+ if(check_dce_status(status, "sec_login_purge_context:"))
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+ (void) check_dce_status(status, "sec_login_setup_identity(2):");
+ debug_return_int(AUTH_FAILURE);
+}
+
+/* Returns 0 for DCE "ok" status, 1 otherwise */
+static int
+check_dce_status(error_status_t input_status, char *comment)
+{
+ int error_stat;
+ unsigned char error_string[dce_c_error_string_len];
+ debug_decl(check_dce_status, SUDOERS_DEBUG_AUTH);
+
+ if (input_status == rpc_s_ok)
+ debug_return_int(0);
+ dce_error_inq_text(input_status, error_string, &error_stat);
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "%s %s\n", comment, error_string);
+ debug_return_int(1);
+}
+
+#endif /* HAVE_DCE */
diff --git a/plugins/sudoers/auth/fwtk.c b/plugins/sudoers/auth/fwtk.c
new file mode 100644
index 0000000..7f3385d
--- /dev/null
+++ b/plugins/sudoers/auth/fwtk.c
@@ -0,0 +1,150 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2008, 2010-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_FWTK
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <auth.h>
+#include <firewall.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+int
+sudo_fwtk_init(struct passwd *pw, sudo_auth *auth)
+{
+ static Cfg *confp; /* Configuration entry struct */
+ char resp[128]; /* Response from the server */
+ debug_decl(sudo_fwtk_init, SUDOERS_DEBUG_AUTH);
+
+ if ((confp = cfg_read("sudo")) == (Cfg *)-1) {
+ sudo_warnx("%s", U_("unable to read fwtk config"));
+ debug_return_int(AUTH_FATAL);
+ }
+
+ if (auth_open(confp)) {
+ sudo_warnx("%s", U_("unable to connect to authentication server"));
+ debug_return_int(AUTH_FATAL);
+ }
+
+ /* Get welcome message from auth server */
+ if (auth_recv(resp, sizeof(resp))) {
+ sudo_warnx("%s", U_("lost connection to authentication server"));
+ debug_return_int(AUTH_FATAL);
+ }
+ if (strncmp(resp, "Authsrv ready", 13) != 0) {
+ sudo_warnx(U_("authentication server error:\n%s"), resp);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_fwtk_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char *pass; /* Password from the user */
+ char buf[SUDO_CONV_REPL_MAX + 12]; /* General prupose buffer */
+ char resp[128]; /* Response from the server */
+ int error;
+ debug_decl(sudo_fwtk_verify, SUDOERS_DEBUG_AUTH);
+
+ /* Send username to authentication server. */
+ (void) snprintf(buf, sizeof(buf), "authorize %s 'sudo'", pw->pw_name);
+restart:
+ if (auth_send(buf) || auth_recv(resp, sizeof(resp))) {
+ sudo_warnx("%s", U_("lost connection to authentication server"));
+ debug_return_int(AUTH_FATAL);
+ }
+
+ /* Get the password/response from the user. */
+ if (strncmp(resp, "challenge ", 10) == 0) {
+ (void) snprintf(buf, sizeof(buf), "%s\nResponse: ", &resp[10]);
+ pass = auth_getpass(buf, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ if (pass && *pass == '\0') {
+ free(pass);
+ pass = auth_getpass("Response [echo on]: ",
+ SUDO_CONV_PROMPT_ECHO_ON, callback);
+ }
+ } else if (strncmp(resp, "chalnecho ", 10) == 0) {
+ pass = auth_getpass(&resp[10], SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ } else if (strncmp(resp, "password", 8) == 0) {
+ pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ } else if (strncmp(resp, "display ", 8) == 0) {
+ sudo_printf(SUDO_CONV_INFO_MSG|SUDO_CONV_PREFER_TTY, "%s\n", &resp[8]);
+ strlcpy(buf, "response noop", sizeof(buf));
+ goto restart;
+ } else {
+ sudo_warnx("%s", resp);
+ debug_return_int(AUTH_FATAL);
+ }
+ if (!pass) { /* ^C or error */
+ debug_return_int(AUTH_INTR);
+ }
+
+ /* Send the user's response to the server */
+ (void) snprintf(buf, sizeof(buf), "response '%s'", pass);
+ if (auth_send(buf) || auth_recv(resp, sizeof(resp))) {
+ sudo_warnx("%s", U_("lost connection to authentication server"));
+ error = AUTH_FATAL;
+ goto done;
+ }
+
+ if (strncmp(resp, "ok", 2) == 0) {
+ error = AUTH_SUCCESS;
+ goto done;
+ }
+
+ /* Main loop prints "Permission Denied" or insult. */
+ if (strcmp(resp, "Permission Denied.") != 0)
+ sudo_warnx("%s", resp);
+ error = AUTH_FAILURE;
+done:
+ explicit_bzero(buf, sizeof(buf));
+ freezero(pass, strlen(pass));
+ debug_return_int(error);
+}
+
+int
+sudo_fwtk_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ debug_decl(sudo_fwtk_cleanup, SUDOERS_DEBUG_AUTH);
+
+ auth_close();
+ debug_return_int(AUTH_SUCCESS);
+}
+
+#endif /* HAVE_FWTK */
diff --git a/plugins/sudoers/auth/kerb5.c b/plugins/sudoers/auth/kerb5.c
new file mode 100644
index 0000000..a607226
--- /dev/null
+++ b/plugins/sudoers/auth/kerb5.c
@@ -0,0 +1,332 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007-2008, 2010-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_KERB5
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <krb5.h>
+#ifdef HAVE_HEIMDAL
+#include <com_err.h>
+#endif
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+#ifdef HAVE_HEIMDAL
+# define extract_name(c, p) krb5_principal_get_comp_string(c, p, 1)
+# define krb5_free_data_contents(c, d) krb5_data_free(d)
+#else
+# define extract_name(c, p) (krb5_princ_component(c, p, 1)->data)
+#endif
+
+#ifndef HAVE_KRB5_VERIFY_USER
+static int verify_krb_v5_tgt(krb5_context, krb5_creds *, char *);
+#endif
+static struct _sudo_krb5_data {
+ krb5_context sudo_context;
+ krb5_principal princ;
+ krb5_ccache ccache;
+} sudo_krb5_data = { NULL, NULL, NULL };
+typedef struct _sudo_krb5_data *sudo_krb5_datap;
+
+#ifdef SUDO_KRB5_INSTANCE
+static const char *sudo_krb5_instance = SUDO_KRB5_INSTANCE;
+#else
+static const char *sudo_krb5_instance = NULL;
+#endif
+
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+static krb5_error_code
+krb5_get_init_creds_opt_alloc(krb5_context context,
+ krb5_get_init_creds_opt **opts)
+{
+ *opts = malloc(sizeof(krb5_get_init_creds_opt));
+ if (*opts == NULL)
+ return KRB5_CC_NOMEM;
+ krb5_get_init_creds_opt_init(*opts);
+ return 0;
+}
+
+static void
+krb5_get_init_creds_opt_free(krb5_get_init_creds_opt *opts)
+{
+ free(opts);
+}
+#endif
+
+int
+sudo_krb5_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
+{
+ static char *krb5_prompt;
+ debug_decl(sudo_krb5_init, SUDOERS_DEBUG_AUTH);
+
+ if (krb5_prompt == NULL) {
+ krb5_context sudo_context;
+ krb5_principal princ;
+ char *pname;
+ krb5_error_code error;
+
+ sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
+ princ = ((sudo_krb5_datap) auth->data)->princ;
+
+ /*
+ * Really, we need to tell the caller not to prompt for password. The
+ * API does not currently provide this unless the auth is standalone.
+ */
+ if ((error = krb5_unparse_name(sudo_context, princ, &pname))) {
+ log_warningx(0,
+ N_("%s: unable to convert principal to string ('%s'): %s"),
+ auth->name, pw->pw_name, error_message(error));
+ debug_return_int(AUTH_FAILURE);
+ }
+
+ /* Only rewrite prompt if user didn't specify their own. */
+ /*if (!strcmp(prompt, PASSPROMPT)) { */
+ if (asprintf(&krb5_prompt, "Password for %s: ", pname) == -1) {
+ log_warningx(0, N_("unable to allocate memory"));
+ free(pname);
+ debug_return_int(AUTH_FATAL);
+ }
+ /*}*/
+ free(pname);
+ }
+ *promptp = krb5_prompt;
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_krb5_init(struct passwd *pw, sudo_auth *auth)
+{
+ krb5_context sudo_context;
+ krb5_error_code error;
+ char cache_name[64], *pname = pw->pw_name;
+ debug_decl(sudo_krb5_init, SUDOERS_DEBUG_AUTH);
+
+ auth->data = (void *) &sudo_krb5_data; /* Stash all our data here */
+
+ if (sudo_krb5_instance != NULL) {
+ int len = asprintf(&pname, "%s%s%s", pw->pw_name,
+ sudo_krb5_instance[0] != '/' ? "/" : "", sudo_krb5_instance);
+ if (len == -1) {
+ log_warningx(0, N_("unable to allocate memory"));
+ debug_return_int(AUTH_FATAL);
+ }
+ }
+
+#ifdef HAVE_KRB5_INIT_SECURE_CONTEXT
+ error = krb5_init_secure_context(&(sudo_krb5_data.sudo_context));
+#else
+ error = krb5_init_context(&(sudo_krb5_data.sudo_context));
+#endif
+ if (error)
+ goto done;
+ sudo_context = sudo_krb5_data.sudo_context;
+
+ error = krb5_parse_name(sudo_context, pname, &(sudo_krb5_data.princ));
+ if (error) {
+ log_warningx(0, N_("%s: unable to parse '%s': %s"), auth->name, pname,
+ error_message(error));
+ goto done;
+ }
+
+ (void) snprintf(cache_name, sizeof(cache_name), "MEMORY:sudocc_%ld",
+ (long) getpid());
+ if ((error = krb5_cc_resolve(sudo_context, cache_name,
+ &(sudo_krb5_data.ccache)))) {
+ log_warningx(0, N_("%s: unable to resolve credential cache: %s"),
+ auth->name, error_message(error));
+ goto done;
+ }
+
+done:
+ if (sudo_krb5_instance != NULL)
+ free(pname);
+ debug_return_int(error ? AUTH_FAILURE : AUTH_SUCCESS);
+}
+
+#ifdef HAVE_KRB5_VERIFY_USER
+int
+sudo_krb5_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ krb5_context sudo_context;
+ krb5_principal princ;
+ krb5_ccache ccache;
+ krb5_error_code error;
+ debug_decl(sudo_krb5_verify, SUDOERS_DEBUG_AUTH);
+
+ sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
+ princ = ((sudo_krb5_datap) auth->data)->princ;
+ ccache = ((sudo_krb5_datap) auth->data)->ccache;
+
+ error = krb5_verify_user(sudo_context, princ, ccache, pass, 1, NULL);
+ debug_return_int(error ? AUTH_FAILURE : AUTH_SUCCESS);
+}
+#else
+int
+sudo_krb5_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ krb5_context sudo_context;
+ krb5_principal princ;
+ krb5_creds credbuf, *creds = NULL;
+ krb5_ccache ccache;
+ krb5_error_code error;
+ krb5_get_init_creds_opt *opts = NULL;
+ debug_decl(sudo_krb5_verify, SUDOERS_DEBUG_AUTH);
+
+ sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
+ princ = ((sudo_krb5_datap) auth->data)->princ;
+ ccache = ((sudo_krb5_datap) auth->data)->ccache;
+
+ /* Set default flags based on the local config file. */
+ error = krb5_get_init_creds_opt_alloc(sudo_context, &opts);
+ if (error) {
+ log_warningx(0, N_("%s: unable to allocate options: %s"), auth->name,
+ error_message(error));
+ goto done;
+ }
+#ifdef HAVE_HEIMDAL
+ krb5_get_init_creds_opt_set_default_flags(sudo_context, NULL,
+ krb5_principal_get_realm(sudo_context, princ), opts);
+#endif
+
+ /* Note that we always obtain a new TGT to verify the user */
+ if ((error = krb5_get_init_creds_password(sudo_context, &credbuf, princ,
+ pass, krb5_prompter_posix,
+ NULL, 0, NULL, opts))) {
+ /* Don't print error if just a bad password */
+ if (error != KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ log_warningx(0, N_("%s: unable to get credentials: %s"),
+ auth->name, error_message(error));
+ }
+ goto done;
+ }
+ creds = &credbuf;
+
+ /* Verify the TGT to prevent spoof attacks. */
+ if ((error = verify_krb_v5_tgt(sudo_context, creds, auth->name)))
+ goto done;
+
+ /* Store credential in cache. */
+ if ((error = krb5_cc_initialize(sudo_context, ccache, princ))) {
+ log_warningx(0, N_("%s: unable to initialize credential cache: %s"),
+ auth->name, error_message(error));
+ } else if ((error = krb5_cc_store_cred(sudo_context, ccache, creds))) {
+ log_warningx(0, N_("%s: unable to store credential in cache: %s"),
+ auth->name, error_message(error));
+ }
+
+done:
+ if (opts) {
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_TWO_ARGS
+ krb5_get_init_creds_opt_free(sudo_context, opts);
+#else
+ krb5_get_init_creds_opt_free(opts);
+#endif
+ }
+ if (creds)
+ krb5_free_cred_contents(sudo_context, creds);
+ debug_return_int(error ? AUTH_FAILURE : AUTH_SUCCESS);
+}
+#endif
+
+int
+sudo_krb5_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ krb5_context sudo_context;
+ krb5_principal princ;
+ krb5_ccache ccache;
+ debug_decl(sudo_krb5_cleanup, SUDOERS_DEBUG_AUTH);
+
+ sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
+ princ = ((sudo_krb5_datap) auth->data)->princ;
+ ccache = ((sudo_krb5_datap) auth->data)->ccache;
+
+ if (sudo_context) {
+ if (ccache)
+ krb5_cc_destroy(sudo_context, ccache);
+ if (princ)
+ krb5_free_principal(sudo_context, princ);
+ krb5_free_context(sudo_context);
+ }
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+#ifndef HAVE_KRB5_VERIFY_USER
+/*
+ * Verify the Kerberos ticket-granting ticket just retrieved for the
+ * user. If the Kerberos server doesn't respond, assume the user is
+ * trying to fake us out (since we DID just get a TGT from what is
+ * supposedly our KDC).
+ *
+ * Returns 0 for successful authentication, non-zero for failure.
+ */
+static int
+verify_krb_v5_tgt(krb5_context sudo_context, krb5_creds *cred, char *auth_name)
+{
+ krb5_error_code error;
+ krb5_principal server;
+ krb5_verify_init_creds_opt vopt;
+ debug_decl(verify_krb_v5_tgt, SUDOERS_DEBUG_AUTH);
+
+ /*
+ * Get the server principal for the local host.
+ * (Use defaults of "host" and canonicalized local name.)
+ */
+ if ((error = krb5_sname_to_principal(sudo_context, NULL, NULL,
+ KRB5_NT_SRV_HST, &server))) {
+ log_warningx(0, N_("%s: unable to get host principal: %s"), auth_name,
+ error_message(error));
+ debug_return_int(-1);
+ }
+
+ /* Initialize verify opts and set secure mode */
+ krb5_verify_init_creds_opt_init(&vopt);
+ krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, 1);
+
+ /* verify the Kerberos ticket-granting ticket we just retrieved */
+ error = krb5_verify_init_creds(sudo_context, cred, server, NULL,
+ NULL, &vopt);
+ krb5_free_principal(sudo_context, server);
+ if (error) {
+ log_warningx(0, N_("%s: Cannot verify TGT! Possible attack!: %s"),
+ auth_name, error_message(error));
+ }
+ debug_return_int(error);
+}
+#endif
+
+#endif /* HAVE_KERB5 */
diff --git a/plugins/sudoers/auth/pam.c b/plugins/sudoers/auth/pam.c
new file mode 100644
index 0000000..6310364
--- /dev/null
+++ b/plugins/sudoers/auth/pam.c
@@ -0,0 +1,765 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_PAM
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+
+#ifdef HAVE_PAM_PAM_APPL_H
+# include <pam/pam_appl.h>
+#else
+# include <security/pam_appl.h>
+#endif
+
+#ifdef __hpux
+# include <nl_types.h>
+#endif
+
+#ifdef HAVE_LIBINTL_H
+# if defined(__LINUX_PAM__)
+# define PAM_TEXT_DOMAIN "Linux-PAM"
+# elif defined(__sun__)
+# define PAM_TEXT_DOMAIN "SUNW_OST_SYSOSPAM"
+# endif
+#endif
+
+/* We don't want to translate the strings in the calls to dgt(). */
+#ifdef PAM_TEXT_DOMAIN
+# define dgt(d, t) dgettext(d, t)
+#endif
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+/* Only OpenPAM and Linux PAM use const qualifiers. */
+#ifdef PAM_SUN_CODEBASE
+# define PAM_CONST
+#else
+# define PAM_CONST const
+#endif
+
+/* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
+#ifdef PAM_SUN_CODEBASE
+# define PAM_MSG_GET(msg, n) (*(msg) + (n))
+#else
+# define PAM_MSG_GET(msg, n) ((msg)[(n)])
+#endif
+
+#ifndef PAM_DATA_SILENT
+#define PAM_DATA_SILENT 0
+#endif
+
+struct conv_filter {
+ char *msg;
+ size_t msglen;
+};
+
+static int converse(int, PAM_CONST struct pam_message **,
+ struct pam_response **, void *);
+static struct sudo_conv_callback *conv_callback;
+static struct pam_conv pam_conv = { converse, &conv_callback };
+static char *def_prompt = PASSPROMPT;
+static bool getpass_error;
+static pam_handle_t *pamh;
+static struct conv_filter *conv_filter;
+
+static void
+conv_filter_init(void)
+{
+ debug_decl(conv_filter_init, SUDOERS_DEBUG_AUTH);
+
+#ifdef __hpux
+ /*
+ * HP-UX displays last login information as part of either account
+ * management (in trusted mode) or session management (regular mode).
+ * Filter those out in the conversation function unless running a shell.
+ */
+ if (!ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
+ int i, nfilt = 0, maxfilters = 0;
+ struct conv_filter *newfilt;
+ nl_catd catd;
+ char *msg;
+
+ /*
+ * Messages from PAM account management when trusted mode is enabled:
+ * 1 Last successful login for %s: %s
+ * 2 Last successful login for %s: %s on %s
+ * 3 Last unsuccessful login for %s: %s
+ * 4 Last unsuccessful login for %s: %s on %s
+ */
+ if ((catd = catopen("pam_comsec", NL_CAT_LOCALE)) != -1) {
+ maxfilters += 4;
+ newfilt = reallocarray(conv_filter, maxfilters + 1,
+ sizeof(*conv_filter));
+ if (newfilt != NULL) {
+ conv_filter = newfilt;
+ for (i = 1; i < 5; i++) {
+ if ((msg = catgets(catd, 1, i, NULL)) == NULL)
+ break;
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "adding \"%s\" to conversation filter", msg);
+ if ((conv_filter[nfilt].msg = strdup(msg)) == NULL)
+ break;
+ conv_filter[nfilt].msglen = strcspn(msg, "%");
+ nfilt++;
+ }
+ }
+ }
+ /*
+ * Messages from PAM session management when trusted mode is disabled:
+ * 3 Last successful login: %s %s %s %s
+ * 4 Last authentication failure: %s %s %s %s
+ */
+ if ((catd = catopen("pam_hpsec", NL_CAT_LOCALE)) != -1) {
+ maxfilters += 2;
+ newfilt = reallocarray(conv_filter, maxfilters + 1,
+ sizeof(*conv_filter));
+ if (newfilt != NULL) {
+ conv_filter = newfilt;
+ for (i = 3; i < 5; i++) {
+ if ((msg = catgets(catd, 1, i, NULL)) == NULL)
+ break;
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "adding \"%s\" to conversation filter", msg);
+ if ((conv_filter[nfilt].msg = strdup(msg)) == NULL)
+ break;
+ conv_filter[nfilt].msglen = strcspn(msg, "%");
+ nfilt++;
+ }
+ }
+ }
+ if (conv_filter != NULL) {
+ conv_filter[nfilt].msg = NULL;
+ conv_filter[nfilt].msglen = 0;
+ }
+ }
+#endif /* __hpux */
+ debug_return;
+}
+
+/*
+ * Like pam_strerror() but never returns NULL and uses strerror(errno)
+ * for PAM_SYSTEM_ERR.
+ */
+static const char *
+sudo_pam_strerror(pam_handle_t *handle, int errnum)
+{
+ const char *errstr;
+ static char errbuf[32];
+
+ if (errnum == PAM_SYSTEM_ERR)
+ return strerror(errno);
+ if ((errstr = pam_strerror(handle, errnum)) == NULL)
+ (void)snprintf(errbuf, sizeof(errbuf), "PAM error %d", errnum);
+ return errstr;
+}
+
+static int
+sudo_pam_init2(struct passwd *pw, sudo_auth *auth, bool quiet)
+{
+ static int pam_status = PAM_SUCCESS;
+ const char *ttypath = user_ttypath;
+ const char *errstr, *pam_service;
+ int rc;
+ debug_decl(sudo_pam_init, SUDOERS_DEBUG_AUTH);
+
+ /* Stash pointer to last pam status. */
+ auth->data = &pam_status;
+
+#ifdef _AIX
+ if (pamh != NULL) {
+ /* Already initialized (may happen with AIX). */
+ debug_return_int(AUTH_SUCCESS);
+ }
+#endif /* _AIX */
+
+ /* Initial PAM. */
+ pam_service = ISSET(sudo_mode, MODE_LOGIN_SHELL) ?
+ def_pam_login_service : def_pam_service;
+ pam_status = pam_start(pam_service, pw->pw_name, &pam_conv, &pamh);
+ if (pam_status != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(NULL, pam_status);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_start(%s, %s, %p, %p): %s", pam_service, pw->pw_name,
+ &pam_conv, &pamh, errstr);
+ if (!quiet)
+ log_warningx(0, N_("unable to initialize PAM: %s"), errstr);
+ debug_return_int(AUTH_FATAL);
+ }
+
+ /* Initialize conversation function message filter. */
+ conv_filter_init();
+
+ /*
+ * Set PAM_RUSER to the invoking user (the "from" user).
+ * Solaris 7 and below require PAM_RHOST to be set if PAM_RUSER is.
+ * Note: PAM_RHOST may cause a DNS lookup on Linux in libaudit.
+ */
+ if (def_pam_ruser) {
+ rc = pam_set_item(pamh, PAM_RUSER, user_name);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_set_item(pamh, PAM_RUSER, %s): %s", user_name, errstr);
+ }
+ }
+ if (def_pam_rhost) {
+ rc = pam_set_item(pamh, PAM_RHOST, user_host);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_set_item(pamh, PAM_RHOST, %s): %s", user_host, errstr);
+ }
+ }
+
+#if defined(__LINUX_PAM__) || defined(__sun__)
+ /*
+ * Some PAM modules assume PAM_TTY is set and will misbehave (or crash)
+ * if it is not. Known offenders include pam_lastlog and pam_time.
+ */
+ if (ttypath == NULL)
+ ttypath = "";
+#endif
+ if (ttypath != NULL) { // -V547
+ rc = pam_set_item(pamh, PAM_TTY, ttypath);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_set_item(pamh, PAM_TTY, %s): %s", ttypath, errstr);
+ }
+ }
+
+ /*
+ * If PAM session and setcred support is disabled we don't
+ * need to keep a sudo process around to close the session.
+ */
+ if (!def_pam_session && !def_pam_setcred)
+ auth->end_session = NULL;
+
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_pam_init(struct passwd *pw, sudo_auth *auth)
+{
+ return sudo_pam_init2(pw, auth, false);
+}
+
+#ifdef _AIX
+int
+sudo_pam_init_quiet(struct passwd *pw, sudo_auth *auth)
+{
+ return sudo_pam_init2(pw, auth, true);
+}
+#endif /* _AIX */
+
+int
+sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ const char *envccname;
+ const char *s;
+ int *pam_status = (int *) auth->data;
+ debug_decl(sudo_pam_verify, SUDOERS_DEBUG_AUTH);
+
+ def_prompt = prompt; /* for converse */
+ getpass_error = false; /* set by converse if user presses ^C */
+ conv_callback = callback; /* passed to conversation function */
+
+ /* Set KRB5CCNAME from the user environment if not set to propagate this
+ * information to PAM modules that may use it to authentication. */
+ envccname = sudo_getenv("KRB5CCNAME");
+ if (envccname == NULL && user_ccname != NULL) {
+ if (sudo_setenv("KRB5CCNAME", user_ccname, true) != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to set KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+
+ /* PAM_SILENT prevents the authentication service from generating output. */
+ *pam_status = pam_authenticate(pamh, PAM_SILENT);
+
+ /* Restore KRB5CCNAME to its original value. */
+ if (envccname == NULL && sudo_unsetenv("KRB5CCNAME") != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to restore KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+
+ if (getpass_error) {
+ /* error or ^C from tgetpass() */
+ debug_return_int(AUTH_INTR);
+ }
+ switch (*pam_status) {
+ case PAM_SUCCESS:
+ debug_return_int(AUTH_SUCCESS);
+ case PAM_AUTH_ERR:
+ case PAM_AUTHINFO_UNAVAIL:
+ case PAM_MAXTRIES:
+ case PAM_PERM_DENIED:
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "pam_authenticate: %d", *pam_status);
+ debug_return_int(AUTH_FAILURE);
+ default:
+ s = sudo_pam_strerror(pamh, *pam_status);
+ log_warningx(0, N_("PAM authentication error: %s"), s);
+ debug_return_int(AUTH_FATAL);
+ }
+}
+
+int
+sudo_pam_approval(struct passwd *pw, sudo_auth *auth, bool exempt)
+{
+ const char *s;
+ int rc, status = AUTH_SUCCESS;
+ int *pam_status = (int *) auth->data;
+ debug_decl(sudo_pam_approval, SUDOERS_DEBUG_AUTH);
+
+ if (def_pam_acct_mgmt) {
+ rc = pam_acct_mgmt(pamh, PAM_SILENT);
+ switch (rc) {
+ case PAM_SUCCESS:
+ break;
+ case PAM_AUTH_ERR:
+ log_warningx(0, N_("account validation failure, "
+ "is your account locked?"));
+ status = AUTH_FATAL;
+ break;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* Ignore if user is exempt from password restrictions. */
+ if (exempt) {
+ rc = *pam_status;
+ break;
+ }
+ /* New password required, try to change it. */
+ log_warningx(0, N_("Account or password is "
+ "expired, reset your password and try again"));
+ rc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+ if (rc == PAM_SUCCESS)
+ break;
+ s = pam_strerror(pamh, rc);
+ log_warningx(0, N_("unable to change expired password: %s"), s);
+ status = AUTH_FAILURE;
+ break;
+ case PAM_AUTHTOK_EXPIRED:
+ /* Ignore if user is exempt from password restrictions. */
+ if (exempt) {
+ rc = *pam_status;
+ break;
+ }
+ /* Password expired, cannot be updated by user. */
+ log_warningx(0,
+ N_("Password expired, contact your system administrator"));
+ status = AUTH_FATAL;
+ break;
+ case PAM_ACCT_EXPIRED:
+ log_warningx(0,
+ N_("Account expired or PAM config lacks an \"account\" "
+ "section for sudo, contact your system administrator"));
+ status = AUTH_FATAL;
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ case PAM_MAXTRIES:
+ case PAM_PERM_DENIED:
+ s = sudo_pam_strerror(pamh, rc);
+ log_warningx(0, N_("PAM account management error: %s"), s);
+ status = AUTH_FAILURE;
+ break;
+ default:
+ s = sudo_pam_strerror(pamh, rc);
+ log_warningx(0, N_("PAM account management error: %s"), s);
+ status = AUTH_FATAL;
+ break;
+ }
+ *pam_status = rc;
+ }
+ debug_return_int(status);
+}
+
+int
+sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ int *pam_status = (int *) auth->data;
+ debug_decl(sudo_pam_cleanup, SUDOERS_DEBUG_AUTH);
+
+ /* If successful, we can't close the session until sudo_pam_end_session() */
+ if (force || *pam_status != PAM_SUCCESS || auth->end_session == NULL) {
+ *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
+ pamh = NULL;
+ }
+ debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
+}
+
+int
+sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth)
+{
+ int rc, status = AUTH_SUCCESS;
+ int *pam_status = (int *) auth->data;
+ const char *errstr;
+ debug_decl(sudo_pam_begin_session, SUDOERS_DEBUG_AUTH);
+
+ /*
+ * If there is no valid user we cannot open a PAM session.
+ * This is not an error as sudo can run commands with arbitrary
+ * uids, it just means we are done from a session management standpoint.
+ */
+ if (pw == NULL) {
+ if (pamh != NULL) {
+ rc = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_end: %s", errstr);
+ }
+ pamh = NULL;
+ }
+ goto done;
+ }
+
+ /*
+ * Update PAM_USER to reference the user we are running the command
+ * as, as opposed to the user we authenticated as.
+ */
+ rc = pam_set_item(pamh, PAM_USER, pw->pw_name);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_set_item(pamh, PAM_USER, %s): %s", pw->pw_name, errstr);
+ }
+
+ /*
+ * Reinitialize credentials when changing the user.
+ * We don't worry about a failure from pam_setcred() since with
+ * stacked PAM auth modules a failure from one module may override
+ * PAM_SUCCESS from another. For example, given a non-local user,
+ * pam_unix will fail but pam_ldap or pam_sss may succeed, but if
+ * pam_unix is first in the stack, pam_setcred() will fail.
+ */
+ if (def_pam_setcred) {
+ rc = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_setcred: %s", errstr);
+ def_pam_setcred = false;
+ }
+ }
+
+ if (def_pam_session) {
+ /*
+ * We use PAM_SILENT to prevent pam_lastlog from printing last login
+ * information except when explicitly running a shell.
+ */
+ const bool silent = !ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL);
+ rc = pam_open_session(pamh, silent ? PAM_SILENT : 0);
+ switch (rc) {
+ case PAM_SUCCESS:
+ break;
+ case PAM_SESSION_ERR:
+ /* Treat PAM_SESSION_ERR as a non-fatal error. */
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_open_session: %s", errstr);
+ /* Avoid closing session that was not opened. */
+ def_pam_session = false;
+ break;
+ default:
+ /* Unexpected session failure, treat as fatal error. */
+ *pam_status = rc;
+ errstr = sudo_pam_strerror(pamh, rc);
+ log_warningx(0, N_("%s: %s"), "pam_open_session", errstr);
+ rc = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_end: %s", errstr);
+ }
+ pamh = NULL;
+ status = AUTH_FATAL;
+ goto done;
+ }
+ }
+
+#ifdef HAVE_PAM_GETENVLIST
+ /*
+ * Update environment based on what is stored in pamh.
+ * If no authentication is done we will only have environment
+ * variables if pam_env is called via session.
+ */
+ if (user_envp != NULL) {
+ char **pam_envp = pam_getenvlist(pamh);
+ if (pam_envp != NULL) {
+ /* Merge pam env with user env. */
+ if (!env_init(*user_envp) || !env_merge(pam_envp))
+ status = AUTH_FATAL;
+ *user_envp = env_get();
+ (void)env_init(NULL);
+ free(pam_envp);
+ /* XXX - we leak any duplicates that were in pam_envp */
+ }
+ }
+#endif /* HAVE_PAM_GETENVLIST */
+
+done:
+ debug_return_int(status);
+}
+
+int
+sudo_pam_end_session(struct passwd *pw, sudo_auth *auth)
+{
+ int rc, status = AUTH_SUCCESS;
+ const char *errstr;
+ debug_decl(sudo_pam_end_session, SUDOERS_DEBUG_AUTH);
+
+ if (pamh != NULL) {
+ /*
+ * Update PAM_USER to reference the user we are running the command
+ * as, as opposed to the user we authenticated as.
+ * XXX - still needed now that session init is in parent?
+ */
+ rc = pam_set_item(pamh, PAM_USER, pw->pw_name);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_set_item(pamh, PAM_USER, %s): %s", pw->pw_name, errstr);
+ }
+ if (def_pam_session) {
+ rc = pam_close_session(pamh, PAM_SILENT);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_close_session: %s", errstr);
+ }
+ }
+ if (def_pam_setcred) {
+ rc = pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_setcred: %s", errstr);
+ }
+ }
+ rc = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+ if (rc != PAM_SUCCESS) {
+ errstr = sudo_pam_strerror(pamh, rc);
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "pam_end: %s", errstr);
+ status = AUTH_FATAL;
+ }
+ pamh = NULL;
+ }
+
+ debug_return_int(status);
+}
+
+#define PROMPT_IS_PASSWORD(_p) \
+ (strncmp((_p), "Password:", 9) == 0 && \
+ ((_p)[9] == '\0' || ((_p)[9] == ' ' && (_p)[10] == '\0')))
+
+#ifdef PAM_TEXT_DOMAIN
+# define PAM_PROMPT_IS_PASSWORD(_p) \
+ (strcmp((_p), dgt(PAM_TEXT_DOMAIN, "Password:")) == 0 || \
+ strcmp((_p), dgt(PAM_TEXT_DOMAIN, "Password: ")) == 0 || \
+ PROMPT_IS_PASSWORD(_p))
+#else
+# define PAM_PROMPT_IS_PASSWORD(_p) PROMPT_IS_PASSWORD(_p)
+#endif /* PAM_TEXT_DOMAIN */
+
+/*
+ * We use the PAM prompt in preference to sudo's as long
+ * as passprompt_override is not set and:
+ * a) the (translated) sudo prompt matches /^Password: ?/
+ * or:
+ * b) the PAM prompt itself *doesn't* match /^Password: ?/
+ * or /^username's Password: ?/
+ *
+ * The intent is to use the PAM prompt for things like
+ * challenge-response, otherwise use sudo's prompt.
+ * There may also be cases where a localized translation
+ * of "Password: " exists for PAM but not for sudo.
+ */
+static bool
+use_pam_prompt(const char *pam_prompt)
+{
+ size_t user_len;
+ debug_decl(use_pam_prompt, SUDOERS_DEBUG_AUTH);
+
+ /* Always use sudo prompt if passprompt_override is set. */
+ if (def_passprompt_override)
+ debug_return_bool(false);
+
+ /* If sudo prompt matches "^Password: ?$", use PAM prompt. */
+ if (PROMPT_IS_PASSWORD(def_prompt))
+ debug_return_bool(true);
+
+ /* If PAM prompt matches "^Password: ?$", use sudo prompt. */
+ if (PAM_PROMPT_IS_PASSWORD(pam_prompt))
+ debug_return_bool(false);
+
+ /*
+ * Some PAM modules use "^username's Password: ?$" instead of
+ * "^Password: ?" so check for that too.
+ */
+ user_len = strlen(user_name);
+ if (strncmp(pam_prompt, user_name, user_len) == 0) {
+ const char *cp = pam_prompt + user_len;
+ if (strncmp(cp, "'s Password:", 12) == 0 &&
+ (cp[12] == '\0' || (cp[12] == ' ' && cp[13] == '\0')))
+ debug_return_bool(false);
+ }
+
+ /* Otherwise, use the PAM prompt. */
+ debug_return_bool(true);
+}
+
+static bool
+is_filtered(const char *msg)
+{
+ bool filtered = false;
+
+ if (conv_filter != NULL) {
+ struct conv_filter *filt = conv_filter;
+ while (filt->msg != NULL) {
+ if (strncmp(msg, filt->msg, filt->msglen) == 0) {
+ filtered = true;
+ break;
+ }
+ filt++;
+ }
+ }
+ return filtered;
+}
+
+/*
+ * ``Conversation function'' for PAM <-> human interaction.
+ */
+static int
+converse(int num_msg, PAM_CONST struct pam_message **msg,
+ struct pam_response **reply_out, void *vcallback)
+{
+ struct sudo_conv_callback *callback = NULL;
+ struct pam_response *reply;
+ const char *prompt;
+ char *pass;
+ int n, type;
+ int ret = PAM_SUCCESS;
+ debug_decl(converse, SUDOERS_DEBUG_AUTH);
+
+ if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "invalid number of PAM messages: %d", num_msg);
+ debug_return_int(PAM_CONV_ERR);
+ }
+ sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
+ "number of PAM messages: %d", num_msg);
+
+ if ((reply = calloc(num_msg, sizeof(struct pam_response))) == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_int(PAM_BUF_ERR);
+ }
+ *reply_out = reply;
+
+ if (vcallback != NULL)
+ callback = *((struct sudo_conv_callback **)vcallback);
+
+ for (n = 0; n < num_msg; n++) {
+ PAM_CONST struct pam_message *pm = PAM_MSG_GET(msg, n);
+
+ type = SUDO_CONV_PROMPT_ECHO_OFF;
+ switch (pm->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ type = SUDO_CONV_PROMPT_ECHO_ON;
+ FALLTHROUGH;
+ case PAM_PROMPT_ECHO_OFF:
+ /* Error out if the last password read was interrupted. */
+ if (getpass_error)
+ goto done;
+
+ /* Choose either the sudo prompt or the PAM one. */
+ prompt = use_pam_prompt(pm->msg) ? pm->msg : def_prompt;
+
+ /* Read the password unless interrupted. */
+ pass = auth_getpass(prompt, type, callback);
+ if (pass == NULL) {
+ /* Error (or ^C) reading password, don't try again. */
+ getpass_error = true;
+ ret = PAM_CONV_ERR;
+ goto done;
+ }
+ if (strlen(pass) >= PAM_MAX_RESP_SIZE) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "password longer than %d", PAM_MAX_RESP_SIZE);
+ ret = PAM_CONV_ERR;
+ explicit_bzero(pass, strlen(pass));
+ goto done;
+ }
+ reply[n].resp = pass; /* auth_getpass() malloc's a copy */
+ break;
+ case PAM_TEXT_INFO:
+ if (pm->msg != NULL && !is_filtered(pm->msg))
+ sudo_printf(SUDO_CONV_INFO_MSG|SUDO_CONV_PREFER_TTY,
+ "%s\n", pm->msg);
+ break;
+ case PAM_ERROR_MSG:
+ if (pm->msg != NULL)
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "%s\n", pm->msg);
+ break;
+ default:
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "unsupported message style: %d", pm->msg_style);
+ ret = PAM_CONV_ERR;
+ goto done;
+ }
+ }
+
+done:
+ if (ret != PAM_SUCCESS) {
+ /* Zero and free allocated memory and return an error. */
+ for (n = 0; n < num_msg; n++) {
+ struct pam_response *pr = &reply[n];
+
+ if (pr->resp != NULL) {
+ freezero(pr->resp, strlen(pr->resp));
+ pr->resp = NULL;
+ }
+ }
+ free(reply);
+ *reply_out = NULL;
+ }
+ debug_return_int(ret);
+}
+
+#endif /* HAVE_PAM */
diff --git a/plugins/sudoers/auth/passwd.c b/plugins/sudoers/auth/passwd.c
new file mode 100644
index 0000000..566881f
--- /dev/null
+++ b/plugins/sudoers/auth/passwd.c
@@ -0,0 +1,122 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2010-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+#define DESLEN 13
+#define HAS_AGEINFO(p, l) (l == 18 && p[DESLEN] == ',')
+
+int
+sudo_passwd_init(struct passwd *pw, sudo_auth *auth)
+{
+ debug_decl(sudo_passwd_init, SUDOERS_DEBUG_AUTH);
+
+#ifdef HAVE_SKEYACCESS
+ if (skeyaccess(pw, user_tty, NULL, NULL) == 0)
+ debug_return_int(AUTH_FAILURE);
+#endif
+ sudo_setspent();
+ auth->data = sudo_getepw(pw);
+ sudo_endspent();
+ debug_return_int(auth->data ? AUTH_SUCCESS : AUTH_FATAL);
+}
+
+#ifdef HAVE_CRYPT
+int
+sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char sav, *epass;
+ char *pw_epasswd = auth->data;
+ size_t pw_len;
+ int matched = 0;
+ debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH);
+
+ /* An empty plain-text password must match an empty encrypted password. */
+ if (pass[0] == '\0')
+ debug_return_int(pw_epasswd[0] ? AUTH_FAILURE : AUTH_SUCCESS);
+
+ /*
+ * Truncate to 8 chars if standard DES since not all crypt()'s do this.
+ * If this turns out not to be safe we will have to use OS #ifdef's (sigh).
+ */
+ sav = pass[8];
+ pw_len = strlen(pw_epasswd);
+ if (pw_len == DESLEN || HAS_AGEINFO(pw_epasswd, pw_len))
+ pass[8] = '\0';
+
+ /*
+ * Normal UN*X password check.
+ * HP-UX may add aging info (separated by a ',') at the end so
+ * only compare the first DESLEN characters in that case.
+ */
+ epass = (char *) crypt(pass, pw_epasswd);
+ pass[8] = sav;
+ if (epass != NULL) {
+ if (HAS_AGEINFO(pw_epasswd, pw_len) && strlen(epass) == DESLEN)
+ matched = !strncmp(pw_epasswd, epass, DESLEN);
+ else
+ matched = !strcmp(pw_epasswd, epass);
+ }
+
+ debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE);
+}
+#else
+int
+sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char *pw_passwd = auth->data;
+ int matched;
+ debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH);
+
+ /* Simple string compare for systems without crypt(). */
+ matched = !strcmp(pass, pw_passwd);
+
+ debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE);
+}
+#endif
+
+int
+sudo_passwd_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ char *pw_epasswd = auth->data;
+ debug_decl(sudo_passwd_cleanup, SUDOERS_DEBUG_AUTH);
+
+ if (pw_epasswd != NULL)
+ freezero(pw_epasswd, strlen(pw_epasswd));
+
+ debug_return_int(AUTH_SUCCESS);
+}
diff --git a/plugins/sudoers/auth/rfc1938.c b/plugins/sudoers/auth/rfc1938.c
new file mode 100644
index 0000000..2e4824c
--- /dev/null
+++ b/plugins/sudoers/auth/rfc1938.c
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1994-1996, 1998-2005, 2010-2012, 2014-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#if defined(HAVE_SKEY) || defined(HAVE_OPIE)
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#if defined(HAVE_SKEY)
+# include <skey.h>
+# define RFC1938 skey
+# ifdef HAVE_RFC1938_SKEYCHALLENGE
+# define rfc1938challenge(a,b,c,d) skeychallenge((a),(b),(c),(d))
+# else
+# define rfc1938challenge(a,b,c,d) skeychallenge((a),(b),(c))
+# endif
+# define rfc1938verify(a,b) skeyverify((a),(b))
+#elif defined(HAVE_OPIE)
+# include <opie.h>
+# define RFC1938 opie
+# define rfc1938challenge(a,b,c,d) opiechallenge((a),(b),(c))
+# define rfc1938verify(a,b) opieverify((a),(b))
+#endif
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+int
+sudo_rfc1938_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
+{
+ char challenge[256];
+ size_t challenge_len;
+ static char *orig_prompt = NULL, *new_prompt = NULL;
+ static size_t op_len, np_size;
+ static struct RFC1938 rfc1938;
+ debug_decl(sudo_rfc1938_setup, SUDOERS_DEBUG_AUTH);
+
+ /* Stash a pointer to the rfc1938 struct if we have not initialized */
+ if (!auth->data)
+ auth->data = &rfc1938;
+
+ /* Save the original prompt */
+ if (orig_prompt == NULL) {
+ orig_prompt = *promptp;
+ op_len = strlen(orig_prompt);
+
+ /* Ignore trailing colon (we will add our own) */
+ if (orig_prompt[op_len - 1] == ':')
+ op_len--;
+ else if (op_len >= 2 && orig_prompt[op_len - 1] == ' '
+ && orig_prompt[op_len - 2] == ':')
+ op_len -= 2;
+ }
+
+#ifdef HAVE_SKEY
+ /* Close old stream */
+ if (rfc1938.keyfile)
+ (void) fclose(rfc1938.keyfile);
+#endif
+
+ /*
+ * Look up the user and get the rfc1938 challenge.
+ * If the user is not in the OTP db, only post a fatal error if
+ * we are running alone (since they may just use a normal passwd).
+ */
+ if (rfc1938challenge(&rfc1938, pw->pw_name, challenge, sizeof(challenge))) {
+ if (IS_ONEANDONLY(auth)) {
+ sudo_warnx(U_("you do not exist in the %s database"), auth->name);
+ debug_return_int(AUTH_FATAL);
+ } else {
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+
+ /* Get space for new prompt with embedded challenge */
+ challenge_len = strlen(challenge);
+ if (np_size < op_len + challenge_len + 7) {
+ char *p = realloc(new_prompt, op_len + challenge_len + 7);
+ if (p == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_int(AUTH_FATAL);
+ }
+ np_size = op_len + challenge_len + 7;
+ new_prompt = p;
+ }
+
+ if (def_long_otp_prompt)
+ (void) snprintf(new_prompt, np_size, "%s\n%s", challenge, orig_prompt);
+ else
+ (void) snprintf(new_prompt, np_size, "%.*s [ %s ]:", (int)op_len,
+ orig_prompt, challenge);
+
+ *promptp = new_prompt;
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_rfc1938_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ debug_decl(sudo_rfc1938_verify, SUDOERS_DEBUG_AUTH);
+
+ if (rfc1938verify((struct RFC1938 *) auth->data, pass) == 0)
+ debug_return_int(AUTH_SUCCESS);
+ else
+ debug_return_int(AUTH_FAILURE);
+}
+
+#endif /* HAVE_SKEY || HAVE_OPIE */
diff --git a/plugins/sudoers/auth/secureware.c b/plugins/sudoers/auth/secureware.c
new file mode 100644
index 0000000..dd7aa42
--- /dev/null
+++ b/plugins/sudoers/auth/secureware.c
@@ -0,0 +1,109 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1998-2005, 2010-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_GETPRPWNAM
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#ifdef __hpux
+# undef MAXINT
+# include <hpsecurity.h>
+#else
+# include <sys/security.h>
+#endif /* __hpux */
+#include <prot.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+#ifdef __alpha
+extern int crypt_type;
+#endif
+
+int
+sudo_secureware_init(struct passwd *pw, sudo_auth *auth)
+{
+ debug_decl(sudo_secureware_init, SUDOERS_DEBUG_AUTH);
+
+#ifdef __alpha
+ if (crypt_type == INT_MAX)
+ debug_return_int(AUTH_FAILURE); /* no shadow */
+#endif
+
+ sudo_setspent();
+ auth->data = sudo_getepw(pw);
+ sudo_endspent();
+ debug_return_int(auth->data ? AUTH_SUCCESS : AUTH_FATAL);
+}
+
+int
+sudo_secureware_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ char *pw_epasswd = auth->data;
+ char *epass = NULL;
+ debug_decl(sudo_secureware_verify, SUDOERS_DEBUG_AUTH);
+
+ /* An empty plain-text password must match an empty encrypted password. */
+ if (pass[0] == '\0')
+ debug_return_int(pw_epasswd[0] ? AUTH_FAILURE : AUTH_SUCCESS);
+
+#if defined(__alpha)
+# ifdef HAVE_DISPCRYPT
+ epass = dispcrypt(pass, pw_epasswd, crypt_type);
+# else
+ if (crypt_type == AUTH_CRYPT_BIGCRYPT)
+ epass = bigcrypt(pass, pw_epasswd);
+ else if (crypt_type == AUTH_CRYPT_CRYPT16)
+ epass = crypt(pass, pw_epasswd);
+# endif /* HAVE_DISPCRYPT */
+#elif defined(HAVE_BIGCRYPT)
+ epass = bigcrypt(pass, pw_epasswd);
+#endif /* __alpha */
+
+ if (epass != NULL && strcmp(pw_epasswd, epass) == 0)
+ debug_return_int(AUTH_SUCCESS);
+ debug_return_int(AUTH_FAILURE);
+}
+
+int
+sudo_secureware_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ char *pw_epasswd = auth->data;
+ debug_decl(sudo_secureware_cleanup, SUDOERS_DEBUG_AUTH);
+
+ if (pw_epasswd != NULL)
+ freezero(pw_epasswd, strlen(pw_epasswd));
+ debug_return_int(AUTH_SUCCESS);
+}
+
+#endif /* HAVE_GETPRPWNAM */
diff --git a/plugins/sudoers/auth/securid5.c b/plugins/sudoers/auth/securid5.c
new file mode 100644
index 0000000..698953f
--- /dev/null
+++ b/plugins/sudoers/auth/securid5.c
@@ -0,0 +1,225 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007, 2010-2012, 2014-2016
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2002 Michael Stroucken <michael@stroucken.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_SECURID
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+/* Needed for SecurID v5.0 Authentication on UNIX */
+#define UNIX 1
+#include <acexport.h>
+#include <sdacmvls.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+/*
+ * securid_init - Initialises communications with ACE server
+ * Arguments in:
+ * pw - UNUSED
+ * auth - sudo authentication structure
+ *
+ * Results out:
+ * auth - auth->data contains pointer to new SecurID handle
+ * return code - Fatal if initialization unsuccessful, otherwise
+ * success.
+ */
+int
+sudo_securid_init(struct passwd *pw, sudo_auth *auth)
+{
+ static SDI_HANDLE sd_dat; /* SecurID handle */
+ debug_decl(sudo_securid_init, SUDOERS_DEBUG_AUTH);
+
+ auth->data = (void *) &sd_dat; /* For method-specific data */
+
+ /* Start communications */
+ if (AceInitialize() != SD_FALSE)
+ debug_return_int(AUTH_SUCCESS);
+
+ sudo_warnx("%s", U_("failed to initialise the ACE API library"));
+ debug_return_int(AUTH_FATAL);
+}
+
+/*
+ * securid_setup - Initialises a SecurID transaction and locks out other
+ * ACE servers
+ *
+ * Arguments in:
+ * pw - struct passwd for username
+ * promptp - UNUSED
+ * auth - sudo authentication structure for SecurID handle
+ *
+ * Results out:
+ * return code - Success if transaction started correctly, fatal
+ * otherwise
+ */
+int
+sudo_securid_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
+{
+ SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
+ int retval;
+ debug_decl(sudo_securid_setup, SUDOERS_DEBUG_AUTH);
+
+ /* Re-initialize SecurID every time. */
+ if (SD_Init(sd) != ACM_OK) {
+ sudo_warnx("%s", U_("unable to contact the SecurID server"));
+ debug_return_int(AUTH_FATAL);
+ }
+
+ /* Lock new PIN code */
+ retval = SD_Lock(*sd, pw->pw_name);
+
+ switch (retval) {
+ case ACM_OK:
+ sudo_warnx("%s", U_("User ID locked for SecurID Authentication"));
+ debug_return_int(AUTH_SUCCESS);
+
+ case ACE_UNDEFINED_USERNAME:
+ sudo_warnx("%s", U_("invalid username length for SecurID"));
+ debug_return_int(AUTH_FATAL);
+
+ case ACE_ERR_INVALID_HANDLE:
+ sudo_warnx("%s", U_("invalid Authentication Handle for SecurID"));
+ debug_return_int(AUTH_FATAL);
+
+ case ACM_ACCESS_DENIED:
+ sudo_warnx("%s", U_("SecurID communication failed"));
+ debug_return_int(AUTH_FATAL);
+
+ default:
+ sudo_warnx("%s", U_("unknown SecurID error"));
+ debug_return_int(AUTH_FATAL);
+ }
+}
+
+/*
+ * securid_verify - Authenticates user and handles ACE responses
+ *
+ * Arguments in:
+ * pw - struct passwd for username
+ * pass - UNUSED
+ * auth - sudo authentication structure for SecurID handle
+ *
+ * Results out:
+ * return code - Success on successful authentication, failure on
+ * incorrect authentication, fatal on errors
+ */
+int
+sudo_securid_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
+{
+ SDI_HANDLE *sd = (SDI_HANDLE *) auth->data;
+ int ret;
+ debug_decl(sudo_securid_verify, SUDOERS_DEBUG_AUTH);
+
+ pass = auth_getpass("Enter your PASSCODE: ", SUDO_CONV_PROMPT_ECHO_OFF,
+ callback);
+
+ /* Have ACE verify password */
+ switch (SD_Check(*sd, pass, pw->pw_name)) {
+ case ACM_OK:
+ ret = AUTH_SUCESS;
+ break;
+
+ case ACE_UNDEFINED_PASSCODE:
+ sudo_warnx("%s", U_("invalid passcode length for SecurID"));
+ ret = AUTH_FATAL;
+ break;
+
+ case ACE_UNDEFINED_USERNAME:
+ sudo_warnx("%s", U_("invalid username length for SecurID"));
+ ret = AUTH_FATAL;
+ break;
+
+ case ACE_ERR_INVALID_HANDLE:
+ sudo_warnx("%s", U_("invalid Authentication Handle for SecurID"));
+ ret = AUTH_FATAL;
+ break;
+
+ case ACM_ACCESS_DENIED:
+ ret = AUTH_FAILURE;
+ break;
+
+ case ACM_NEXT_CODE_REQUIRED:
+ /* Sometimes (when current token close to expire?)
+ ACE challenges for the next token displayed
+ (entered without the PIN) */
+ if (pass != NULL)
+ freezero(pass, strlen(pass));
+ pass = auth_getpass("\
+!!! ATTENTION !!!\n\
+Wait for the token code to change, \n\
+then enter the new token code.\n", \
+ SUDO_CONV_PROMPT_ECHO_OFF, callback);
+
+ if (SD_Next(*sd, pass) == ACM_OK) {
+ ret = AUTH_SUCCESS;
+ break;
+ }
+
+ ret = AUTH_FAILURE;
+ break;
+
+ case ACM_NEW_PIN_REQUIRED:
+ /*
+ * This user's SecurID has not been activated yet,
+ * or the pin has been reset
+ */
+ /* XXX - Is setting up a new PIN within sudo's scope? */
+ SD_Pin(*sd, "");
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "Your SecurID access has not yet been set up.\n");
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY,
+ "Please set up a PIN before you try to authenticate.\n");
+ ret = AUTH_FATAL;
+ break;
+
+ default:
+ sudo_warnx("%s", U_("unknown SecurID error"));
+ ret = AUTH_FATAL;
+ break;
+ }
+
+ /* Free resources */
+ SD_Close(*sd);
+
+ if (pass != NULL)
+ freezero(pass, strlen(pass));
+
+ /* Return stored state to calling process */
+ debug_return_int(ret);
+}
+
+#endif /* HAVE_SECURID */
diff --git a/plugins/sudoers/auth/sia.c b/plugins/sudoers/auth/sia.c
new file mode 100644
index 0000000..7feb19e
--- /dev/null
+++ b/plugins/sudoers/auth/sia.c
@@ -0,0 +1,152 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007, 2010-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#ifdef HAVE_SIA_SES_INIT
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <signal.h>
+#include <siad.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+
+static char **sudo_argv;
+static int sudo_argc;
+
+int
+sudo_sia_setup(struct passwd *pw, char **promptp, sudo_auth *auth)
+{
+ SIAENTITY *siah;
+ int i;
+ debug_decl(sudo_sia_setup, SUDOERS_DEBUG_AUTH);
+
+ /* Rebuild argv for sia_ses_init() */
+ sudo_argc = NewArgc + 1;
+ sudo_argv = reallocarray(NULL, sudo_argc + 1, sizeof(char *));
+ if (sudo_argv == NULL) {
+ log_warningx(0, N_("unable to allocate memory"));
+ debug_return_int(AUTH_FATAL);
+ }
+ sudo_argv[0] = "sudo";
+ for (i = 0; i < NewArgc; i++)
+ sudo_argv[i + 1] = NewArgv[i];
+ sudo_argv[sudo_argc] = NULL;
+
+ /* We don't let SIA prompt the user for input. */
+ if (sia_ses_init(&siah, sudo_argc, sudo_argv, NULL, pw->pw_name, user_ttypath, 0, NULL) != SIASUCCESS) {
+ log_warning(0, N_("unable to initialize SIA session"));
+ debug_return_int(AUTH_FATAL);
+ }
+
+ auth->data = siah;
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_sia_verify(struct passwd *pw, char *prompt, sudo_auth *auth,
+ struct sudo_conv_callback *callback)
+{
+ SIAENTITY *siah = auth->data;
+ char *pass;
+ int rc;
+ debug_decl(sudo_sia_verify, SUDOERS_DEBUG_AUTH);
+
+ /* Get password, return AUTH_INTR if we got ^C */
+ pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ if (pass == NULL)
+ debug_return_int(AUTH_INTR);
+
+ /* Check password and zero out plaintext copy. */
+ rc = sia_ses_authent(NULL, pass, siah);
+ freezero(pass, strlen(pass));
+
+ if (rc == SIASUCCESS)
+ debug_return_int(AUTH_SUCCESS);
+ if (ISSET(rc, SIASTOP))
+ debug_return_int(AUTH_FATAL);
+ debug_return_int(AUTH_FAILURE);
+}
+
+int
+sudo_sia_cleanup(struct passwd *pw, sudo_auth *auth, bool force)
+{
+ SIAENTITY *siah = auth->data;
+ debug_decl(sudo_sia_cleanup, SUDOERS_DEBUG_AUTH);
+
+ (void) sia_ses_release(&siah);
+ auth->data = NULL;
+ free(sudo_argv);
+ debug_return_int(AUTH_SUCCESS);
+}
+
+int
+sudo_sia_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth)
+{
+ SIAENTITY *siah;
+ int status = AUTH_FATAL;
+ debug_decl(sudo_sia_begin_session, SUDOERS_DEBUG_AUTH);
+
+ /* Re-init sia for the target user's session. */
+ if (sia_ses_init(&siah, NewArgc, NewArgv, NULL, pw->pw_name, user_ttypath, 0, NULL) != SIASUCCESS) {
+ log_warning(0, N_("unable to initialize SIA session"));
+ goto done;
+ }
+
+ if (sia_make_entity_pwd(pw, siah) != SIASUCCESS) {
+ sudo_warn("sia_make_entity_pwd");
+ goto done;
+ }
+
+ status = AUTH_FAILURE; /* no more fatal errors. */
+
+ siah->authtype = SIA_A_NONE;
+ if (sia_ses_estab(sia_collect_trm, siah) != SIASUCCESS) {
+ sudo_warn("sia_ses_estab");
+ goto done;
+ }
+
+ if (sia_ses_launch(sia_collect_trm, siah) != SIASUCCESS) {
+ sudo_warn("sia_ses_launch");
+ goto done;
+ }
+
+ status = AUTH_SUCCESS;
+
+done:
+ (void) sia_ses_release(&siah);
+ debug_return_int(status);
+}
+
+#endif /* HAVE_SIA_SES_INIT */
diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c
new file mode 100644
index 0000000..188b65f
--- /dev/null
+++ b/plugins/sudoers/auth/sudo_auth.c
@@ -0,0 +1,477 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2008-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(HAVE_STDINT_H)
+# include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <time.h>
+#include <signal.h>
+
+#include "sudoers.h"
+#include "sudo_auth.h"
+#include "insults.h"
+
+static sudo_auth auth_switch[] = {
+/* Standalone entries first */
+#ifdef HAVE_AIXAUTH
+ AUTH_ENTRY("aixauth", FLAG_STANDALONE, sudo_aix_init, NULL, sudo_aix_verify, NULL, sudo_aix_cleanup, NULL, NULL)
+#endif
+#ifdef HAVE_PAM
+ AUTH_ENTRY("pam", FLAG_STANDALONE, sudo_pam_init, NULL, sudo_pam_verify, sudo_pam_approval, sudo_pam_cleanup, sudo_pam_begin_session, sudo_pam_end_session)
+#endif
+#ifdef HAVE_SECURID
+ AUTH_ENTRY("SecurId", FLAG_STANDALONE, sudo_securid_init, sudo_securid_setup, sudo_securid_verify, NULL, NULL, NULL, NULL)
+#endif
+#ifdef HAVE_SIA_SES_INIT
+ AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sudo_sia_setup, sudo_sia_verify, NULL, sudo_sia_cleanup, sudo_sia_begin_session, NULL)
+#endif
+#ifdef HAVE_FWTK
+ AUTH_ENTRY("fwtk", FLAG_STANDALONE, sudo_fwtk_init, NULL, sudo_fwtk_verify, NULL, sudo_fwtk_cleanup, NULL, NULL)
+#endif
+#ifdef HAVE_BSD_AUTH_H
+ AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_approval, bsdauth_cleanup, NULL, NULL)
+#endif
+
+/* Non-standalone entries */
+#ifndef WITHOUT_PASSWD
+ AUTH_ENTRY("passwd", 0, sudo_passwd_init, NULL, sudo_passwd_verify, NULL, sudo_passwd_cleanup, NULL, NULL)
+#endif
+#if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
+ AUTH_ENTRY("secureware", 0, sudo_secureware_init, NULL, sudo_secureware_verify, NULL, sudo_secureware_cleanup, NULL, NULL)
+#endif
+#ifdef HAVE_AFS
+ AUTH_ENTRY("afs", 0, NULL, NULL, sudo_afs_verify, NULL, NULL, NULL, NULL)
+#endif
+#ifdef HAVE_DCE
+ AUTH_ENTRY("dce", 0, NULL, NULL, sudo_dce_verify, NULL, NULL, NULL, NULL)
+#endif
+#ifdef HAVE_KERB5
+ AUTH_ENTRY("kerb5", 0, sudo_krb5_init, sudo_krb5_setup, sudo_krb5_verify, NULL, sudo_krb5_cleanup, NULL, NULL)
+#endif
+#ifdef HAVE_SKEY
+ AUTH_ENTRY("S/Key", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
+#endif
+#ifdef HAVE_OPIE
+ AUTH_ENTRY("OPIE", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL)
+#endif
+ AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
+};
+
+static bool standalone;
+
+/*
+ * Initialize sudoers authentication method(s).
+ * Returns 0 on success and -1 on error.
+ */
+int
+sudo_auth_init(struct passwd *pw)
+{
+ sudo_auth *auth;
+ int status = AUTH_SUCCESS;
+ debug_decl(sudo_auth_init, SUDOERS_DEBUG_AUTH);
+
+ if (auth_switch[0].name == NULL)
+ debug_return_int(0);
+
+ /* Initialize auth methods and unconfigure the method if necessary. */
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->init && !IS_DISABLED(auth)) {
+ /* Disable if it failed to init unless there was a fatal error. */
+ status = (auth->init)(pw, auth);
+ if (status == AUTH_FAILURE)
+ SET(auth->flags, FLAG_DISABLED);
+ else if (status == AUTH_FATAL)
+ break; /* assume error msg already printed */
+ }
+ }
+
+ /*
+ * Make sure we haven't mixed standalone and shared auth methods.
+ * If there are multiple standalone methods, only use the first one.
+ */
+ if ((standalone = IS_STANDALONE(&auth_switch[0]))) {
+ bool found = false;
+ for (auth = auth_switch; auth->name; auth++) {
+ if (IS_DISABLED(auth))
+ continue;
+ if (!IS_STANDALONE(auth)) {
+ audit_failure(NewArgv, N_("invalid authentication methods"));
+ log_warningx(SLOG_SEND_MAIL,
+ N_("Invalid authentication methods compiled into sudo! "
+ "You may not mix standalone and non-standalone authentication."));
+ debug_return_int(-1);
+ }
+ if (!found) {
+ /* Found first standalone method. */
+ found = true;
+ continue;
+ }
+ /* Disable other standalone methods. */
+ SET(auth->flags, FLAG_DISABLED);
+ }
+ }
+
+ /* Set FLAG_ONEANDONLY if there is only one auth method. */
+ for (auth = auth_switch; auth->name; auth++) {
+ /* Find first enabled auth method. */
+ if (!IS_DISABLED(auth)) {
+ sudo_auth *first = auth;
+ /* Check for others. */
+ for (; auth->name; auth++) {
+ if (!IS_DISABLED(auth))
+ break;
+ }
+ if (auth->name == NULL)
+ SET(first->flags, FLAG_ONEANDONLY);
+ break;
+ }
+ }
+
+ debug_return_int(status == AUTH_FATAL ? -1 : 0);
+}
+
+/*
+ * Cleanup all authentication approval methods.
+ * Returns true on success, false on failure and -1 on error.
+ */
+int
+sudo_auth_approval(struct passwd *pw, int validated, bool exempt)
+{
+ sudo_auth *auth;
+ debug_decl(sudo_auth_approval, SUDOERS_DEBUG_AUTH);
+
+ /* Call approval routines. */
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->approval && !IS_DISABLED(auth)) {
+ int status = (auth->approval)(pw, auth, exempt);
+ if (status != AUTH_SUCCESS) {
+ /* Assume error msg already printed. */
+ log_auth_failure(validated, 0);
+ debug_return_int(status == AUTH_FAILURE ? false : -1);
+ }
+ }
+ }
+ debug_return_int(true);
+}
+
+/*
+ * Cleanup all authentication methods.
+ * Returns 0 on success and -1 on error.
+ */
+int
+sudo_auth_cleanup(struct passwd *pw, bool force)
+{
+ sudo_auth *auth;
+ debug_decl(sudo_auth_cleanup, SUDOERS_DEBUG_AUTH);
+
+ /* Call cleanup routines. */
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->cleanup && !IS_DISABLED(auth)) {
+ int status = (auth->cleanup)(pw, auth, force);
+ if (status == AUTH_FATAL) {
+ /* Assume error msg already printed. */
+ debug_return_int(-1);
+ }
+ }
+ }
+ debug_return_int(0);
+}
+
+static void
+pass_warn(void)
+{
+ const char *warning = def_badpass_message;
+ debug_decl(pass_warn, SUDOERS_DEBUG_AUTH);
+
+#ifdef INSULT
+ if (def_insults)
+ warning = INSULT;
+#endif
+ sudo_printf(SUDO_CONV_ERROR_MSG|SUDO_CONV_PREFER_TTY, "%s\n", warning);
+
+ debug_return;
+}
+
+static bool
+user_interrupted(void)
+{
+ sigset_t mask;
+
+ return (sigpending(&mask) == 0 &&
+ (sigismember(&mask, SIGINT) || sigismember(&mask, SIGQUIT)));
+}
+
+/*
+ * Verify the specified user.
+ * Returns true if verified, false if not or -1 on error.
+ */
+int
+verify_user(struct passwd *pw, char *prompt, int validated,
+ struct sudo_conv_callback *callback)
+{
+ unsigned int ntries;
+ int ret, status, success = AUTH_FAILURE;
+ sudo_auth *auth;
+ sigset_t mask, omask;
+ struct sigaction sa, saved_sigtstp;
+ debug_decl(verify_user, SUDOERS_DEBUG_AUTH);
+
+ /* Make sure we have at least one auth method. */
+ if (auth_switch[0].name == NULL) {
+ audit_failure(NewArgv, N_("no authentication methods"));
+ log_warningx(SLOG_SEND_MAIL,
+ N_("There are no authentication methods compiled into sudo! "
+ "If you want to turn off authentication, use the "
+ "--disable-authentication configure option."));
+ debug_return_int(-1);
+ }
+
+ /* Enable suspend during password entry. */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = SIG_DFL;
+ (void) sigaction(SIGTSTP, &sa, &saved_sigtstp);
+
+ /*
+ * We treat authentication as a critical section and block
+ * keyboard-generated signals such as SIGINT and SIGQUIT
+ * which might otherwise interrupt a sleep(3).
+ * They are temporarily unblocked by auth_getpass().
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGQUIT);
+ (void) sigprocmask(SIG_BLOCK, &mask, &omask);
+
+ for (ntries = 0; ntries < def_passwd_tries; ntries++) {
+ int num_methods = 0;
+ char *pass = NULL;
+
+ /* If user attempted to interrupt password verify, quit now. */
+ if (user_interrupted())
+ goto done;
+
+ if (ntries != 0)
+ pass_warn();
+
+ /* Do any per-method setup and unconfigure the method if needed */
+ for (auth = auth_switch; auth->name; auth++) {
+ if (IS_DISABLED(auth))
+ continue;
+ num_methods++;
+ if (auth->setup != NULL) {
+ status = (auth->setup)(pw, &prompt, auth);
+ if (status == AUTH_FAILURE)
+ SET(auth->flags, FLAG_DISABLED);
+ else if (status == AUTH_FATAL || user_interrupted())
+ goto done; /* assume error msg already printed */
+ }
+ }
+ if (num_methods == 0) {
+ audit_failure(NewArgv, N_("no authentication methods"));
+ log_warningx(SLOG_SEND_MAIL,
+ N_("Unable to initialize authentication methods."));
+ debug_return_int(-1);
+ }
+
+ /* Get the password unless the auth function will do it for us */
+ if (!standalone) {
+ pass = auth_getpass(prompt, SUDO_CONV_PROMPT_ECHO_OFF, callback);
+ if (pass == NULL)
+ break;
+ }
+
+ /* Call authentication functions. */
+ for (auth = auth_switch; auth->name; auth++) {
+ if (IS_DISABLED(auth))
+ continue;
+
+ success = auth->status =
+ (auth->verify)(pw, standalone ? prompt : pass, auth, callback);
+ if (success != AUTH_FAILURE)
+ break;
+ }
+ if (pass != NULL)
+ freezero(pass, strlen(pass));
+
+ if (success != AUTH_FAILURE)
+ goto done;
+ }
+
+done:
+ /* Restore signal handlers and signal mask. */
+ (void) sigaction(SIGTSTP, &saved_sigtstp, NULL);
+ (void) sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ switch (success) {
+ case AUTH_SUCCESS:
+ ret = true;
+ break;
+ case AUTH_INTR:
+ case AUTH_FAILURE:
+ if (ntries != 0)
+ validated |= FLAG_BAD_PASSWORD;
+ log_auth_failure(validated, ntries);
+ ret = false;
+ break;
+ case AUTH_FATAL:
+ default:
+ log_auth_failure(validated, 0);
+ ret = -1;
+ break;
+ }
+
+ debug_return_int(ret);
+}
+
+/*
+ * Call authentication method begin session hooks.
+ * Returns 1 on success and -1 on error.
+ */
+int
+sudo_auth_begin_session(struct passwd *pw, char **user_env[])
+{
+ sudo_auth *auth;
+ debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH);
+
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->begin_session && !IS_DISABLED(auth)) {
+ int status = (auth->begin_session)(pw, user_env, auth);
+ if (status != AUTH_SUCCESS) {
+ /* Assume error msg already printed. */
+ debug_return_int(-1);
+ }
+ }
+ }
+ debug_return_int(1);
+}
+
+bool
+sudo_auth_needs_end_session(void)
+{
+ sudo_auth *auth;
+ bool needed = false;
+ debug_decl(sudo_auth_needs_end_session, SUDOERS_DEBUG_AUTH);
+
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->end_session && !IS_DISABLED(auth)) {
+ needed = true;
+ break;
+ }
+ }
+ debug_return_bool(needed);
+}
+
+/*
+ * Call authentication method end session hooks.
+ * Returns 1 on success and -1 on error.
+ */
+int
+sudo_auth_end_session(struct passwd *pw)
+{
+ sudo_auth *auth;
+ int status;
+ debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH);
+
+ for (auth = auth_switch; auth->name; auth++) {
+ if (auth->end_session && !IS_DISABLED(auth)) {
+ status = (auth->end_session)(pw, auth);
+ if (status == AUTH_FATAL) {
+ /* Assume error msg already printed. */
+ debug_return_int(-1);
+ }
+ }
+ }
+ debug_return_int(1);
+}
+
+/*
+ * Prompts the user for a password using the conversation function.
+ * Returns the plaintext password or NULL.
+ * The user is responsible for freeing the returned value.
+ */
+char *
+auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback)
+{
+ struct sudo_conv_message msg;
+ struct sudo_conv_reply repl;
+ sigset_t mask, omask;
+ debug_decl(auth_getpass, SUDOERS_DEBUG_AUTH);
+
+ /* Mask user input if pwfeedback set and echo is off. */
+ if (type == SUDO_CONV_PROMPT_ECHO_OFF && def_pwfeedback)
+ type = SUDO_CONV_PROMPT_MASK;
+
+ /* If visiblepw set, do not error out if there is no tty. */
+ if (def_visiblepw)
+ type |= SUDO_CONV_PROMPT_ECHO_OK;
+
+ /* Unblock SIGINT and SIGQUIT during password entry. */
+ /* XXX - do in tgetpass() itself instead? */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGQUIT);
+ (void) sigprocmask(SIG_UNBLOCK, &mask, &omask);
+
+ /* Call conversation function. */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_type = type;
+ msg.timeout = def_passwd_timeout.tv_sec;
+ msg.msg = prompt;
+ memset(&repl, 0, sizeof(repl));
+ sudo_conv(1, &msg, &repl, callback);
+ /* XXX - check for ENOTTY? */
+
+ /* Restore previous signal mask. */
+ (void) sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ debug_return_str_masked(repl.reply);
+}
+
+void
+dump_auth_methods(void)
+{
+ sudo_auth *auth;
+ debug_decl(dump_auth_methods, SUDOERS_DEBUG_AUTH);
+
+ sudo_printf(SUDO_CONV_INFO_MSG, _("Authentication methods:"));
+ for (auth = auth_switch; auth->name; auth++)
+ sudo_printf(SUDO_CONV_INFO_MSG, " '%s'", auth->name);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+
+ debug_return;
+}
diff --git a/plugins/sudoers/auth/sudo_auth.h b/plugins/sudoers/auth/sudo_auth.h
new file mode 100644
index 0000000..9ee408d
--- /dev/null
+++ b/plugins/sudoers/auth/sudo_auth.h
@@ -0,0 +1,104 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007-2016, 2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SUDO_AUTH_H
+#define SUDO_AUTH_H
+
+/* Auth function return values. */
+#define AUTH_SUCCESS 0
+#define AUTH_FAILURE 1
+#define AUTH_INTR 2
+#define AUTH_FATAL 3
+
+typedef struct sudo_auth {
+ int flags; /* various flags, see below */
+ int status; /* status from verify routine */
+ char *name; /* name of the method as a string */
+ void *data; /* method-specific data pointer */
+ int (*init)(struct passwd *pw, struct sudo_auth *auth);
+ int (*setup)(struct passwd *pw, char **prompt, struct sudo_auth *auth);
+ int (*verify)(struct passwd *pw, char *p, struct sudo_auth *auth, struct sudo_conv_callback *callback);
+ int (*approval)(struct passwd *pw, struct sudo_auth *auth, bool exempt);
+ int (*cleanup)(struct passwd *pw, struct sudo_auth *auth, bool force);
+ int (*begin_session)(struct passwd *pw, char **user_env[], struct sudo_auth *auth);
+ int (*end_session)(struct passwd *pw, struct sudo_auth *auth);
+} sudo_auth;
+
+/* Values for sudo_auth.flags. */
+#define FLAG_DISABLED 0x02 /* method disabled */
+#define FLAG_STANDALONE 0x04 /* standalone auth method */
+#define FLAG_ONEANDONLY 0x08 /* one and only auth method */
+
+/* Shortcuts for using the flags above. */
+#define IS_DISABLED(x) ((x)->flags & FLAG_DISABLED)
+#define IS_STANDALONE(x) ((x)->flags & FLAG_STANDALONE)
+#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
+
+/* Like tgetpass() but uses conversation function */
+char *auth_getpass(const char *prompt, int type, struct sudo_conv_callback *callback);
+
+/* Pointer to conversation function to use with auth_getpass(). */
+extern sudo_conv_t sudo_conv;
+
+/* Prototypes for standalone methods */
+int bsdauth_init(struct passwd *pw, sudo_auth *auth);
+int bsdauth_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback);
+int bsdauth_approval(struct passwd *pw, sudo_auth *auth, bool exempt);
+int bsdauth_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_aix_init(struct passwd *pw, sudo_auth *auth);
+int sudo_aix_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_aix_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_fwtk_init(struct passwd *pw, sudo_auth *auth);
+int sudo_fwtk_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_fwtk_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_pam_init(struct passwd *pw, sudo_auth *auth);
+int sudo_pam_init_quiet(struct passwd *pw, sudo_auth *auth);
+int sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_pam_approval(struct passwd *pw, sudo_auth *auth, bool exempt);
+int sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_pam_begin_session(struct passwd *pw, char **user_env[], sudo_auth *auth);
+int sudo_pam_end_session(struct passwd *pw, sudo_auth *auth);
+int sudo_securid_init(struct passwd *pw, sudo_auth *auth);
+int sudo_securid_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
+int sudo_securid_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_sia_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
+int sudo_sia_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_sia_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_sia_begin_session(struct passwd *pw, char **user_env[], sudo_auth *auth);
+
+/* Prototypes for normal methods */
+int sudo_afs_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_dce_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_krb5_init(struct passwd *pw, sudo_auth *auth);
+int sudo_krb5_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
+int sudo_krb5_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_krb5_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_passwd_init(struct passwd *pw, sudo_auth *auth);
+int sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_passwd_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+int sudo_rfc1938_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
+int sudo_rfc1938_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_secureware_init(struct passwd *pw, sudo_auth *auth);
+int sudo_secureware_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback);
+int sudo_secureware_cleanup(struct passwd *pw, sudo_auth *auth, bool force);
+
+/* Fields: name, flags, init, setup, verify, approval, cleanup, begin_sess, end_sess */
+#define AUTH_ENTRY(n, f, i, s, v, a, c, b, e) \
+ { (f), AUTH_FAILURE, (n), NULL, (i), (s), (v), (a), (c) , (b), (e) },
+
+#endif /* SUDO_AUTH_H */