xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/ccapi_util.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccapi_util.c - conversion functions for CCAPI creds */
3 /*
4  * Copyright (C) 2022 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "cc-int.h"
34 #include "ccapi_util.h"
35 
36 #if defined(USE_CCAPI) || defined(USE_CCAPI_MACOS)
37 
38 static void
free_cc_data_list(cc_data ** list)39 free_cc_data_list(cc_data **list)
40 {
41     size_t i;
42 
43     for (i = 0; list != NULL && list[i] != NULL; i++) {
44         free(list[i]->data);
45         free(list[i]);
46     }
47     free(list);
48 }
49 
50 static krb5_error_code
cc_data_list_to_addresses(krb5_context context,cc_data ** list,krb5_address *** addrs_out)51 cc_data_list_to_addresses(krb5_context context, cc_data **list,
52                           krb5_address ***addrs_out)
53 {
54     krb5_error_code ret;
55     size_t count, i;
56     krb5_address **addrs = NULL;
57 
58     *addrs_out = NULL;
59     if (list == NULL)
60         return 0;
61 
62     for (count = 0; list[count]; count++);
63     addrs = k5calloc(count + 1, sizeof(*addrs), &ret);
64     if (addrs == NULL)
65         return ret;
66 
67     for (i = 0; i < count; i++) {
68         addrs[i] = k5alloc(sizeof(*addrs[i]), &ret);
69         if (addrs[i] == NULL)
70             goto cleanup;
71 
72         addrs[i]->contents = k5memdup(list[i]->data, list[i]->length, &ret);
73         if (addrs[i]->contents == NULL)
74             goto cleanup;
75         addrs[i]->length = list[i]->length;
76         addrs[i]->addrtype = list[i]->type;
77         addrs[i]->magic = KV5M_ADDRESS;
78     }
79 
80     *addrs_out = addrs;
81     addrs = NULL;
82 
83 cleanup:
84     krb5_free_addresses(context, addrs);
85     return ret;
86 }
87 
88 static krb5_error_code
cc_data_list_to_authdata(krb5_context context,cc_data ** list,krb5_authdata *** authdata_out)89 cc_data_list_to_authdata(krb5_context context, cc_data **list,
90                          krb5_authdata ***authdata_out)
91 {
92     krb5_error_code ret;
93     size_t count, i;
94     krb5_authdata **authdata = NULL;
95 
96     *authdata_out = NULL;
97     if (list == NULL)
98         return 0;
99 
100     for (count = 0; list[count]; count++);
101     authdata = k5calloc(count + 1, sizeof(*authdata), &ret);
102     if (authdata == NULL)
103         return ret;
104 
105     for (i = 0; i < count; i++) {
106         authdata[i] = k5alloc(sizeof(*authdata[i]), &ret);
107         if (authdata[i] == NULL)
108             goto cleanup;
109 
110         authdata[i]->contents = k5memdup(list[i]->data, list[i]->length, &ret);
111         if (authdata[i]->contents == NULL)
112             goto cleanup;
113         authdata[i]->length = list[i]->length;
114         authdata[i]->ad_type = list[i]->type;
115         authdata[i]->magic = KV5M_AUTHDATA;
116     }
117 
118     *authdata_out = authdata;
119     authdata = NULL;
120 
121 cleanup:
122     krb5_free_authdata(context, authdata);
123     return ret;
124 }
125 
126 static krb5_error_code
addresses_to_cc_data_list(krb5_context context,krb5_address ** addrs,cc_data *** list_out)127 addresses_to_cc_data_list(krb5_context context, krb5_address **addrs,
128                           cc_data ***list_out)
129 {
130     krb5_error_code ret;
131     size_t count, i;
132     cc_data **list = NULL;
133 
134     *list_out = NULL;
135     if (addrs == NULL)
136         return 0;
137 
138     for (count = 0; addrs[count]; count++);
139     list = k5calloc(count + 1, sizeof(*list), &ret);
140     if (list == NULL)
141         return ret;
142 
143     for (i = 0; i < count; i++) {
144         list[i] = k5alloc(sizeof(*list[i]), &ret);
145         if (list[i] == NULL)
146             goto cleanup;
147 
148         list[i]->data = k5memdup(addrs[i]->contents, addrs[i]->length, &ret);
149         if (list[i]->data == NULL)
150             goto cleanup;
151         list[i]->length = addrs[i]->length;
152         list[i]->type = addrs[i]->addrtype;
153     }
154 
155     *list_out = list;
156     list = NULL;
157 
158 cleanup:
159     free_cc_data_list(list);
160     return ret;
161 }
162 
163 static krb5_error_code
authdata_to_cc_data_list(krb5_context context,krb5_authdata ** authdata,cc_data *** list_out)164 authdata_to_cc_data_list(krb5_context context, krb5_authdata **authdata,
165                          cc_data ***list_out)
166 {
167     krb5_error_code ret;
168     size_t count, i;
169     cc_data **list = NULL;
170 
171     *list_out = NULL;
172     if (authdata == NULL)
173         return 0;
174 
175     for (count = 0; authdata[count]; count++);
176     list = k5calloc(count + 1, sizeof(*list), &ret);
177     if (list == NULL)
178         return ret;
179 
180     for (i = 0; i < count; i++) {
181         list[i] = k5alloc(sizeof(*list[i]), &ret);
182         if (list[i] == NULL)
183             goto cleanup;
184 
185         list[i]->data = k5memdup(authdata[i]->contents, authdata[i]->length,
186                                  &ret);
187         if (list[i]->data == NULL)
188             goto cleanup;
189         list[i]->length = authdata[i]->length;
190         list[i]->type = authdata[i]->ad_type;
191     }
192 
193     *list_out = list;
194     list = NULL;
195 
196 cleanup:
197     free_cc_data_list(list);
198     return ret;
199 }
200 
201 krb5_error_code
k5_ccapi_to_krb5_creds(krb5_context context,const cc_credentials_union * ccapi_cred,krb5_creds * cred_out)202 k5_ccapi_to_krb5_creds(krb5_context context,
203                        const cc_credentials_union *ccapi_cred,
204                        krb5_creds *cred_out)
205 {
206     krb5_error_code ret;
207     cc_credentials_v5_t *cv5 = NULL;
208     krb5_principal client = NULL;
209     krb5_principal server = NULL;
210     char *ticket_data = NULL;
211     char *second_ticket_data = NULL;
212     uint8_t *keyblock_contents = NULL;
213     krb5_address **addresses = NULL;
214     krb5_authdata **authdata = NULL;
215 
216     if (ccapi_cred->version != cc_credentials_v5)
217         return KRB5_CC_NOT_KTYPE;
218 
219     cv5 = ccapi_cred->credentials.credentials_v5;
220 
221     ret = krb5_parse_name(context, cv5->client, &client);
222     if (ret)
223         goto cleanup;
224     ret = krb5_parse_name(context, cv5->server, &server);
225     if (ret)
226         goto cleanup;
227 
228     if (cv5->keyblock.length > 0) {
229         keyblock_contents = k5memdup(cv5->keyblock.data, cv5->keyblock.length,
230                                      &ret);
231         if (keyblock_contents == NULL)
232             goto cleanup;
233     }
234 
235     if (cv5->ticket.length > 0) {
236         ticket_data = k5memdup(cv5->ticket.data, cv5->ticket.length, &ret);
237         if (ticket_data == NULL)
238             goto cleanup;
239     }
240 
241     if (cv5->second_ticket.length > 0) {
242         second_ticket_data = k5memdup(cv5->second_ticket.data,
243                                       cv5->second_ticket.length, &ret);
244         if (second_ticket_data == NULL)
245             goto cleanup;
246     }
247 
248     ret = cc_data_list_to_addresses(context, cv5->addresses, &addresses);
249     if (ret)
250         goto cleanup;
251 
252     ret = cc_data_list_to_authdata(context, cv5->authdata, &authdata);
253     if (ret)
254         goto cleanup;
255 
256     cred_out->client = client;
257     cred_out->server = server;
258     client = server = NULL;
259 
260     cred_out->keyblock.magic = KV5M_KEYBLOCK;
261     cred_out->keyblock.enctype = cv5->keyblock.type;
262     cred_out->keyblock.length = cv5->keyblock.length;
263     cred_out->keyblock.contents = keyblock_contents;
264     keyblock_contents = NULL;
265 
266     cred_out->times.authtime = cv5->authtime;
267     cred_out->times.starttime = cv5->starttime;
268     cred_out->times.endtime = cv5->endtime;
269     cred_out->times.renew_till = cv5->renew_till;
270     cred_out->is_skey = cv5->is_skey;
271     cred_out->ticket_flags = cv5->ticket_flags;
272 
273     cred_out->ticket = make_data(ticket_data, cv5->ticket.length);
274     cred_out->second_ticket = make_data(second_ticket_data,
275                                         cv5->second_ticket.length);
276     ticket_data = second_ticket_data = NULL;
277 
278     cred_out->addresses = addresses;
279     addresses = NULL;
280 
281     cred_out->authdata = authdata;
282     authdata = NULL;
283 
284     cred_out->magic = KV5M_CREDS;
285 
286 cleanup:
287     krb5_free_principal(context, client);
288     krb5_free_principal(context, server);
289     krb5_free_addresses(context, addresses);
290     krb5_free_authdata(context, authdata);
291     free(keyblock_contents);
292     free(ticket_data);
293     free(second_ticket_data);
294     return ret;
295 }
296 
297 krb5_error_code
k5_krb5_to_ccapi_creds(krb5_context context,krb5_creds * cred,cc_credentials_union ** ccapi_cred_out)298 k5_krb5_to_ccapi_creds(krb5_context context, krb5_creds *cred,
299                        cc_credentials_union **ccapi_cred_out)
300 {
301     krb5_error_code ret;
302     cc_credentials_union *cred_union = NULL;
303     cc_credentials_v5_t *cv5 = NULL;
304     char *client = NULL, *server = NULL;
305     uint8_t *ticket_data = NULL, *second_ticket_data = NULL;
306     uint8_t *keyblock_data = NULL;
307     cc_data **addr_list = NULL, **authdata_list = NULL;
308 
309     cred_union = k5alloc(sizeof(*cred_union), &ret);
310     if (cred_union == NULL)
311         goto cleanup;
312 
313     cv5 = k5alloc(sizeof(*cv5), &ret);
314     if (cv5 == NULL)
315         goto cleanup;
316 
317     ret = krb5_unparse_name(context, cred->client, &client);
318     if (ret)
319         goto cleanup;
320     ret = krb5_unparse_name(context, cred->server, &server);
321     if (ret)
322         goto cleanup;
323 
324     if (cred->keyblock.length > 0) {
325         keyblock_data = k5memdup(cred->keyblock.contents,
326                                  cred->keyblock.length, &ret);
327         if (keyblock_data == NULL)
328             goto cleanup;
329     }
330 
331     if (cred->ticket.length > 0) {
332         ticket_data = k5memdup0(cred->ticket.data, cred->ticket.length, &ret);
333         if (ticket_data == NULL)
334             goto cleanup;
335     }
336 
337     if (cred->second_ticket.length > 0) {
338         second_ticket_data = k5memdup0(cred->second_ticket.data,
339                                        cred->second_ticket.length, &ret);
340         if (second_ticket_data == NULL)
341             goto cleanup;
342     }
343 
344     ret = addresses_to_cc_data_list(context, cred->addresses, &addr_list);
345     if (ret)
346         goto cleanup;
347 
348     ret = authdata_to_cc_data_list(context, cred->authdata, &authdata_list);
349     if (ret)
350         goto cleanup;
351 
352     cv5->client = client;
353     cv5->server = server;
354     client = server = NULL;
355 
356     cv5->keyblock.type = cred->keyblock.enctype;
357     cv5->keyblock.length = cred->keyblock.length;
358     cv5->keyblock.data = keyblock_data;
359     keyblock_data = NULL;
360 
361     cv5->authtime = cred->times.authtime;
362     cv5->starttime = cred->times.starttime;
363     cv5->endtime = cred->times.endtime;
364     cv5->renew_till = cred->times.renew_till;
365     cv5->is_skey = cred->is_skey;
366     cv5->ticket_flags = cred->ticket_flags;
367 
368     cv5->ticket.length = cred->ticket.length;
369     cv5->ticket.data = ticket_data;
370     cv5->second_ticket.length = cred->second_ticket.length;
371     cv5->second_ticket.data = second_ticket_data;
372     ticket_data = second_ticket_data = NULL;
373 
374     cv5->addresses = addr_list;
375     addr_list = NULL;
376 
377     cv5->authdata = authdata_list;
378     authdata_list = NULL;
379 
380     cred_union->version = cc_credentials_v5;
381     cred_union->credentials.credentials_v5 = cv5;
382     cv5 = NULL;
383 
384     *ccapi_cred_out = cred_union;
385     cred_union = NULL;
386 
387 cleanup:
388     free_cc_data_list(addr_list);
389     free_cc_data_list(authdata_list);
390     free(keyblock_data);
391     free(ticket_data);
392     free(second_ticket_data);
393     krb5_free_unparsed_name(context, client);
394     krb5_free_unparsed_name(context, server);
395     free(cv5);
396     free(cred_union);
397     return ret;
398 }
399 
400 void
k5_release_ccapi_cred(cc_credentials_union * ccapi_cred)401 k5_release_ccapi_cred(cc_credentials_union *ccapi_cred)
402 {
403     cc_credentials_v5_t *cv5;
404 
405     if (ccapi_cred == NULL)
406         return;
407     if (ccapi_cred->version != cc_credentials_v5)
408         return;
409     if (ccapi_cred->credentials.credentials_v5 == NULL)
410         return;
411 
412     cv5 = ccapi_cred->credentials.credentials_v5;
413 
414     free(cv5->client);
415     free(cv5->server);
416     free(cv5->keyblock.data);
417     free(cv5->ticket.data);
418     free(cv5->second_ticket.data);
419     free_cc_data_list(cv5->addresses);
420     free_cc_data_list(cv5->authdata);
421     free(cv5);
422     free(ccapi_cred);
423 }
424 
425 #endif /* defined(USE_CCAPI) */
426