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 */
30 static krb5_error_code
encrypt_credencpart(krb5_context context,krb5_cred_enc_part * pcredpart,krb5_keyblock * pkeyblock,krb5_enc_data * pencdata)31 encrypt_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
71 static krb5_error_code
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)72 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)
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
143 cleanup:
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 */
154 krb5_error_code KRB5_CALLCONV
krb5_mk_ncred(krb5_context context,krb5_auth_context auth_context,krb5_creds ** ppcreds,krb5_data ** ppdata,krb5_replay_data * outdata)155 krb5_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
275 error:
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 */
295 krb5_error_code KRB5_CALLCONV
krb5_mk_1cred(krb5_context context,krb5_auth_context auth_context,krb5_creds * pcreds,krb5_data ** ppdata,krb5_replay_data * outdata)296 krb5_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