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