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