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