xref: /freebsd/contrib/pam-krb5/module/options.c (revision d903f2e289a7f8c502918ebc64d46aaa86efce24)
1 /*
2  * Option handling for pam-krb5.
3  *
4  * Responsible for initializing the args struct that's passed to nearly all
5  * internal functions.  Retrieves configuration information from krb5.conf and
6  * parses the PAM configuration.
7  *
8  * Copyright 2005-2010, 2014, 2020 Russ Allbery <eagle@eyrie.org>
9  * Copyright 2011-2012
10  *     The Board of Trustees of the Leland Stanford Junior University
11  * Copyright 2005 Andres Salomon <dilinger@debian.org>
12  * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
13  *
14  * SPDX-License-Identifier: BSD-3-clause or GPL-1+
15  */
16 
17 #include <config.h>
18 #include <portable/krb5.h>
19 #include <portable/system.h>
20 
21 #include <errno.h>
22 
23 #include <module/internal.h>
24 #include <pam-util/args.h>
25 #include <pam-util/logging.h>
26 #include <pam-util/options.h>
27 #include <pam-util/vector.h>
28 
29 /* Our option definition.  Must be sorted. */
30 #define K(name) (#name), offsetof(struct pam_config, name)
31 /* clang-format off */
32 static const struct option options[] = {
33     { K(alt_auth_map),       true,  STRING (NULL)  },
34     { K(anon_fast),          true,  BOOL   (false) },
35     { K(banner),             true,  STRING ("Kerberos") },
36     { K(ccache),             true,  STRING (NULL)  },
37     { K(ccache_dir),         true,  STRING ("FILE:/tmp") },
38     { K(clear_on_fail),      true,  BOOL   (false) },
39     { K(debug),              true,  BOOL   (false) },
40     { K(defer_pwchange),     true,  BOOL   (false) },
41     { K(expose_account),     true,  BOOL   (false) },
42     { K(fail_pwchange),      true,  BOOL   (false) },
43     { K(fast_ccache),        true,  STRING (NULL)  },
44     { K(force_alt_auth),     true,  BOOL   (false) },
45     { K(force_first_pass),   false, BOOL   (false) },
46     { K(force_pwchange),     true,  BOOL   (false) },
47     { K(forwardable),        true,  BOOL   (false) },
48     { K(ignore_k5login),     true,  BOOL   (false) },
49     { K(ignore_root),        true,  BOOL   (false) },
50     { K(keytab),             true,  STRING (NULL)  },
51     { K(minimum_uid),        true,  NUMBER (0)     },
52     { K(no_ccache),          false, BOOL   (false) },
53     { K(no_prompt),          true,  BOOL   (false) },
54     { K(no_update_user),     true,  BOOL   (false) },
55     { K(no_warn),	     true,  BOOL   (false) },
56     { K(only_alt_auth),      true,  BOOL   (false) },
57     { K(pkinit_anchors),     true,  STRING (NULL)  },
58     { K(pkinit_prompt),      true,  BOOL   (false) },
59     { K(pkinit_user),        true,  STRING (NULL)  },
60     { K(preauth_opt),        true,  LIST   (NULL)  },
61     { K(prompt_principal),   true,  BOOL   (false) },
62     { K(realm),              false, STRING (NULL)  },
63     { K(renew_lifetime),     true,  TIME   (0)     },
64     { K(retain_after_close), true,  BOOL   (false) },
65     { K(search_k5login),     true,  BOOL   (false) },
66     { K(silent),             false, BOOL   (false) },
67     { K(ticket_lifetime),    true,  TIME   (0)     },
68     { K(trace),              false, STRING (NULL)  },
69     { K(try_first_pass),     false, BOOL   (false) },
70     { K(try_pkinit),         true,  BOOL   (false) },
71     { K(use_authtok),        false, BOOL   (false) },
72     { K(use_first_pass),     false, BOOL   (false) },
73     { K(use_pkinit),         true,  BOOL   (false) },
74     { K(user_realm),         true,  STRING (NULL)  },
75 };
76 /* clang-format on */
77 static const size_t optlen = sizeof(options) / sizeof(options[0]);
78 
79 
80 /*
81  * Allocate a new struct pam_args and initialize its data members, including
82  * parsing the arguments and getting settings from krb5.conf.  Check the
83  * resulting options for consistency.
84  */
85 struct pam_args *
pamk5_init(pam_handle_t * pamh,int flags,int argc,const char ** argv)86 pamk5_init(pam_handle_t *pamh, int flags, int argc, const char **argv)
87 {
88     int i;
89     struct pam_args *args;
90     struct pam_config *config = NULL;
91 
92     args = putil_args_new(pamh, flags);
93     if (args == NULL) {
94         return NULL;
95     }
96     config = calloc(1, sizeof(struct pam_config));
97     if (config == NULL) {
98         goto nomem;
99     }
100     args->config = config;
101 
102     /*
103      * Do an initial scan to see if the realm is already set in our options.
104      * If so, make sure that's set before we start loading option values,
105      * since it affects what comes out of krb5.conf.
106      *
107      * We will then ignore args->config->realm, set later by option parsing,
108      * in favor of using args->realm extracted here.  However, the latter must
109      * exist to avoid throwing unknown option errors.
110      */
111     for (i = 0; i < argc; i++) {
112         if (strncmp(argv[i], "realm=", 6) != 0)
113             continue;
114         free(args->realm);
115         args->realm = strdup(&argv[i][strlen("realm=")]);
116         if (args->realm == NULL)
117             goto nomem;
118     }
119 
120     if (!putil_args_defaults(args, options, optlen)) {
121         free(config);
122         putil_args_free(args);
123         return NULL;
124     }
125     if (!putil_args_krb5(args, "pam", options, optlen)) {
126         goto fail;
127     }
128     if (!putil_args_parse(args, argc, argv, options, optlen)) {
129         goto fail;
130     }
131     if (config->debug) {
132         args->debug = true;
133     }
134     if (config->silent) {
135         args->silent = true;
136     }
137 
138     /* An empty banner should be treated the same as not having one. */
139     if (config->banner != NULL && config->banner[0] == '\0') {
140         free(config->banner);
141         config->banner = NULL;
142     }
143 
144     /* Sanity-check try_first_pass, use_first_pass, and force_first_pass. */
145     if (config->force_first_pass && config->try_first_pass) {
146         putil_err(args, "force_first_pass set, ignoring try_first_pass");
147         config->try_first_pass = 0;
148     }
149     if (config->force_first_pass && config->use_first_pass) {
150         putil_err(args, "force_first_pass set, ignoring use_first_pass");
151         config->use_first_pass = 0;
152     }
153     if (config->use_first_pass && config->try_first_pass) {
154         putil_err(args, "use_first_pass set, ignoring try_first_pass");
155         config->try_first_pass = 0;
156     }
157 
158     /*
159      * Don't set expose_account if we're using search_k5login.  The user will
160      * get a principal formed from the account into which they're logging in,
161      * which isn't the password they'll use (that's the whole point of
162      * search_k5login).
163      */
164     if (config->search_k5login) {
165         config->expose_account = 0;
166     }
167 
168     /* UIDs are unsigned on some systems. */
169     if (config->minimum_uid < 0) {
170         config->minimum_uid = 0;
171     }
172 
173     /*
174      * Warn if PKINIT options were set and PKINIT isn't supported.  The MIT
175      * method (krb5_get_init_creds_opt_set_pa) can't support use_pkinit.
176      */
177 #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT
178 #    ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA
179     if (config->try_pkinit) {
180         putil_err(args, "try_pkinit requested but PKINIT not available");
181     } else if (config->use_pkinit) {
182         putil_err(args, "use_pkinit requested but PKINIT not available");
183     }
184 #    endif
185 #    ifndef HAVE_KRB5_GET_PROMPT_TYPES
186     if (config->use_pkinit) {
187         putil_err(args, "use_pkinit requested but PKINIT cannot be enforced");
188     }
189 #    endif
190 #endif
191 
192     /* Warn if the FAST option was set and FAST isn't supported. */
193 #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
194     if (config->fast_ccache || config->anon_fast) {
195         putil_err(args, "fast_ccache or anon_fast requested but FAST not"
196                         " supported by Kerberos libraries");
197     }
198 #endif
199 
200     /* If tracing was requested enable it if possible. */
201 #ifdef HAVE_KRB5_SET_TRACE_FILENAME
202     if (config->trace != NULL) {
203         krb5_error_code retval;
204 
205         retval = krb5_set_trace_filename(args->ctx, config->trace);
206         if (retval == 0)
207             putil_debug(args, "enabled trace logging to %s", config->trace);
208         else
209             putil_err_krb5(args, retval, "cannot enable trace logging to %s",
210                            config->trace);
211     }
212 #else
213     if (config->trace != NULL) {
214         putil_err(args, "trace logging requested but not supported");
215     }
216 #endif
217 
218     return args;
219 
220 nomem:
221     putil_crit(args, "cannot allocate memory: %s", strerror(errno));
222     free(config);
223     putil_args_free(args);
224     return NULL;
225 
226 fail:
227     pamk5_free(args);
228     return NULL;
229 }
230 
231 
232 /*
233  * Free the allocated args struct and any memory it points to.
234  */
235 void
pamk5_free(struct pam_args * args)236 pamk5_free(struct pam_args *args)
237 {
238     struct pam_config *config;
239 
240     if (args == NULL)
241         return;
242     config = args->config;
243     if (config != NULL) {
244         free(config->alt_auth_map);
245         free(config->banner);
246         free(config->ccache);
247         free(config->ccache_dir);
248         free(config->fast_ccache);
249         free(config->keytab);
250         free(config->pkinit_anchors);
251         free(config->pkinit_user);
252         vector_free(config->preauth_opt);
253         free(config->realm);
254         free(config->trace);
255         free(config->user_realm);
256         free(args->config);
257         args->config = NULL;
258     }
259     putil_args_free(args);
260 }
261