xref: /freebsd/contrib/pam-krb5/module/context.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
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