aboutsummaryrefslogtreecommitdiff
path: root/module/options.c
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2025-04-17 02:13:41 +0000
committerCy Schubert <cy@FreeBSD.org>2025-05-27 16:20:06 +0000
commit24f0b4ca2d565cdbb4fe7839ff28320706bf2386 (patch)
treebc9ce87edb73f767f5580887d0fc8c643b9d7a49 /module/options.c
Diffstat (limited to 'module/options.c')
-rw-r--r--module/options.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/module/options.c b/module/options.c
new file mode 100644
index 000000000000..f2c3791d895a
--- /dev/null
+++ b/module/options.c
@@ -0,0 +1,259 @@
+/*
+ * Option handling for pam-krb5.
+ *
+ * Responsible for initializing the args struct that's passed to nearly all
+ * internal functions. Retrieves configuration information from krb5.conf and
+ * parses the PAM configuration.
+ *
+ * Copyright 2005-2010, 2014, 2020 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2011-2012
+ * The Board of Trustees of the Leland Stanford Junior University
+ * Copyright 2005 Andres Salomon <dilinger@debian.org>
+ * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
+ *
+ * SPDX-License-Identifier: BSD-3-clause or GPL-1+
+ */
+
+#include <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <errno.h>
+
+#include <module/internal.h>
+#include <pam-util/args.h>
+#include <pam-util/logging.h>
+#include <pam-util/options.h>
+#include <pam-util/vector.h>
+
+/* Our option definition. Must be sorted. */
+#define K(name) (#name), offsetof(struct pam_config, name)
+/* clang-format off */
+static const struct option options[] = {
+ { K(alt_auth_map), true, STRING (NULL) },
+ { K(anon_fast), true, BOOL (false) },
+ { K(banner), true, STRING ("Kerberos") },
+ { K(ccache), true, STRING (NULL) },
+ { K(ccache_dir), true, STRING ("FILE:/tmp") },
+ { K(clear_on_fail), true, BOOL (false) },
+ { K(debug), true, BOOL (false) },
+ { K(defer_pwchange), true, BOOL (false) },
+ { K(expose_account), true, BOOL (false) },
+ { K(fail_pwchange), true, BOOL (false) },
+ { K(fast_ccache), true, STRING (NULL) },
+ { K(force_alt_auth), true, BOOL (false) },
+ { K(force_first_pass), false, BOOL (false) },
+ { K(force_pwchange), true, BOOL (false) },
+ { K(forwardable), true, BOOL (false) },
+ { K(ignore_k5login), true, BOOL (false) },
+ { K(ignore_root), true, BOOL (false) },
+ { K(keytab), true, STRING (NULL) },
+ { K(minimum_uid), true, NUMBER (0) },
+ { K(no_ccache), false, BOOL (false) },
+ { K(no_prompt), true, BOOL (false) },
+ { K(no_update_user), true, BOOL (false) },
+ { K(only_alt_auth), true, BOOL (false) },
+ { K(pkinit_anchors), true, STRING (NULL) },
+ { K(pkinit_prompt), true, BOOL (false) },
+ { K(pkinit_user), true, STRING (NULL) },
+ { K(preauth_opt), true, LIST (NULL) },
+ { K(prompt_principal), true, BOOL (false) },
+ { K(realm), false, STRING (NULL) },
+ { K(renew_lifetime), true, TIME (0) },
+ { K(retain_after_close), true, BOOL (false) },
+ { K(search_k5login), true, BOOL (false) },
+ { K(silent), false, BOOL (false) },
+ { K(ticket_lifetime), true, TIME (0) },
+ { K(trace), false, STRING (NULL) },
+ { K(try_first_pass), false, BOOL (false) },
+ { K(try_pkinit), true, BOOL (false) },
+ { K(use_authtok), false, BOOL (false) },
+ { K(use_first_pass), false, BOOL (false) },
+ { K(use_pkinit), true, BOOL (false) },
+ { K(user_realm), true, STRING (NULL) },
+};
+/* clang-format on */
+static const size_t optlen = sizeof(options) / sizeof(options[0]);
+
+
+/*
+ * Allocate a new struct pam_args and initialize its data members, including
+ * parsing the arguments and getting settings from krb5.conf. Check the
+ * resulting options for consistency.
+ */
+struct pam_args *
+pamk5_init(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int i;
+ struct pam_args *args;
+ struct pam_config *config = NULL;
+
+ args = putil_args_new(pamh, flags);
+ if (args == NULL) {
+ return NULL;
+ }
+ config = calloc(1, sizeof(struct pam_config));
+ if (config == NULL) {
+ goto nomem;
+ }
+ args->config = config;
+
+ /*
+ * Do an initial scan to see if the realm is already set in our options.
+ * If so, make sure that's set before we start loading option values,
+ * since it affects what comes out of krb5.conf.
+ *
+ * We will then ignore args->config->realm, set later by option parsing,
+ * in favor of using args->realm extracted here. However, the latter must
+ * exist to avoid throwing unknown option errors.
+ */
+ for (i = 0; i < argc; i++) {
+ if (strncmp(argv[i], "realm=", 6) != 0)
+ continue;
+ free(args->realm);
+ args->realm = strdup(&argv[i][strlen("realm=")]);
+ if (args->realm == NULL)
+ goto nomem;
+ }
+
+ if (!putil_args_defaults(args, options, optlen)) {
+ free(config);
+ putil_args_free(args);
+ return NULL;
+ }
+ if (!putil_args_krb5(args, "pam", options, optlen)) {
+ goto fail;
+ }
+ if (!putil_args_parse(args, argc, argv, options, optlen)) {
+ goto fail;
+ }
+ if (config->debug) {
+ args->debug = true;
+ }
+ if (config->silent) {
+ args->silent = true;
+ }
+
+ /* An empty banner should be treated the same as not having one. */
+ if (config->banner != NULL && config->banner[0] == '\0') {
+ free(config->banner);
+ config->banner = NULL;
+ }
+
+ /* Sanity-check try_first_pass, use_first_pass, and force_first_pass. */
+ if (config->force_first_pass && config->try_first_pass) {
+ putil_err(args, "force_first_pass set, ignoring try_first_pass");
+ config->try_first_pass = 0;
+ }
+ if (config->force_first_pass && config->use_first_pass) {
+ putil_err(args, "force_first_pass set, ignoring use_first_pass");
+ config->use_first_pass = 0;
+ }
+ if (config->use_first_pass && config->try_first_pass) {
+ putil_err(args, "use_first_pass set, ignoring try_first_pass");
+ config->try_first_pass = 0;
+ }
+
+ /*
+ * Don't set expose_account if we're using search_k5login. The user will
+ * get a principal formed from the account into which they're logging in,
+ * which isn't the password they'll use (that's the whole point of
+ * search_k5login).
+ */
+ if (config->search_k5login) {
+ config->expose_account = 0;
+ }
+
+ /* UIDs are unsigned on some systems. */
+ if (config->minimum_uid < 0) {
+ config->minimum_uid = 0;
+ }
+
+ /*
+ * Warn if PKINIT options were set and PKINIT isn't supported. The MIT
+ * method (krb5_get_init_creds_opt_set_pa) can't support use_pkinit.
+ */
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT
+# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA
+ if (config->try_pkinit) {
+ putil_err(args, "try_pkinit requested but PKINIT not available");
+ } else if (config->use_pkinit) {
+ putil_err(args, "use_pkinit requested but PKINIT not available");
+ }
+# endif
+# ifndef HAVE_KRB5_GET_PROMPT_TYPES
+ if (config->use_pkinit) {
+ putil_err(args, "use_pkinit requested but PKINIT cannot be enforced");
+ }
+# endif
+#endif
+
+ /* Warn if the FAST option was set and FAST isn't supported. */
+#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
+ if (config->fast_ccache || config->anon_fast) {
+ putil_err(args, "fast_ccache or anon_fast requested but FAST not"
+ " supported by Kerberos libraries");
+ }
+#endif
+
+ /* If tracing was requested enable it if possible. */
+#ifdef HAVE_KRB5_SET_TRACE_FILENAME
+ if (config->trace != NULL) {
+ krb5_error_code retval;
+
+ retval = krb5_set_trace_filename(args->ctx, config->trace);
+ if (retval == 0)
+ putil_debug(args, "enabled trace logging to %s", config->trace);
+ else
+ putil_err_krb5(args, retval, "cannot enable trace logging to %s",
+ config->trace);
+ }
+#else
+ if (config->trace != NULL) {
+ putil_err(args, "trace logging requested but not supported");
+ }
+#endif
+
+ return args;
+
+nomem:
+ putil_crit(args, "cannot allocate memory: %s", strerror(errno));
+ free(config);
+ putil_args_free(args);
+ return NULL;
+
+fail:
+ pamk5_free(args);
+ return NULL;
+}
+
+
+/*
+ * Free the allocated args struct and any memory it points to.
+ */
+void
+pamk5_free(struct pam_args *args)
+{
+ struct pam_config *config;
+
+ if (args == NULL)
+ return;
+ config = args->config;
+ if (config != NULL) {
+ free(config->alt_auth_map);
+ free(config->banner);
+ free(config->ccache);
+ free(config->ccache_dir);
+ free(config->fast_ccache);
+ free(config->keytab);
+ free(config->pkinit_anchors);
+ free(config->pkinit_user);
+ vector_free(config->preauth_opt);
+ free(config->realm);
+ free(config->trace);
+ free(config->user_realm);
+ free(args->config);
+ args->config = NULL;
+ }
+ putil_args_free(args);
+}