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