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