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 * 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 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