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