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