copy_addrs.c fwd_tgt.c mk_cred.c sendauth.c: Added FAR declarations
[krb5.git] / src / lib / krb5 / krb / mk_cred.c
1 /* 
2  * NAME
3  *    cred.c
4  * 
5  * DESCRIPTION
6  *    Provide an interface to assemble and disassemble krb5_cred
7  *    structures.
8  *
9  */
10 #include <k5-int.h>
11 #include "cleanup.h"
12 #include "auth_con.h"
13
14 #include <stddef.h>           /* NULL */
15 #include <stdlib.h>           /* malloc */
16 #include <errno.h>            /* ENOMEM */
17
18 /*-------------------- encrypt_credencpart --------------------*/
19
20 /*
21  * encrypt the enc_part of krb5_cred
22  */
23 static krb5_error_code 
24 encrypt_credencpart(context, pcredpart, pkeyblock, pencdata)
25     krb5_context          context;
26     krb5_cred_enc_part  * pcredpart;
27     krb5_keyblock       * pkeyblock;
28     krb5_enc_data       * pencdata;
29 {
30     krb5_error_code       retval;
31     krb5_encrypt_block    eblock;
32     krb5_data           * scratch;
33
34     if (pkeyblock && !valid_enctype(pkeyblock->enctype))
35         return KRB5_PROG_ETYPE_NOSUPP;
36
37     /* start by encoding to-be-encrypted part of the message */
38     if ((retval = encode_krb5_enc_cred_part(pcredpart, &scratch)))
39         return retval;
40
41     /*
42      * If the keyblock is NULL, just copy the data from the encoded
43      * data to the ciphertext area.
44      */
45     if (pkeyblock == NULL) {
46             pencdata->ciphertext.data = scratch->data;
47             pencdata->ciphertext.length = scratch->length;
48             krb5_xfree(scratch);
49             return 0;
50     }
51
52     /* put together an eblock for this encryption */
53
54     pencdata->kvno = 0;
55     pencdata->enctype = pkeyblock->enctype;
56
57     krb5_use_enctype(context, &eblock, pkeyblock->enctype);
58     pencdata->ciphertext.length = krb5_encrypt_size(scratch->length, 
59                                                     eblock.crypto_entry);
60
61     /* add padding area, and zero it */
62     if (!(scratch->data = (char *)realloc(scratch->data,
63                                           pencdata->ciphertext.length))) {
64         /* may destroy scratch->data */
65         krb5_xfree(scratch);
66         return ENOMEM;
67     }
68
69     memset(scratch->data + scratch->length, 0,
70            pencdata->ciphertext.length - scratch->length);
71     if (!(pencdata->ciphertext.data =
72           (char *)malloc(pencdata->ciphertext.length))) {
73         retval = ENOMEM;
74         goto clean_scratch;
75     }
76
77     /* do any necessary key pre-processing */
78     if ((retval = krb5_process_key(context, &eblock, pkeyblock))) {
79         goto clean_encpart;
80     }
81
82     /* call the encryption routine */
83     if ((retval = krb5_encrypt(context, (krb5_pointer)scratch->data,
84                                (krb5_pointer)pencdata->ciphertext.data, 
85                                scratch->length, &eblock, 0))) {
86         krb5_finish_key(context, &eblock);
87         goto clean_encpart;
88     }
89     
90     retval = krb5_finish_key(context, &eblock);
91
92 clean_encpart:
93     if (retval) {
94         memset(pencdata->ciphertext.data, 0, pencdata->ciphertext.length);
95         free(pencdata->ciphertext.data);
96         pencdata->ciphertext.length = 0;
97         pencdata->ciphertext.data = 0;
98     }
99
100 clean_scratch:
101     memset(scratch->data, 0, scratch->length); 
102     krb5_free_data(context, scratch);
103
104     return retval;
105 }
106
107 /*----------------------- krb5_mk_ncred_basic -----------------------*/
108
109 static krb5_error_code
110 krb5_mk_ncred_basic(context, ppcreds, nppcreds, keyblock,                 
111                     replaydata, local_addr, remote_addr, pcred)
112     krb5_context        context;
113     krb5_creds          FAR * FAR * ppcreds;
114     krb5_int32          nppcreds;
115     krb5_keyblock       FAR * keyblock;
116     krb5_replay_data    FAR * replaydata;
117     krb5_address        FAR * local_addr;
118     krb5_address        FAR * remote_addr;
119     krb5_cred           FAR * pcred;
120 {
121     krb5_cred_enc_part    credenc;
122     krb5_error_code       retval;
123     size_t                size;
124     int                   i;
125
126     credenc.magic = KV5M_CRED_ENC_PART;
127
128     credenc.s_address = 0;
129     credenc.r_address = 0;
130     if (local_addr) krb5_copy_addr(context, local_addr, &credenc.s_address);
131     if (remote_addr) krb5_copy_addr(context, remote_addr, &credenc.r_address);
132
133     credenc.nonce = replaydata->seq;
134     credenc.usec = replaydata->usec;
135     credenc.timestamp = replaydata->timestamp;
136
137     /* Get memory for creds and initialize it */
138     size = sizeof(krb5_cred_info FAR *) * (nppcreds + 1);
139     credenc.ticket_info = (krb5_cred_info FAR * FAR *) malloc(size);
140     if (credenc.ticket_info == NULL)
141         return ENOMEM;
142     memset(credenc.ticket_info, 0, size);
143     
144     /*
145      * For each credential in the list, initialize a cred info
146      * structure and copy the ticket into the ticket list.
147      */
148     for (i = 0; i < nppcreds; i++) {
149         credenc.ticket_info[i] = malloc(sizeof(krb5_cred_info));
150         if (credenc.ticket_info[i] == NULL) {
151             retval = ENOMEM;
152             goto cleanup;
153         }
154         credenc.ticket_info[i+1] = NULL;
155         
156         credenc.ticket_info[i]->magic = KV5M_CRED_INFO;
157         credenc.ticket_info[i]->times = ppcreds[i]->times;
158         credenc.ticket_info[i]->flags = ppcreds[i]->ticket_flags;
159
160         if ((retval = decode_krb5_ticket(&ppcreds[i]->ticket, 
161                                          &pcred->tickets[i])))
162             goto cleanup;
163
164         if ((retval = krb5_copy_keyblock(context, &ppcreds[i]->keyblock,
165                                          &credenc.ticket_info[i]->session)))
166             goto cleanup;
167
168         if ((retval = krb5_copy_principal(context, ppcreds[i]->client,
169                                           &credenc.ticket_info[i]->client)))
170             goto cleanup;
171
172         if ((retval = krb5_copy_principal(context, ppcreds[i]->server,
173                                           &credenc.ticket_info[i]->server)))
174             goto cleanup;
175
176         if ((retval = krb5_copy_addresses(context, ppcreds[i]->addresses,
177                                           &credenc.ticket_info[i]->caddrs)))
178             goto cleanup;
179     }
180
181     /*
182      * NULL terminate the lists.
183      */
184     pcred->tickets[i] = NULL;
185
186     /* encrypt the credential encrypted part */
187     retval = encrypt_credencpart(context, &credenc, keyblock,
188                                  &pcred->enc_part);
189
190 cleanup:
191     krb5_free_cred_enc_part(context, &credenc);
192     return retval;
193 }
194
195 /*----------------------- krb5_mk_ncred -----------------------*/
196
197 /*
198  * This functions takes as input an array of krb5_credentials, and
199  * outputs an encoded KRB_CRED message suitable for krb5_rd_cred
200  */
201 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
202 krb5_mk_ncred(context, auth_context, ppcreds, ppdata, outdata)
203
204     krb5_context          context;
205     krb5_auth_context     auth_context;
206     krb5_creds         FAR * FAR * ppcreds;
207     krb5_data          FAR * FAR * ppdata;
208     krb5_replay_data    FAR * outdata;
209 {
210     krb5_address FAR * premote_fulladdr = NULL;
211     krb5_address FAR * plocal_fulladdr = NULL;
212     krb5_address remote_fulladdr;
213     krb5_address local_fulladdr;
214     krb5_error_code     retval;
215     krb5_keyblock       FAR * keyblock;
216     krb5_replay_data    replaydata;
217     krb5_cred           FAR * pcred;
218     krb5_int32          ncred;
219
220     local_fulladdr.contents = 0;
221     remote_fulladdr.contents = 0;
222     memset(&replaydata, 0, sizeof(krb5_replay_data));
223
224     if (ppcreds == NULL) {
225         return KRB5KRB_AP_ERR_BADADDR;
226     }
227
228     /*
229      * Allocate memory for a NULL terminated list of tickets.
230      */
231     for (ncred = 0; ppcreds[ncred]; ncred++);
232
233     if ((pcred = (krb5_cred *)malloc(sizeof(krb5_cred))) == NULL) 
234         return ENOMEM;
235     memset(pcred, 0, sizeof(krb5_cred));
236
237     if ((pcred->tickets 
238       = (krb5_ticket FAR * FAR *)malloc(sizeof(krb5_ticket FAR *) * (ncred + 1))) == NULL) {
239         retval = ENOMEM;
240         free(pcred);
241     }
242     memset(pcred->tickets, 0, sizeof(krb5_ticket FAR *) * (ncred +1));
243
244     /* Get keyblock */
245     if ((keyblock = auth_context->local_subkey) == NULL) 
246         if ((keyblock = auth_context->remote_subkey) == NULL) 
247             keyblock = auth_context->keyblock;
248
249     /* Get replay info */
250     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
251       (auth_context->rcache == NULL))
252         return KRB5_RC_REQUIRED;
253
254     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
255       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
256       (outdata == NULL))
257         /* Need a better error */
258         return KRB5_RC_REQUIRED;
259
260     if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
261                                     &replaydata.usec)))
262         return retval;
263     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
264         outdata->timestamp = replaydata.timestamp;
265         outdata->usec = replaydata.usec;
266     }
267     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
268         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
269         replaydata.seq = auth_context->local_seq_number;
270         if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
271             auth_context->local_seq_number++;
272         } else {
273             outdata->seq = replaydata.seq;
274         }
275     }
276
277     if (auth_context->local_addr) {
278         if (auth_context->local_port) {
279             if ((retval = krb5_make_fulladdr(context, auth_context->local_addr,
280                                              auth_context->local_port, 
281                                              &local_fulladdr)))
282                 goto error;
283             plocal_fulladdr = &local_fulladdr;
284         } else {
285             plocal_fulladdr = auth_context->local_addr;
286         }
287     }
288
289     if (auth_context->remote_addr) {
290         if (auth_context->remote_port) {
291             if ((retval = krb5_make_fulladdr(context,auth_context->remote_addr,
292                                               auth_context->remote_port, 
293                                               &remote_fulladdr)))
294                 goto error;
295             premote_fulladdr = &remote_fulladdr;
296         } else {
297             premote_fulladdr = auth_context->remote_addr;
298         }
299     }
300
301     /* Setup creds structure */
302     if ((retval = krb5_mk_ncred_basic(context, ppcreds, ncred, keyblock,
303                                       &replaydata, plocal_fulladdr, 
304                                       premote_fulladdr, pcred))) {
305         goto error;
306     }
307
308     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
309         krb5_donot_replay replay;
310
311         if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
312                                            "_forw", &replay.client)))
313             goto error;
314
315         replay.server = "";             /* XXX */
316         replay.cusec = replaydata.usec;
317         replay.ctime = replaydata.timestamp;
318         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
319             /* should we really error out here? XXX */
320             krb5_xfree(replay.client);
321             goto error;
322         }
323         krb5_xfree(replay.client);
324     }
325
326     /* Encode creds structure */
327     retval = encode_krb5_cred(pcred, ppdata);
328
329 error:
330     if (local_fulladdr.contents)
331         free(local_fulladdr.contents);
332     if (remote_fulladdr.contents)
333         free(remote_fulladdr.contents);
334     krb5_free_cred(context, pcred);
335
336     if (retval) {
337         if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) 
338          || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
339             auth_context->local_seq_number--;
340     }
341     return retval;
342 }
343
344 /*----------------------- krb5_mk_1cred -----------------------*/
345
346 /*
347  * A convenience function that calls krb5_mk_ncred.
348  */
349 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
350 krb5_mk_1cred(context, auth_context, pcreds, ppdata, outdata)
351     krb5_context          context;
352     krb5_auth_context     auth_context;
353     krb5_creds          FAR * pcreds;
354     krb5_data          FAR * FAR * ppdata;
355     krb5_replay_data    FAR * outdata;
356 {
357     krb5_error_code retval;
358     krb5_creds FAR * FAR *ppcreds;
359
360     if ((ppcreds = (krb5_creds FAR * FAR *)malloc(sizeof(*ppcreds) * 2)) == NULL) {
361         return ENOMEM;
362     }
363
364     ppcreds[0] = pcreds;
365     ppcreds[1] = NULL;
366
367     retval = krb5_mk_ncred(context, auth_context, ppcreds,
368                            ppdata, outdata);
369     
370     free(ppcreds);
371     return retval;
372 }
373