Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / ccmarshal.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccmarshal.c - Functions for serializing creds */
3 /*
4  * Copyright (C) 2014 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 /*
34  * This file implements marshalling and unmarshalling of krb5 credentials and
35  * principals in versions 1 through 4 of the FILE ccache format.  Version 4 is
36  * also used for the KEYRING ccache type.
37  *
38  * The FILE credential cache format uses fixed 16-bit or 32-bit representations
39  * of integers.  In versions 1 and 2 these are in host byte order; in later
40  * versions they are in big-endian byte order.  Variable-length fields are
41  * represented with a 32-bit length followed by the field value.  There is no
42  * type tagging; field representations are simply concatenated together.
43  *
44  * A psuedo-BNF grammar for the credential and principal formats is:
45  *
46  * credential ::=
47  *   client (principal)
48  *   server (principal)
49  *   keyblock (keyblock)
50  *   authtime (32 bits)
51  *   starttime (32 bits)
52  *   endtime (32 bits)
53  *   renew_till (32 bits)
54  *   is_skey (1 byte, 0 or 1)
55  *   ticket_flags (32 bits)
56  *   addresses (addresses)
57  *   authdata (authdata)
58  *   ticket (data)
59  *   second_ticket (data)
60  *
61  * principal ::=
62  *   name type (32 bits) [omitted in version 1]
63  *   count of components (32 bits) [includes realm in version 1]
64  *   realm (data)
65  *   component1 (data)
66  *   component2 (data)
67  *   ...
68  *
69  * keyblock ::=
70  *   enctype (16 bits) [repeated twice in version 3; see below]
71  *   data
72  *
73  * addresses ::=
74  *   count (32 bits)
75  *   address1
76  *   address2
77  *   ...
78  *
79  * address ::=
80  *   addrtype (16 bits)
81  *   data
82  *
83  * authdata ::=
84  *   count (32 bits)
85  *   authdata1
86  *   authdata2
87  *   ...
88  *
89  * authdata ::=
90  *   ad_type (16 bits)
91  *   data
92  *
93  * data ::=
94  *   length (32 bits)
95  *   value (length bytes)
96  *
97  * When version 3 was current (before release 1.0), the keyblock had separate
98  * key type and enctype fields, and both were recorded.  At present we record
99  * the enctype field twice when writing the version 3 format and ignore the
100  * second value when reading it.
101  */
102
103 #include "k5-input.h"
104 #include "cc-int.h"
105
106 /* Read a 16-bit integer in host byte order for versions 1 and 2, or in
107  * big-endian byte order for later versions.*/
108 static uint16_t
109 get16(struct k5input *in, int version)
110 {
111     return (version < 3) ? k5_input_get_uint16_n(in) :
112         k5_input_get_uint16_be(in);
113 }
114
115 /* Read a 32-bit integer in host byte order for versions 1 and 2, or in
116  * big-endian byte order for later versions.*/
117 static uint32_t
118 get32(struct k5input *in, int version)
119 {
120     return (version < 3) ? k5_input_get_uint32_n(in) :
121         k5_input_get_uint32_be(in);
122 }
123
124 /* Read a 32-bit length and make a copy of that many bytes.  Return NULL on
125  * error. */
126 static void *
127 get_len_bytes(struct k5input *in, int version, unsigned int *len_out)
128 {
129     krb5_error_code ret;
130     unsigned int len = get32(in, version);
131     const void *bytes = k5_input_get_bytes(in, len);
132     void *copy;
133
134     *len_out = 0;
135     if (bytes == NULL)
136         return NULL;
137     copy = k5memdup0(bytes, len, &ret);
138     if (copy == NULL) {
139         k5_input_set_status(in, ret);
140         return NULL;
141     }
142     *len_out = len;
143     return copy;
144 }
145
146 /* Like get_len_bytes, but put the result in data. */
147 static void
148 get_data(struct k5input *in, int version, krb5_data *data)
149 {
150     unsigned int len;
151     void *bytes = get_len_bytes(in, version, &len);
152
153     *data = (bytes == NULL) ? empty_data() : make_data(bytes, len);
154 }
155
156 static krb5_principal
157 unmarshal_princ(struct k5input *in, int version)
158 {
159     krb5_error_code ret;
160     krb5_principal princ;
161     uint32_t i, ncomps;
162
163     princ = k5alloc(sizeof(krb5_principal_data), &ret);
164     if (princ == NULL) {
165         k5_input_set_status(in, ret);
166         return NULL;
167     }
168     princ->magic = KV5M_PRINCIPAL;
169     /* Version 1 does not store the principal name type, and counts the realm
170      * in the number of components. */
171     princ->type = (version == 1) ? KRB5_NT_UNKNOWN : get32(in, version);
172     ncomps = get32(in, version);
173     if (version == 1)
174         ncomps--;
175     if (ncomps > in->len) {     /* Sanity check to avoid large allocations */
176         ret = EINVAL;
177         goto error;
178     }
179     if (ncomps != 0) {
180         princ->data = k5calloc(ncomps, sizeof(krb5_data), &ret);
181         if (princ->data == NULL)
182             goto error;
183         princ->length = ncomps;
184     }
185     get_data(in, version, &princ->realm);
186     for (i = 0; i < ncomps; i++)
187         get_data(in, version, &princ->data[i]);
188     return princ;
189
190 error:
191     k5_input_set_status(in, ret);
192     krb5_free_principal(NULL, princ);
193     return NULL;
194 }
195
196 static void
197 unmarshal_keyblock(struct k5input *in, int version, krb5_keyblock *kb)
198 {
199     memset(kb, 0, sizeof(*kb));
200     kb->magic = KV5M_KEYBLOCK;
201     /* enctypes can be negative, so sign-extend the 16-bit result. */
202     kb->enctype = (int16_t)get16(in, version);
203     /* Version 3 stores the enctype twice. */
204     if (version == 3)
205         (void)get16(in, version);
206     kb->contents = get_len_bytes(in, version, &kb->length);
207 }
208
209 static krb5_address *
210 unmarshal_addr(struct k5input *in, int version)
211 {
212     krb5_address *addr;
213
214     addr = calloc(1, sizeof(*addr));
215     if (addr == NULL) {
216         k5_input_set_status(in, ENOMEM);
217         return NULL;
218     }
219     addr->magic = KV5M_ADDRESS;
220     addr->addrtype = get16(in, version);
221     addr->contents = get_len_bytes(in, version, &addr->length);
222     return addr;
223 }
224
225 static krb5_address **
226 unmarshal_addrs(struct k5input *in, int version)
227 {
228     krb5_address **addrs;
229     size_t i, count;
230
231     count = get32(in, version);
232     if (count > in->len) {      /* Sanity check to avoid large allocations */
233         k5_input_set_status(in, EINVAL);
234         return NULL;
235     }
236     addrs = calloc(count + 1, sizeof(*addrs));
237     if (addrs == NULL) {
238         k5_input_set_status(in, ENOMEM);
239         return NULL;
240     }
241     for (i = 0; i < count; i++)
242         addrs[i] = unmarshal_addr(in, version);
243     return addrs;
244 }
245
246 static krb5_authdata *
247 unmarshal_authdatum(struct k5input *in, int version)
248 {
249     krb5_authdata *ad;
250
251     ad = calloc(1, sizeof(*ad));
252     if (ad == NULL) {
253         k5_input_set_status(in, ENOMEM);
254         return NULL;
255     }
256     ad->magic = KV5M_ADDRESS;
257     /* Authdata types can be negative, so sign-extend the get16 result. */
258     ad->ad_type = (int16_t)get16(in, version);
259     ad->contents = get_len_bytes(in, version, &ad->length);
260     return ad;
261 }
262
263 static krb5_authdata **
264 unmarshal_authdata(struct k5input *in, int version)
265 {
266     krb5_authdata **authdata;
267     size_t i, count;
268
269     count = get32(in, version);
270     if (count > in->len) {      /* Sanity check to avoid large allocations */
271         k5_input_set_status(in, EINVAL);
272         return NULL;
273     }
274     authdata = calloc(count + 1, sizeof(*authdata));
275     if (authdata == NULL) {
276         k5_input_set_status(in, ENOMEM);
277         return NULL;
278     }
279     for (i = 0; i < count; i++)
280         authdata[i] = unmarshal_authdatum(in, version);
281     return authdata;
282 }
283
284 /* Unmarshal a credential using the specified file ccache version (expressed as
285  * an integer from 1 to 4).  Does not check for trailing garbage. */
286 krb5_error_code
287 k5_unmarshal_cred(const unsigned char *data, size_t len, int version,
288                   krb5_creds *creds)
289 {
290     struct k5input in;
291
292     k5_input_init(&in, data, len);
293     creds->client = unmarshal_princ(&in, version);
294     creds->server = unmarshal_princ(&in, version);
295     unmarshal_keyblock(&in, version, &creds->keyblock);
296     creds->times.authtime = get32(&in, version);
297     creds->times.starttime = get32(&in, version);
298     creds->times.endtime = get32(&in, version);
299     creds->times.renew_till = get32(&in, version);
300     creds->is_skey = k5_input_get_byte(&in);
301     creds->ticket_flags = get32(&in, version);
302     creds->addresses = unmarshal_addrs(&in, version);
303     creds->authdata = unmarshal_authdata(&in, version);
304     get_data(&in, version, &creds->ticket);
305     get_data(&in, version, &creds->second_ticket);
306     if (in.status) {
307         krb5_free_cred_contents(NULL, creds);
308         memset(creds, 0, sizeof(*creds));
309     }
310     return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status;
311 }
312
313 /* Unmarshal a principal using the specified file ccache version (expressed as
314  * an integer from 1 to 4).  Does not check for trailing garbage. */
315 krb5_error_code
316 k5_unmarshal_princ(const unsigned char *data, size_t len, int version,
317                    krb5_principal *princ_out)
318 {
319     struct k5input in;
320     krb5_principal princ;
321
322     *princ_out = NULL;
323     k5_input_init(&in, data, len);
324     princ = unmarshal_princ(&in, version);
325     if (in.status)
326         krb5_free_principal(NULL, princ);
327     else
328         *princ_out = princ;
329     return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status;
330 }
331
332 /* Store a 16-bit integer in host byte order for versions 1 and 2, or in
333  * big-endian byte order for later versions.*/
334 static void
335 put16(struct k5buf *buf, int version, uint16_t num)
336 {
337     char n[2];
338
339     if (version < 3)
340         store_16_n(num, n);
341     else
342         store_16_be(num, n);
343     k5_buf_add_len(buf, n, 2);
344 }
345
346 /* Store a 32-bit integer in host byte order for versions 1 and 2, or in
347  * big-endian byte order for later versions.*/
348 static void
349 put32(struct k5buf *buf, int version, uint32_t num)
350 {
351     char n[4];
352
353     if (version < 3)
354         store_32_n(num, n);
355     else
356         store_32_be(num, n);
357     k5_buf_add_len(buf, n, 4);
358 }
359
360 static void
361 put_len_bytes(struct k5buf *buf, int version, const void *bytes,
362               unsigned int len)
363 {
364     put32(buf, version, len);
365     k5_buf_add_len(buf, bytes, len);
366 }
367
368 static void
369 put_data(struct k5buf *buf, int version, krb5_data *data)
370 {
371     put_len_bytes(buf, version, data->data, data->length);
372 }
373
374 void
375 k5_marshal_princ(struct k5buf *buf, int version, krb5_principal princ)
376 {
377     int32_t i, ncomps;
378
379     /* Version 1 does not store the principal name type, and counts the realm
380      * in the number of components. */
381     if (version != 1)
382         put32(buf, version, princ->type);
383     ncomps = princ->length + ((version == 1) ? 1 : 0);
384     put32(buf, version, ncomps);
385     put_data(buf, version, &princ->realm);
386     for (i = 0; i < princ->length; i++)
387         put_data(buf, version, &princ->data[i]);
388 }
389
390 static void
391 marshal_keyblock(struct k5buf *buf, int version, krb5_keyblock *kb)
392 {
393     put16(buf, version, kb->enctype);
394     /* Version 3 stores the enctype twice. */
395     if (version == 3)
396         put16(buf, version, kb->enctype);
397     put_len_bytes(buf, version, kb->contents, kb->length);
398 }
399
400 static void
401 marshal_addrs(struct k5buf *buf, int version, krb5_address **addrs)
402 {
403     size_t i, count;
404
405     for (count = 0; addrs != NULL && addrs[count] != NULL; count++);
406     put32(buf, version, count);
407     for (i = 0; i < count; i++) {
408         put16(buf, version, addrs[i]->addrtype);
409         put_len_bytes(buf, version, addrs[i]->contents, addrs[i]->length);
410     }
411 }
412
413 static void
414 marshal_authdata(struct k5buf *buf, int version, krb5_authdata **authdata)
415 {
416     size_t i, count;
417
418     for (count = 0; authdata != NULL && authdata[count] != NULL; count++);
419     put32(buf, version, count);
420     for (i = 0; i < count; i++) {
421         put16(buf, version, authdata[i]->ad_type);
422         put_len_bytes(buf, version, authdata[i]->contents,
423                       authdata[i]->length);
424     }
425 }
426
427 /* Marshal a credential using the specified file ccache version (expressed as
428  * an integer from 1 to 4). */
429 void
430 k5_marshal_cred(struct k5buf *buf, int version, krb5_creds *creds)
431 {
432     char is_skey;
433
434     k5_marshal_princ(buf, version, creds->client);
435     k5_marshal_princ(buf, version, creds->server);
436     marshal_keyblock(buf, version, &creds->keyblock);
437     put32(buf, version, creds->times.authtime);
438     put32(buf, version, creds->times.starttime);
439     put32(buf, version, creds->times.endtime);
440     put32(buf, version, creds->times.renew_till);
441     is_skey = creds->is_skey;
442     k5_buf_add_len(buf, &is_skey, 1);
443     put32(buf, version, creds->ticket_flags);
444     marshal_addrs(buf, version, creds->addresses);
445     marshal_authdata(buf, version, creds->authdata);
446     put_data(buf, version, &creds->ticket);
447     put_data(buf, version, &creds->second_ticket);
448 }
449
450 #define SC_CLIENT_PRINCIPAL  0x0001
451 #define SC_SERVER_PRINCIPAL  0x0002
452 #define SC_SESSION_KEY       0x0004
453 #define SC_TICKET            0x0008
454 #define SC_SECOND_TICKET     0x0010
455 #define SC_AUTHDATA          0x0020
456 #define SC_ADDRESSES         0x0040
457
458 /* Construct the header flags field for a matching credential for the Heimdal
459  * KCM format. */
460 static uint32_t
461 mcred_header(krb5_creds *mcred)
462 {
463     uint32_t header = 0;
464
465     if (mcred->client != NULL)
466         header |= SC_CLIENT_PRINCIPAL;
467     if (mcred->server != NULL)
468         header |= SC_SERVER_PRINCIPAL;
469     if (mcred->keyblock.enctype != ENCTYPE_NULL)
470         header |= SC_SESSION_KEY;
471     if (mcred->ticket.length > 0)
472         header |= SC_TICKET;
473     if (mcred->second_ticket.length > 0)
474         header |= SC_SECOND_TICKET;
475     if (mcred->authdata != NULL && *mcred->authdata != NULL)
476         header |= SC_AUTHDATA;
477     if (mcred->addresses != NULL && *mcred->addresses != NULL)
478         header |= SC_ADDRESSES;
479     return header;
480 }
481
482 /*
483  * Marshal a matching credential in the Heimdal KCM format.  Matching
484  * credentials are used to identify an existing credential to retrieve or
485  * remove from a cache.
486  */
487 void
488 k5_marshal_mcred(struct k5buf *buf, krb5_creds *mcred)
489 {
490     const int version = 4;      /* subfields use v4 file format */
491     uint32_t header;
492     char is_skey;
493
494     header = mcred_header(mcred);
495     put32(buf, version, header);
496     if (mcred->client != NULL)
497         k5_marshal_princ(buf, version, mcred->client);
498     if (mcred->server != NULL)
499         k5_marshal_princ(buf, version, mcred->server);
500     if (mcred->keyblock.enctype != ENCTYPE_NULL)
501         marshal_keyblock(buf, version, &mcred->keyblock);
502     put32(buf, version, mcred->times.authtime);
503     put32(buf, version, mcred->times.starttime);
504     put32(buf, version, mcred->times.endtime);
505     put32(buf, version, mcred->times.renew_till);
506     is_skey = mcred->is_skey;
507     k5_buf_add_len(buf, &is_skey, 1);
508     put32(buf, version, mcred->ticket_flags);
509     if (mcred->addresses != NULL && *mcred->addresses != NULL)
510         marshal_addrs(buf, version, mcred->addresses);
511     if (mcred->authdata != NULL && *mcred->authdata != NULL)
512         marshal_authdata(buf, version, mcred->authdata);
513     if (mcred->ticket.length > 0)
514         put_data(buf, version, &mcred->ticket);
515     if (mcred->second_ticket.length > 0)
516         put_data(buf, version, &mcred->second_ticket);
517 }