Convert DEBUG_REFERRALS to TRACE_* framework
[krb5.git] / src / lib / krb5 / krb / rd_cred.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/rd_cred.c - definition of krb5_rd_cred() */
3 /*
4  * Copyright 1994-2009 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 #include "k5-int.h"
28 #include "cleanup.h"
29 #include "auth_con.h"
30
31 #include <stddef.h>           /* NULL */
32 #include <stdlib.h>           /* malloc */
33 #include <errno.h>            /* ENOMEM */
34
35 /*-------------------- decrypt_credencdata --------------------*/
36
37 /*
38  * decrypt the enc_part of a krb5_cred
39  */
40 static krb5_error_code
41 decrypt_credencdata(krb5_context context, krb5_cred *pcred,
42                     krb5_key pkey, krb5_cred_enc_part *pcredenc)
43 {
44     krb5_cred_enc_part  * ppart = NULL;
45     krb5_error_code       retval = 0;
46     krb5_data             scratch;
47
48     scratch.length = pcred->enc_part.ciphertext.length;
49     if (!(scratch.data = (char *)malloc(scratch.length)))
50         return ENOMEM;
51
52     if (pkey != NULL) {
53         if ((retval = krb5_k_decrypt(context, pkey,
54                                      KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0,
55                                      &pcred->enc_part, &scratch)))
56             goto cleanup;
57     } else {
58         memcpy(scratch.data, pcred->enc_part.ciphertext.data, scratch.length);
59     }
60
61     /*  now decode the decrypted stuff */
62     if ((retval = decode_krb5_enc_cred_part(&scratch, &ppart)))
63         goto cleanup;
64
65     *pcredenc = *ppart;
66
67 cleanup:
68     if (ppart != NULL) {
69         memset(ppart, 0, sizeof(*ppart));
70         free(ppart);
71     }
72     memset(scratch.data, 0, scratch.length);
73     free(scratch.data);
74
75     return retval;
76 }
77 /*----------------------- krb5_rd_cred_basic -----------------------*/
78
79 static krb5_error_code
80 krb5_rd_cred_basic(krb5_context context, krb5_data *pcreddata,
81                    krb5_key pkey, krb5_replay_data *replaydata,
82                    krb5_creds ***pppcreds)
83 {
84     krb5_error_code       retval = 0;
85     krb5_cred           * pcred = NULL;
86     krb5_int32            ncreds = 0;
87     krb5_int32            i = 0;
88     krb5_cred_enc_part    encpart;
89
90     /* decode cred message */
91     if ((retval = decode_krb5_cred(pcreddata, &pcred)))
92         return retval;
93
94     memset(&encpart, 0, sizeof(encpart));
95
96     if ((retval = decrypt_credencdata(context, pcred, pkey, &encpart)))
97         goto cleanup_cred;
98
99
100     replaydata->timestamp = encpart.timestamp;
101     replaydata->usec = encpart.usec;
102     replaydata->seq = encpart.nonce;
103
104     /*
105      * Allocate the list of creds.  The memory is allocated so that
106      * krb5_free_tgt_creds can be used to free the list.
107      */
108     for (ncreds = 0; pcred->tickets[ncreds]; ncreds++);
109
110     if ((*pppcreds =
111          (krb5_creds **)malloc((size_t)(sizeof(krb5_creds *) *
112                                         (ncreds + 1)))) == NULL) {
113         retval = ENOMEM;
114         goto cleanup_cred;
115     }
116     (*pppcreds)[0] = NULL;
117
118     /*
119      * For each credential, create a strcture in the list of
120      * credentials and copy the information.
121      */
122     while (i < ncreds) {
123         krb5_cred_info  * pinfo;
124         krb5_creds      * pcur;
125         krb5_data       * pdata;
126
127         if ((pcur = (krb5_creds *)calloc(1, sizeof(krb5_creds))) == NULL) {
128             retval = ENOMEM;
129             goto cleanup;
130         }
131
132         (*pppcreds)[i] = pcur;
133         (*pppcreds)[i+1] = 0;
134         pinfo = encpart.ticket_info[i++];
135
136         if ((retval = krb5_copy_principal(context, pinfo->client,
137                                           &pcur->client)))
138             goto cleanup;
139
140         if ((retval = krb5_copy_principal(context, pinfo->server,
141                                           &pcur->server)))
142             goto cleanup;
143
144         if ((retval = krb5_copy_keyblock_contents(context, pinfo->session,
145                                                   &pcur->keyblock)))
146             goto cleanup;
147
148         if ((retval = krb5_copy_addresses(context, pinfo->caddrs,
149                                           &pcur->addresses)))
150             goto cleanup;
151
152         if ((retval = encode_krb5_ticket(pcred->tickets[i - 1], &pdata)))
153             goto cleanup;
154
155         pcur->ticket = *pdata;
156         free(pdata);
157
158
159         pcur->is_skey = FALSE;
160         pcur->magic = KV5M_CREDS;
161         pcur->times = pinfo->times;
162         pcur->ticket_flags = pinfo->flags;
163         pcur->authdata = NULL;   /* not used */
164         memset(&pcur->second_ticket, 0, sizeof(pcur->second_ticket));
165     }
166
167     /*
168      * NULL terminate the list
169      */
170     (*pppcreds)[i] = NULL;
171
172 cleanup:
173     if (retval)
174         krb5_free_tgt_creds(context, *pppcreds);
175
176 cleanup_cred:
177     krb5_free_cred(context, pcred);
178     krb5_free_cred_enc_part(context, &encpart);
179
180     return retval;
181 }
182
183 /*----------------------- krb5_rd_cred -----------------------*/
184
185 /*
186  * This functions takes as input an KRB_CRED message, validates it, and
187  * outputs the array of the forwarded credentials and replay cache information
188  */
189 krb5_error_code KRB5_CALLCONV
190 krb5_rd_cred(krb5_context context, krb5_auth_context auth_context,
191              krb5_data *pcreddata, krb5_creds ***pppcreds,
192              krb5_replay_data *outdata)
193 {
194     krb5_error_code       retval = 0;
195     krb5_replay_data      replaydata;
196
197     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
198          (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
199         (outdata == NULL))
200         /* Need a better error */
201         return KRB5_RC_REQUIRED;
202
203     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
204         (auth_context->rcache == NULL))
205         return KRB5_RC_REQUIRED;
206
207     /*
208      * If decrypting with the subsession key fails, perhaps the
209      * credentials are stored in the session key so try decrypting with that.
210      */
211     if (auth_context->recv_subkey == NULL ||
212         (retval = krb5_rd_cred_basic(context, pcreddata,
213                                      auth_context->recv_subkey,
214                                      &replaydata, pppcreds))) {
215         retval = krb5_rd_cred_basic(context, pcreddata,
216                                     auth_context->key,
217                                     &replaydata, pppcreds);
218         if (retval)
219             return retval;
220     }
221
222     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
223         krb5_donot_replay replay;
224
225         if ((retval = krb5_check_clockskew(context, replaydata.timestamp)))
226             goto error;
227
228         if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
229                                            "_forw", &replay.client)))
230             goto error;
231
232         replay.server = "";             /* XXX */
233         replay.msghash = NULL;
234         replay.cusec = replaydata.usec;
235         replay.ctime = replaydata.timestamp;
236         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
237             free(replay.client);
238             goto error;
239         }
240         free(replay.client);
241     }
242
243     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
244         if (auth_context->remote_seq_number != replaydata.seq) {
245             retval =  KRB5KRB_AP_ERR_BADORDER;
246             goto error;
247         }
248         auth_context->remote_seq_number++;
249     }
250
251     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
252         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
253         outdata->timestamp = replaydata.timestamp;
254         outdata->usec = replaydata.usec;
255         outdata->seq = replaydata.seq;
256     }
257
258 error:;
259     if (retval) {
260         krb5_free_tgt_creds(context, *pppcreds);
261         *pppcreds = NULL;
262     }
263     return retval;
264 }