This repository has been archived by the owner on Jun 5, 2018. It is now read-only.
forked from rra/pam-krb5
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.c
185 lines (164 loc) · 5.38 KB
/
cache.c
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
/*
* 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, 2014 Russ Allbery <[email protected]>
* Copyright 2005 Andres Salomon <[email protected]>
* Copyright 1999, 2000 Frank Cusack <[email protected]>
*
* 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:
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;
}