1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * lib/krb5/krb/get_creds.c
9 *
10 * Copyright 1990 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
31 *
32 *
33 * krb5_get_credentials()
34 */
35
36
37
38 /*
39 Attempts to use the credentials cache or TGS exchange to get an additional
40 ticket for the
41 client identified by in_creds->client, the server identified by
42 in_creds->server, with options options, expiration date specified in
43 in_creds->times.endtime (0 means as long as possible), session key type
44 specified in in_creds->keyblock.enctype (if non-zero)
45
46 Any returned ticket and intermediate ticket-granting tickets are
47 stored in ccache.
48
49 returns errors from encryption routines, system errors
50 */
51
52 #include "k5-int.h"
53
54 /*ARGSUSED*/
55 static krb5_error_code
krb5_get_credentials_core(krb5_context context,krb5_flags options,krb5_creds * in_creds,krb5_creds * mcreds,krb5_flags * fields)56 krb5_get_credentials_core(krb5_context context, krb5_flags options,
57 krb5_creds *in_creds, krb5_creds *mcreds,
58 krb5_flags *fields)
59 {
60 /* Solaris Kerberos */
61 krb5_error_code ret = 0;
62
63 if (!in_creds || !in_creds->server || !in_creds->client)
64 return EINVAL;
65
66 memset((char *)mcreds, 0, sizeof(krb5_creds));
67 mcreds->magic = KV5M_CREDS;
68 /*
69 * Solaris Kerberos:
70 * Set endtime appropriately to make sure we do not rope in
71 * expired creds. If endtime is set to 0 (which it almost always
72 * is, courtesy memset/calloc) the krb5_cc_retrieve_cred() call in
73 * krb5_get_credentials() with KRB5_TC_MATCH_TIMES will
74 * succeed and return the expired cred.
75 *
76 * Hence, endtime below is set to "now" if in_creds->times.endtime
77 * is 0, so that krb5_cc_retrieve_cred fails and we get fresh creds,
78 * if necessary. But, if in_creds has a non-zero endtime, we honor it.
79 */
80 if (in_creds->times.endtime != 0)
81 mcreds->times.endtime = in_creds->times.endtime;
82 else
83 if ((ret = krb5_timeofday(context, &mcreds->times.endtime)) != 0)
84 return (ret);
85
86 ret = krb5_copy_keyblock_contents(context, &in_creds->keyblock,
87 &mcreds->keyblock);
88 if (ret)
89 return (ret);
90
91 mcreds->authdata = in_creds->authdata;
92 mcreds->server = in_creds->server;
93 mcreds->client = in_creds->client;
94
95 *fields = KRB5_TC_MATCH_TIMES /*XXX |KRB5_TC_MATCH_SKEY_TYPE */
96 | KRB5_TC_MATCH_AUTHDATA
97 | KRB5_TC_SUPPORTED_KTYPES;
98 if (mcreds->keyblock.enctype) {
99 krb5_enctype *ktypes;
100 int i;
101
102 *fields |= KRB5_TC_MATCH_KTYPE;
103 ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes);
104 for (i = 0; ktypes[i]; i++)
105 if (ktypes[i] == mcreds->keyblock.enctype)
106 break;
107 if (ktypes[i] == 0)
108 ret = KRB5_CC_NOT_KTYPE;
109 free (ktypes);
110 if (ret) {
111 krb5_free_keyblock_contents(context, &mcreds->keyblock);
112 return ret;
113 }
114 }
115 if (options & KRB5_GC_USER_USER) {
116 /* also match on identical 2nd tkt and tkt encrypted in a
117 session key */
118 *fields |= KRB5_TC_MATCH_2ND_TKT|KRB5_TC_MATCH_IS_SKEY;
119 mcreds->is_skey = TRUE;
120 mcreds->second_ticket = in_creds->second_ticket;
121 if (!in_creds->second_ticket.length) {
122 krb5_free_keyblock_contents(context, &mcreds->keyblock);
123 return KRB5_NO_2ND_TKT;
124 }
125 }
126
127 return 0;
128 }
129
130 krb5_error_code KRB5_CALLCONV
krb5_get_credentials(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)131 krb5_get_credentials(krb5_context context, krb5_flags options,
132 krb5_ccache ccache, krb5_creds *in_creds,
133 krb5_creds **out_creds)
134 {
135 krb5_error_code retval;
136 krb5_creds mcreds;
137 krb5_creds *ncreds;
138 krb5_creds **tgts;
139 krb5_flags fields;
140 int not_ktype;
141
142 retval = krb5_get_credentials_core(context, options,
143 in_creds,
144 &mcreds, &fields);
145
146 if (retval) return retval;
147
148 if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) {
149 krb5_free_keyblock_contents(context, &mcreds.keyblock);
150 return ENOMEM;
151 }
152
153 memset((char *)ncreds, 0, sizeof(krb5_creds));
154 ncreds->magic = KV5M_CREDS;
155
156 /* The caller is now responsible for cleaning up in_creds */
157 /* Solaris Kerberos */
158 if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds,
159 ncreds)) !=0) {
160 krb5_xfree(ncreds);
161 ncreds = in_creds;
162 } else {
163 *out_creds = ncreds;
164 }
165
166 if ((retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE)
167 || options & KRB5_GC_CACHED) {
168 krb5_free_keyblock_contents(context, &mcreds.keyblock);
169 return retval;
170 }
171
172 if (retval == KRB5_CC_NOT_KTYPE)
173 not_ktype = 1;
174 else
175 not_ktype = 0;
176
177 retval = krb5_get_cred_from_kdc(context, ccache, ncreds, out_creds, &tgts);
178 if (tgts) {
179 register int i = 0;
180 krb5_error_code rv2;
181 while (tgts[i]) {
182 /* Solaris Kerberos */
183 if ((rv2 = krb5_cc_store_cred(context, ccache, tgts[i])) != 0) {
184 retval = rv2;
185 break;
186 }
187 i++;
188 }
189 krb5_free_tgt_creds(context, tgts);
190 }
191 /*
192 * Translate KRB5_CC_NOTFOUND if we previously got
193 * KRB5_CC_NOT_KTYPE from krb5_cc_retrieve_cred(), in order to
194 * handle the case where there is no TGT in the ccache and the
195 * input enctype didn't match. This handling is necessary because
196 * some callers, such as GSSAPI, iterate through enctypes and
197 * KRB5_CC_NOTFOUND passed through from the
198 * krb5_get_cred_from_kdc() is semantically incorrect, since the
199 * actual failure was the non-existence of a ticket of the correct
200 * enctype rather than the missing TGT.
201 */
202 if ((retval == KRB5_CC_NOTFOUND || retval == KRB5_CC_NOT_KTYPE)
203 && not_ktype)
204 retval = KRB5_CC_NOT_KTYPE;
205
206 if (!retval) {
207 /* the purpose of the krb5_get_credentials call is to
208 * obtain a set of credentials for the caller. the
209 * krb5_cc_store_cred() call is to optimize performance
210 * for future calls. Ignore any errors, since the credentials
211 * are still valid even if we fail to store them in the cache.
212 */
213 /* Solaris Kerberos */
214 retval = krb5_cc_store_cred(context, ccache, *out_creds);
215 }
216
217 krb5_free_keyblock_contents(context, &mcreds.keyblock);
218 return retval;
219 }
220
221 #define INT_GC_VALIDATE 1
222 #define INT_GC_RENEW 2
223
224 /*ARGSUSED*/
225 static krb5_error_code
krb5_get_credentials_val_renew_core(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds,int which)226 krb5_get_credentials_val_renew_core(krb5_context context, krb5_flags options,
227 krb5_ccache ccache, krb5_creds *in_creds,
228 krb5_creds **out_creds, int which)
229 {
230 krb5_error_code retval;
231 krb5_principal tmp;
232 krb5_creds **tgts = 0;
233
234 switch(which) {
235 case INT_GC_VALIDATE:
236 retval = krb5_get_cred_from_kdc_validate(context, ccache,
237 in_creds, out_creds, &tgts);
238 break;
239 case INT_GC_RENEW:
240 retval = krb5_get_cred_from_kdc_renew(context, ccache,
241 in_creds, out_creds, &tgts);
242 break;
243 default:
244 /* Should never happen */
245 retval = 255;
246 break;
247 }
248 if (retval) return retval;
249 if (tgts) krb5_free_tgt_creds(context, tgts);
250
251 retval = krb5_cc_get_principal(context, ccache, &tmp);
252 if (retval) return retval;
253
254 retval = krb5_cc_initialize(context, ccache, tmp);
255 /* Solaris Kerberos */
256 if (retval) {
257 krb5_free_principal(context, tmp);
258 return retval;
259 }
260
261 retval = krb5_cc_store_cred(context, ccache, *out_creds);
262 krb5_free_principal(context, tmp);
263 return retval;
264 }
265
266 krb5_error_code KRB5_CALLCONV
krb5_get_credentials_validate(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)267 krb5_get_credentials_validate(krb5_context context, krb5_flags options,
268 krb5_ccache ccache, krb5_creds *in_creds,
269 krb5_creds **out_creds)
270 {
271 return(krb5_get_credentials_val_renew_core(context, options, ccache,
272 in_creds, out_creds,
273 INT_GC_VALIDATE));
274 }
275
276 krb5_error_code KRB5_CALLCONV
krb5_get_credentials_renew(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)277 krb5_get_credentials_renew(krb5_context context, krb5_flags options,
278 krb5_ccache ccache, krb5_creds *in_creds,
279 krb5_creds **out_creds)
280 {
281
282 return(krb5_get_credentials_val_renew_core(context, options, ccache,
283 in_creds, out_creds,
284 INT_GC_RENEW));
285 }
286
287 static krb5_error_code
krb5_validate_or_renew_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_ccache ccache,char * in_tkt_service,int validate)288 krb5_validate_or_renew_creds(krb5_context context, krb5_creds *creds,
289 krb5_principal client, krb5_ccache ccache,
290 char *in_tkt_service, int validate)
291 {
292 krb5_error_code ret;
293 krb5_creds in_creds; /* only client and server need to be filled in */
294 krb5_creds *out_creds = 0; /* for check before dereferencing below */
295 krb5_creds **tgts;
296
297 memset((char *)&in_creds, 0, sizeof(krb5_creds));
298
299 in_creds.server = NULL;
300 tgts = NULL;
301
302 in_creds.client = client;
303
304 if (in_tkt_service) {
305 /* this is ugly, because so are the data structures involved. I'm
306 in the library, so I'm going to manipulate the data structures
307 directly, otherwise, it will be worse. */
308
309 if ((ret = krb5_parse_name(context, in_tkt_service, &in_creds.server)))
310 goto cleanup;
311
312 /* stuff the client realm into the server principal.
313 realloc if necessary */
314 if (in_creds.server->realm.length < in_creds.client->realm.length)
315 if ((in_creds.server->realm.data =
316 (char *) realloc(in_creds.server->realm.data,
317 in_creds.client->realm.length)) == NULL) {
318 ret = ENOMEM;
319 goto cleanup;
320 }
321
322 in_creds.server->realm.length = in_creds.client->realm.length;
323 memcpy(in_creds.server->realm.data, in_creds.client->realm.data,
324 in_creds.client->realm.length);
325 } else {
326 if ((ret = krb5_build_principal_ext(context, &in_creds.server,
327 in_creds.client->realm.length,
328 in_creds.client->realm.data,
329 KRB5_TGS_NAME_SIZE,
330 KRB5_TGS_NAME,
331 in_creds.client->realm.length,
332 in_creds.client->realm.data,
333 0)))
334 goto cleanup;
335 }
336
337 if (validate)
338 ret = krb5_get_cred_from_kdc_validate(context, ccache,
339 &in_creds, &out_creds, &tgts);
340 else
341 ret = krb5_get_cred_from_kdc_renew(context, ccache,
342 &in_creds, &out_creds, &tgts);
343
344 /* ick. copy the struct contents, free the container */
345 if (out_creds) {
346 *creds = *out_creds;
347 krb5_xfree(out_creds);
348 }
349
350 cleanup:
351
352 if (in_creds.server)
353 krb5_free_principal(context, in_creds.server);
354 if (tgts)
355 krb5_free_tgt_creds(context, tgts);
356
357 return(ret);
358 }
359
360 krb5_error_code KRB5_CALLCONV
krb5_get_validated_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_ccache ccache,char * in_tkt_service)361 krb5_get_validated_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
362 {
363 return(krb5_validate_or_renew_creds(context, creds, client, ccache,
364 in_tkt_service, 1));
365 }
366
367 krb5_error_code KRB5_CALLCONV
krb5_get_renewed_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_ccache ccache,char * in_tkt_service)368 krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
369 {
370 return(krb5_validate_or_renew_creds(context, creds, client, ccache,
371 in_tkt_service, 0));
372 }
373