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 178 179 180 181 182 183 184 185 186
|
/*
* Ticket cache initialization.
*
* Provides functions for creating ticket caches, used by pam_authenticate,
* pam_setcred, and pam_chauthtok after changing an expired password.
*
* Copyright 2011, 2012
* The Board of Trustees of the Leland Stanford Junior University
* Copyright 2005, 2006, 2007, 2008, 2009 Russ Allbery <rra@stanford.edu>
* Copyright 2005 Andres Salomon <dilinger@debian.org>
* Copyright 1999, 2000 Frank Cusack <fcusack@fcusack.com>
*
* See LICENSE for licensing terms.
*/
#include <config.h>
#include <portable/krb5.h>
#include <portable/pam.h>
#include <portable/system.h>
#include <errno.h>
#include <internal.h>
#include <pam-util/args.h>
#include <pam-util/logging.h>
/*
* Get the name of a cache. Takes the name of the environment variable that
* should be set to indicate which cache to use, either the permanent cache
* (KRB5CCNAME) or the temporary cache (PAM_KRB5CCNAME).
*
* Treat an empty environment variable setting the same as if the variable
* was not set, since on FreeBSD we can't delete the environment variable,
* only set it to an empty value.
*/
const char *
pamk5_get_krb5ccname(struct pam_args *args, const char *key)
{
const char *name;
/* When refreshing a cache, we need to try the regular environment. */
name = pam_getenv(args->pamh, key);
if (name == NULL || *name == '\0')
name = getenv(key);
if (name == NULL || *name == '\0')
return NULL;
else
return name;
}
/*
* Put the ticket cache information into the environment. Takes the path and
* the environment variable to set, since this is used both for the permanent
* cache (KRB5CCNAME) and the temporary cache (PAM_KRB5CCNAME). Returns a PAM
* status code.
*/
int
pamk5_set_krb5ccname(struct pam_args *args, const char *name, const char *key)
{
char *env_name = NULL;
int pamret;
if (asprintf(&env_name, "%s=%s", key, name) < 0) {
putil_crit(args, "asprintf failed: %s", strerror(errno));
pamret = PAM_BUF_ERR;
goto done;
}
pamret = pam_putenv(args->pamh, env_name);
if (pamret != PAM_SUCCESS) {
putil_err_pam(args, pamret, "pam_putenv failed");
pamret = PAM_SERVICE_ERR;
goto done;
}
pamret = PAM_SUCCESS;
done:
if (env_name != NULL)
free(env_name);
return pamret;
}
/*
* Given the template for a ticket cache name, initialize that file securely
* mkstemp. Returns a PAM success or error code.
*/
int
pamk5_cache_mkstemp(struct pam_args *args, char *template)
{
int ccfd, oerrno;
ccfd = mkstemp(template);
if (ccfd < 0) {
oerrno = errno;
putil_crit(args, "mkstemp(\"%s\") failed: %s", template,
strerror(errno));
errno = oerrno;
return PAM_SERVICE_ERR;
}
close(ccfd);
return PAM_SUCCESS;
}
/*
* Given a cache name and the initial credentials, initialize the cache, store
* the credentials in that cache, and return a pointer to the new cache in the
* cache argument. Returns a PAM success or error code.
*/
int
pamk5_cache_init(struct pam_args *args, const char *ccname, krb5_creds *creds,
krb5_ccache *cache)
{
struct context *ctx;
int retval;
if (args == NULL || args->config == NULL || args->config->ctx == NULL
|| args->config->ctx->context == NULL)
return PAM_SERVICE_ERR;
ctx = args->config->ctx;
retval = krb5_cc_resolve(ctx->context, ccname, cache);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot resolve ticket cache %s", ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
retval = krb5_cc_initialize(ctx->context, *cache, ctx->princ);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot initialize ticket cache %s",
ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
retval = krb5_cc_store_cred(ctx->context, *cache, creds);
if (retval != 0) {
putil_err_krb5(args, retval, "cannot store credentials in %s", ccname);
retval = PAM_SERVICE_ERR;
goto done;
}
done:
if (retval != PAM_SUCCESS && *cache != NULL) {
krb5_cc_destroy(ctx->context, *cache);
*cache = NULL;
}
return retval;
}
/*
* Initialize an internal ticket cache with a random name, store the given
* credentials in the cache, and store the cache in the context. Put the path
* in PAM_KRB5CCNAME where it can be picked up later by pam_setcred. Returns
* a PAM success or error code.
*/
int
pamk5_cache_init_random(struct pam_args *args, krb5_creds *creds)
{
char *cache_name = NULL;
const char *dir;
int pamret;
/* Store the obtained credentials in a temporary cache. */
dir = args->config->ccache_dir;
if (strncmp("FILE:", args->config->ccache_dir, strlen("FILE:")) == 0)
dir += strlen("FILE:");
if (asprintf(&cache_name, "%s/krb5cc_pam_XXXXXX", dir) < 0) {
putil_crit(args, "malloc failure: %s", strerror(errno));
return PAM_SERVICE_ERR;
}
pamret = pamk5_cache_mkstemp(args, cache_name);
if (pamret != PAM_SUCCESS)
goto done;
pamret = pamk5_cache_init(args, cache_name, creds,
&args->config->ctx->cache);
if (pamret != PAM_SUCCESS)
goto done;
putil_debug(args, "temporarily storing credentials in %s", cache_name);
pamret = pamk5_set_krb5ccname(args, cache_name, "PAM_KRB5CCNAME");
done:
free(cache_name);
return pamret;
}
|