xref: /freebsd/contrib/pam-krb5/module/options.c (revision e928afc531e68b7a142ee49d8f7e5c8426d54033)
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 *
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
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