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