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
pamk5_context_new(struct pam_args * args)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
pamk5_context_fetch(struct pam_args * args)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
context_free(struct context * ctx,bool free_context)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
pamk5_context_free(struct pam_args * args)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
pamk5_context_destroy(pam_handle_t * pamh UNUSED,void * data,int pam_end_status)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