1 /* 2 * Manage context structure. 3 * 4 * The context structure is the internal state maintained by the pam-krb5 5 * module between calls to the various public interfaces. 6 * 7 * Copyright 2005-2009, 2014, 2020-2021 Russ Allbery <eagle@eyrie.org> 8 * Copyright 2011 9 * The Board of Trustees of the Leland Stanford Junior University 10 * Copyright 2005 Andres Salomon <dilinger@debian.org> 11 * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com> 12 * 13 * SPDX-License-Identifier: BSD-3-clause or GPL-1+ 14 */ 15 16 #include <config.h> 17 #include <portable/pam.h> 18 #include <portable/system.h> 19 20 #include <errno.h> 21 22 #include <module/internal.h> 23 #include <pam-util/args.h> 24 #include <pam-util/logging.h> 25 26 27 /* 28 * Create a new context and populate it with the user from PAM and the current 29 * Kerberos context. Set the default realm if one was configured. 30 */ 31 int 32 pamk5_context_new(struct pam_args *args) 33 { 34 struct context *ctx; 35 int retval; 36 PAM_CONST char *name; 37 38 ctx = calloc(1, sizeof(struct context)); 39 if (ctx == NULL) { 40 retval = PAM_BUF_ERR; 41 goto done; 42 } 43 ctx->cache = NULL; 44 ctx->princ = NULL; 45 ctx->creds = NULL; 46 ctx->fast_cache = NULL; 47 ctx->context = args->ctx; 48 args->config->ctx = ctx; 49 50 /* 51 * This will prompt for the username if it's not already set (generally it 52 * will be). Otherwise, grab the saved username. 53 */ 54 retval = pam_get_user(args->pamh, &name, NULL); 55 if (retval != PAM_SUCCESS || name == NULL) { 56 if (retval == PAM_CONV_AGAIN) 57 retval = PAM_INCOMPLETE; 58 else 59 retval = PAM_SERVICE_ERR; 60 goto done; 61 } 62 ctx->name = strdup(name); 63 args->user = ctx->name; 64 65 /* Set a default realm if one was configured. */ 66 if (args->realm != NULL) { 67 retval = krb5_set_default_realm(ctx->context, args->realm); 68 if (retval != 0) { 69 putil_err_krb5(args, retval, "cannot set default realm"); 70 retval = PAM_SERVICE_ERR; 71 goto done; 72 } 73 } 74 75 done: 76 if (ctx != NULL && retval != PAM_SUCCESS) 77 pamk5_context_free(args); 78 return retval; 79 } 80 81 82 /* 83 * Retrieve a context from the PAM data structures, returning failure if no 84 * context was present. Note that OpenSSH loses contexts between authenticate 85 * and setcred, so failure shouldn't always be fatal. 86 */ 87 int 88 pamk5_context_fetch(struct pam_args *args) 89 { 90 int pamret; 91 92 pamret = pam_get_data(args->pamh, "pam_krb5", (void *) &args->config->ctx); 93 if (pamret != PAM_SUCCESS) 94 args->config->ctx = NULL; 95 if (pamret == PAM_SUCCESS && args->config->ctx == NULL) 96 return PAM_SERVICE_ERR; 97 if (args->config->ctx != NULL) 98 args->user = args->config->ctx->name; 99 return pamret; 100 } 101 102 103 /* 104 * Free a context and all of the data that's stored in it. Normally this also 105 * includes destroying the ticket cache, but don't do this (just close it) if 106 * a flag was set to preserve it. 107 * 108 * This function is common code between pamk5_context_free (called internally 109 * by our code) and pamk5_context_destroy (called by PAM as a data callback). 110 */ 111 static void 112 context_free(struct context *ctx, bool free_context) 113 { 114 if (ctx == NULL) 115 return; 116 free(ctx->name); 117 if (ctx->context != NULL) { 118 if (ctx->princ != NULL) 119 krb5_free_principal(ctx->context, ctx->princ); 120 if (ctx->cache != NULL) { 121 if (ctx->dont_destroy_cache) 122 krb5_cc_close(ctx->context, ctx->cache); 123 else 124 krb5_cc_destroy(ctx->context, ctx->cache); 125 } 126 if (ctx->creds != NULL) { 127 krb5_free_cred_contents(ctx->context, ctx->creds); 128 free(ctx->creds); 129 } 130 if (free_context) 131 krb5_free_context(ctx->context); 132 } 133 if (ctx->fast_cache != NULL) 134 krb5_cc_destroy(ctx->context, ctx->fast_cache); 135 free(ctx); 136 } 137 138 139 /* 140 * Free the current context, used internally by pam-krb5 code. This is a 141 * wrapper around context_free that makes sure we don't destroy the Kerberos 142 * context if it's the same as the top-level context and handles other 143 * bookkeeping in the top-level pam_args struct. 144 */ 145 void 146 pamk5_context_free(struct pam_args *args) 147 { 148 if (args->config->ctx == NULL) 149 return; 150 if (args->user == args->config->ctx->name) 151 args->user = NULL; 152 context_free(args->config->ctx, args->ctx != args->config->ctx->context); 153 args->config->ctx = NULL; 154 } 155 156 157 /* 158 * The PAM callback to destroy the context stored in the PAM data structures. 159 */ 160 void 161 pamk5_context_destroy(pam_handle_t *pamh UNUSED, void *data, 162 int pam_end_status) 163 { 164 struct context *ctx = (struct context *) data; 165 166 /* 167 * Do not destroy the cache if the status contains PAM_DATA_SILENT, since 168 * in that case we may be in a child and the parent will still rely on 169 * underlying resources such as the ticket cache to exist. 170 */ 171 if (PAM_DATA_SILENT != 0 && (pam_end_status & PAM_DATA_SILENT)) 172 ctx->dont_destroy_cache = true; 173 174 /* The rest of the work is in context_free. */ 175 if (ctx != NULL) 176 context_free(ctx, true); 177 } 178