source: trunk/server/source4/auth/credentials/credentials_krb5.c@ 745

Last change on this file since 745 was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 23.1 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Handle user credentials (as regards krb5)
5
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <[email protected]> 2005
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "includes.h"
25#include "system/kerberos.h"
26#include "auth/kerberos/kerberos.h"
27#include "auth/credentials/credentials.h"
28#include "auth/credentials/credentials_proto.h"
29#include "auth/credentials/credentials_krb5.h"
30#include "auth/kerberos/kerberos_credentials.h"
31#include "auth/kerberos/kerberos_util.h"
32#include "param/param.h"
33
34_PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
35 struct loadparm_context *lp_ctx,
36 struct smb_krb5_context **smb_krb5_context)
37{
38 int ret;
39 if (cred->smb_krb5_context) {
40 *smb_krb5_context = cred->smb_krb5_context;
41 return 0;
42 }
43
44 ret = smb_krb5_init_context(cred, NULL, lp_ctx,
45 &cred->smb_krb5_context);
46 if (ret) {
47 cred->smb_krb5_context = NULL;
48 return ret;
49 }
50 *smb_krb5_context = cred->smb_krb5_context;
51 return 0;
52}
53
54/* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
55 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
56 */
57_PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
58 struct smb_krb5_context *smb_krb5_context)
59{
60 if (smb_krb5_context == NULL) {
61 talloc_unlink(cred, cred->smb_krb5_context);
62 cred->smb_krb5_context = NULL;
63 return NT_STATUS_OK;
64 }
65
66 if (!talloc_reference(cred, smb_krb5_context)) {
67 return NT_STATUS_NO_MEMORY;
68 }
69 cred->smb_krb5_context = smb_krb5_context;
70 return NT_STATUS_OK;
71}
72
73static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
74 struct ccache_container *ccache,
75 enum credentials_obtained obtained,
76 const char **error_string)
77{
78
79 krb5_principal princ;
80 krb5_error_code ret;
81 char *name;
82
83 if (cred->ccache_obtained > obtained) {
84 return 0;
85 }
86
87 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
88 ccache->ccache, &princ);
89
90 if (ret) {
91 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
92 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
93 ret, cred));
94 return ret;
95 }
96
97 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
98 if (ret) {
99 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
100 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
101 ret, cred));
102 return ret;
103 }
104
105 cli_credentials_set_principal(cred, name, obtained);
106
107 free(name);
108
109 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
110
111 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
112 cred->ccache_obtained = obtained;
113
114 return 0;
115}
116
117/* Free a memory ccache */
118static int free_mccache(struct ccache_container *ccc)
119{
120 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
121
122 return 0;
123}
124
125/* Free a disk-based ccache */
126static int free_dccache(struct ccache_container *ccc) {
127 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
128
129 return 0;
130}
131
132_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
133 struct loadparm_context *lp_ctx,
134 const char *name,
135 enum credentials_obtained obtained,
136 const char **error_string)
137{
138 krb5_error_code ret;
139 krb5_principal princ;
140 struct ccache_container *ccc;
141 if (cred->ccache_obtained > obtained) {
142 return 0;
143 }
144
145 ccc = talloc(cred, struct ccache_container);
146 if (!ccc) {
147 (*error_string) = error_message(ENOMEM);
148 return ENOMEM;
149 }
150
151 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
152 &ccc->smb_krb5_context);
153 if (ret) {
154 (*error_string) = error_message(ret);
155 talloc_free(ccc);
156 return ret;
157 }
158 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
159 talloc_free(ccc);
160 (*error_string) = error_message(ENOMEM);
161 return ENOMEM;
162 }
163
164 if (name) {
165 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
166 if (ret) {
167 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
168 name,
169 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
170 ret, ccc));
171 talloc_free(ccc);
172 return ret;
173 }
174 } else {
175 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
176 if (ret) {
177 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
178 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
179 ret, ccc));
180 talloc_free(ccc);
181 return ret;
182 }
183 }
184
185 talloc_set_destructor(ccc, free_dccache);
186
187 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
188
189 if (ret == 0) {
190 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
191 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
192
193 if (ret) {
194 (*error_string) = error_message(ret);
195 return ret;
196 }
197
198 cred->ccache = ccc;
199 cred->ccache_obtained = obtained;
200 talloc_steal(cred, ccc);
201
202 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
203 return 0;
204 }
205 return 0;
206}
207
208
209static int cli_credentials_new_ccache(struct cli_credentials *cred,
210 struct loadparm_context *lp_ctx,
211 char *ccache_name,
212 struct ccache_container **_ccc,
213 const char **error_string)
214{
215 bool must_free_cc_name = false;
216 krb5_error_code ret;
217 struct ccache_container *ccc = talloc(cred, struct ccache_container);
218 if (!ccc) {
219 return ENOMEM;
220 }
221
222 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
223 &ccc->smb_krb5_context);
224 if (ret) {
225 talloc_free(ccc);
226 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
227 error_message(ret));
228 return ret;
229 }
230 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
231 talloc_free(ccc);
232 (*error_string) = strerror(ENOMEM);
233 return ENOMEM;
234 }
235
236 if (!ccache_name) {
237 must_free_cc_name = true;
238 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
239 ccc);
240
241 if (!ccache_name) {
242 talloc_free(ccc);
243 (*error_string) = strerror(ENOMEM);
244 return ENOMEM;
245 }
246 }
247
248 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
249 &ccc->ccache);
250 if (ret) {
251 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
252 ccache_name,
253 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
254 ret, ccc));
255 talloc_free(ccache_name);
256 talloc_free(ccc);
257 return ret;
258 }
259
260 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
261 talloc_set_destructor(ccc, free_mccache);
262 } else {
263 talloc_set_destructor(ccc, free_dccache);
264 }
265
266 if (must_free_cc_name) {
267 talloc_free(ccache_name);
268 }
269
270 *_ccc = ccc;
271
272 return 0;
273}
274
275_PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
276 struct tevent_context *event_ctx,
277 struct loadparm_context *lp_ctx,
278 char *ccache_name,
279 struct ccache_container **ccc,
280 const char **error_string)
281{
282 krb5_error_code ret;
283 enum credentials_obtained obtained;
284
285 if (cred->machine_account_pending) {
286 cli_credentials_set_machine_account(cred, lp_ctx);
287 }
288
289 if (cred->ccache_obtained >= cred->ccache_threshold &&
290 cred->ccache_obtained > CRED_UNINITIALISED) {
291 *ccc = cred->ccache;
292 return 0;
293 }
294 if (cli_credentials_is_anonymous(cred)) {
295 (*error_string) = "Cannot get anonymous kerberos credentials";
296 return EINVAL;
297 }
298
299 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
300 if (ret) {
301 return ret;
302 }
303
304 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
305 if (ret) {
306 return ret;
307 }
308
309 ret = cli_credentials_set_from_ccache(cred, *ccc,
310 obtained, error_string);
311
312 cred->ccache = *ccc;
313 cred->ccache_obtained = cred->principal_obtained;
314 if (ret) {
315 return ret;
316 }
317 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
318 return 0;
319}
320
321_PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
322 struct tevent_context *event_ctx,
323 struct loadparm_context *lp_ctx,
324 struct ccache_container **ccc,
325 const char **error_string)
326{
327 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
328}
329
330/* We have good reason to think the ccache in these credentials is invalid - blow it away */
331static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
332{
333 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
334 talloc_unlink(cred, cred->client_gss_creds);
335 cred->client_gss_creds = NULL;
336 }
337 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
338}
339