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