xref: /freebsd/contrib/pam-krb5/module/cache.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Ticket cache initialization.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * Provides functions for creating ticket caches, used by pam_authenticate,
5*bf6873c5SCy Schubert  * pam_setcred, and pam_chauthtok after changing an expired password.
6*bf6873c5SCy Schubert  *
7*bf6873c5SCy Schubert  * Copyright 2005-2009, 2014, 2020 Russ Allbery <eagle@eyrie.org>
8*bf6873c5SCy Schubert  * Copyright 2011-2012
9*bf6873c5SCy Schubert  *     The Board of Trustees of the Leland Stanford Junior University
10*bf6873c5SCy Schubert  * Copyright 2005 Andres Salomon <dilinger@debian.org>
11*bf6873c5SCy Schubert  * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
12*bf6873c5SCy Schubert  *
13*bf6873c5SCy Schubert  * SPDX-License-Identifier: BSD-3-clause or GPL-1+
14*bf6873c5SCy Schubert  */
15*bf6873c5SCy Schubert 
16*bf6873c5SCy Schubert #include <config.h>
17*bf6873c5SCy Schubert #include <portable/krb5.h>
18*bf6873c5SCy Schubert #include <portable/pam.h>
19*bf6873c5SCy Schubert #include <portable/system.h>
20*bf6873c5SCy Schubert 
21*bf6873c5SCy Schubert #include <errno.h>
22*bf6873c5SCy Schubert 
23*bf6873c5SCy Schubert #include <module/internal.h>
24*bf6873c5SCy Schubert #include <pam-util/args.h>
25*bf6873c5SCy Schubert #include <pam-util/logging.h>
26*bf6873c5SCy Schubert 
27*bf6873c5SCy Schubert 
28*bf6873c5SCy Schubert /*
29*bf6873c5SCy Schubert  * Get the name of a cache.  Takes the name of the environment variable that
30*bf6873c5SCy Schubert  * should be set to indicate which cache to use, either the permanent cache
31*bf6873c5SCy Schubert  * (KRB5CCNAME) or the temporary cache (PAM_KRB5CCNAME).
32*bf6873c5SCy Schubert  *
33*bf6873c5SCy Schubert  * Treat an empty environment variable setting the same as if the variable
34*bf6873c5SCy Schubert  * was not set, since on FreeBSD we can't delete the environment variable,
35*bf6873c5SCy Schubert  * only set it to an empty value.
36*bf6873c5SCy Schubert  */
37*bf6873c5SCy Schubert const char *
pamk5_get_krb5ccname(struct pam_args * args,const char * key)38*bf6873c5SCy Schubert pamk5_get_krb5ccname(struct pam_args *args, const char *key)
39*bf6873c5SCy Schubert {
40*bf6873c5SCy Schubert     const char *name;
41*bf6873c5SCy Schubert 
42*bf6873c5SCy Schubert     /* When refreshing a cache, we need to try the regular environment. */
43*bf6873c5SCy Schubert     name = pam_getenv(args->pamh, key);
44*bf6873c5SCy Schubert     if (name == NULL || *name == '\0')
45*bf6873c5SCy Schubert         name = getenv(key);
46*bf6873c5SCy Schubert     if (name == NULL || *name == '\0')
47*bf6873c5SCy Schubert         return NULL;
48*bf6873c5SCy Schubert     else
49*bf6873c5SCy Schubert         return name;
50*bf6873c5SCy Schubert }
51*bf6873c5SCy Schubert 
52*bf6873c5SCy Schubert 
53*bf6873c5SCy Schubert /*
54*bf6873c5SCy Schubert  * Put the ticket cache information into the environment.  Takes the path and
55*bf6873c5SCy Schubert  * the environment variable to set, since this is used both for the permanent
56*bf6873c5SCy Schubert  * cache (KRB5CCNAME) and the temporary cache (PAM_KRB5CCNAME).  Returns a PAM
57*bf6873c5SCy Schubert  * status code.
58*bf6873c5SCy Schubert  */
59*bf6873c5SCy Schubert int
pamk5_set_krb5ccname(struct pam_args * args,const char * name,const char * key)60*bf6873c5SCy Schubert pamk5_set_krb5ccname(struct pam_args *args, const char *name, const char *key)
61*bf6873c5SCy Schubert {
62*bf6873c5SCy Schubert     char *env_name = NULL;
63*bf6873c5SCy Schubert     int pamret;
64*bf6873c5SCy Schubert 
65*bf6873c5SCy Schubert     if (asprintf(&env_name, "%s=%s", key, name) < 0) {
66*bf6873c5SCy Schubert         putil_crit(args, "asprintf failed: %s", strerror(errno));
67*bf6873c5SCy Schubert         pamret = PAM_BUF_ERR;
68*bf6873c5SCy Schubert         goto done;
69*bf6873c5SCy Schubert     }
70*bf6873c5SCy Schubert     pamret = pam_putenv(args->pamh, env_name);
71*bf6873c5SCy Schubert     if (pamret != PAM_SUCCESS) {
72*bf6873c5SCy Schubert         putil_err_pam(args, pamret, "pam_putenv failed");
73*bf6873c5SCy Schubert         pamret = PAM_SERVICE_ERR;
74*bf6873c5SCy Schubert         goto done;
75*bf6873c5SCy Schubert     }
76*bf6873c5SCy Schubert     pamret = PAM_SUCCESS;
77*bf6873c5SCy Schubert 
78*bf6873c5SCy Schubert done:
79*bf6873c5SCy Schubert     free(env_name);
80*bf6873c5SCy Schubert     return pamret;
81*bf6873c5SCy Schubert }
82*bf6873c5SCy Schubert 
83*bf6873c5SCy Schubert 
84*bf6873c5SCy Schubert /*
85*bf6873c5SCy Schubert  * Given the template for a ticket cache name, initialize that file securely
86*bf6873c5SCy Schubert  * mkstemp.  Returns a PAM success or error code.
87*bf6873c5SCy Schubert  */
88*bf6873c5SCy Schubert int
pamk5_cache_mkstemp(struct pam_args * args,char * template)89*bf6873c5SCy Schubert pamk5_cache_mkstemp(struct pam_args *args, char *template)
90*bf6873c5SCy Schubert {
91*bf6873c5SCy Schubert     int ccfd, oerrno;
92*bf6873c5SCy Schubert 
93*bf6873c5SCy Schubert     ccfd = mkstemp(template);
94*bf6873c5SCy Schubert     if (ccfd < 0) {
95*bf6873c5SCy Schubert         oerrno = errno;
96*bf6873c5SCy Schubert         putil_crit(args, "mkstemp(\"%s\") failed: %s", template,
97*bf6873c5SCy Schubert                    strerror(errno));
98*bf6873c5SCy Schubert         errno = oerrno;
99*bf6873c5SCy Schubert         return PAM_SERVICE_ERR;
100*bf6873c5SCy Schubert     }
101*bf6873c5SCy Schubert     close(ccfd);
102*bf6873c5SCy Schubert     return PAM_SUCCESS;
103*bf6873c5SCy Schubert }
104*bf6873c5SCy Schubert 
105*bf6873c5SCy Schubert 
106*bf6873c5SCy Schubert /*
107*bf6873c5SCy Schubert  * Given a cache name and the initial credentials, initialize the cache, store
108*bf6873c5SCy Schubert  * the credentials in that cache, and return a pointer to the new cache in the
109*bf6873c5SCy Schubert  * cache argument.  Returns a PAM success or error code.
110*bf6873c5SCy Schubert  */
111*bf6873c5SCy Schubert int
pamk5_cache_init(struct pam_args * args,const char * ccname,krb5_creds * creds,krb5_ccache * cache)112*bf6873c5SCy Schubert pamk5_cache_init(struct pam_args *args, const char *ccname, krb5_creds *creds,
113*bf6873c5SCy Schubert                  krb5_ccache *cache)
114*bf6873c5SCy Schubert {
115*bf6873c5SCy Schubert     struct context *ctx;
116*bf6873c5SCy Schubert     int retval;
117*bf6873c5SCy Schubert 
118*bf6873c5SCy Schubert     if (args == NULL || args->config == NULL || args->config->ctx == NULL
119*bf6873c5SCy Schubert         || args->config->ctx->context == NULL)
120*bf6873c5SCy Schubert         return PAM_SERVICE_ERR;
121*bf6873c5SCy Schubert     ctx = args->config->ctx;
122*bf6873c5SCy Schubert     retval = krb5_cc_resolve(ctx->context, ccname, cache);
123*bf6873c5SCy Schubert     if (retval != 0) {
124*bf6873c5SCy Schubert         putil_err_krb5(args, retval, "cannot resolve ticket cache %s", ccname);
125*bf6873c5SCy Schubert         retval = PAM_SERVICE_ERR;
126*bf6873c5SCy Schubert         goto done;
127*bf6873c5SCy Schubert     }
128*bf6873c5SCy Schubert     retval = krb5_cc_initialize(ctx->context, *cache, ctx->princ);
129*bf6873c5SCy Schubert     if (retval != 0) {
130*bf6873c5SCy Schubert         putil_err_krb5(args, retval, "cannot initialize ticket cache %s",
131*bf6873c5SCy Schubert                        ccname);
132*bf6873c5SCy Schubert         retval = PAM_SERVICE_ERR;
133*bf6873c5SCy Schubert         goto done;
134*bf6873c5SCy Schubert     }
135*bf6873c5SCy Schubert     retval = krb5_cc_store_cred(ctx->context, *cache, creds);
136*bf6873c5SCy Schubert     if (retval != 0) {
137*bf6873c5SCy Schubert         putil_err_krb5(args, retval, "cannot store credentials in %s", ccname);
138*bf6873c5SCy Schubert         retval = PAM_SERVICE_ERR;
139*bf6873c5SCy Schubert         goto done;
140*bf6873c5SCy Schubert     }
141*bf6873c5SCy Schubert 
142*bf6873c5SCy Schubert done:
143*bf6873c5SCy Schubert     if (retval != PAM_SUCCESS && *cache != NULL) {
144*bf6873c5SCy Schubert         krb5_cc_destroy(ctx->context, *cache);
145*bf6873c5SCy Schubert         *cache = NULL;
146*bf6873c5SCy Schubert     }
147*bf6873c5SCy Schubert     return retval;
148*bf6873c5SCy Schubert }
149*bf6873c5SCy Schubert 
150*bf6873c5SCy Schubert 
151*bf6873c5SCy Schubert /*
152*bf6873c5SCy Schubert  * Initialize an internal ticket cache with a random name, store the given
153*bf6873c5SCy Schubert  * credentials in the cache, and store the cache in the context.  Put the path
154*bf6873c5SCy Schubert  * in PAM_KRB5CCNAME where it can be picked up later by pam_setcred.  Returns
155*bf6873c5SCy Schubert  * a PAM success or error code.
156*bf6873c5SCy Schubert  */
157*bf6873c5SCy Schubert int
pamk5_cache_init_random(struct pam_args * args,krb5_creds * creds)158*bf6873c5SCy Schubert pamk5_cache_init_random(struct pam_args *args, krb5_creds *creds)
159*bf6873c5SCy Schubert {
160*bf6873c5SCy Schubert     char *cache_name = NULL;
161*bf6873c5SCy Schubert     const char *dir;
162*bf6873c5SCy Schubert     int pamret;
163*bf6873c5SCy Schubert 
164*bf6873c5SCy Schubert     /* Store the obtained credentials in a temporary cache. */
165*bf6873c5SCy Schubert     dir = args->config->ccache_dir;
166*bf6873c5SCy Schubert     if (strncmp("FILE:", args->config->ccache_dir, strlen("FILE:")) == 0)
167*bf6873c5SCy Schubert         dir += strlen("FILE:");
168*bf6873c5SCy Schubert     if (asprintf(&cache_name, "%s/krb5cc_pam_XXXXXX", dir) < 0) {
169*bf6873c5SCy Schubert         putil_crit(args, "malloc failure: %s", strerror(errno));
170*bf6873c5SCy Schubert         return PAM_SERVICE_ERR;
171*bf6873c5SCy Schubert     }
172*bf6873c5SCy Schubert     pamret = pamk5_cache_mkstemp(args, cache_name);
173*bf6873c5SCy Schubert     if (pamret != PAM_SUCCESS)
174*bf6873c5SCy Schubert         goto done;
175*bf6873c5SCy Schubert     pamret =
176*bf6873c5SCy Schubert         pamk5_cache_init(args, cache_name, creds, &args->config->ctx->cache);
177*bf6873c5SCy Schubert     if (pamret != PAM_SUCCESS)
178*bf6873c5SCy Schubert         goto done;
179*bf6873c5SCy Schubert     putil_debug(args, "temporarily storing credentials in %s", cache_name);
180*bf6873c5SCy Schubert     pamret = pamk5_set_krb5ccname(args, cache_name, "PAM_KRB5CCNAME");
181*bf6873c5SCy Schubert 
182*bf6873c5SCy Schubert done:
183*bf6873c5SCy Schubert     free(cache_name);
184*bf6873c5SCy Schubert     return pamret;
185*bf6873c5SCy Schubert }
186