1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccmarshal.c - Functions for serializing creds */
4 * Copyright (C) 2014 by the Massachusetts Institute of Technology.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
44 * A psuedo-BNF grammar for the credential and principal formats is:
53 * renew_till (32 bits)
54 * is_skey (1 byte, 0 or 1)
55 * ticket_flags (32 bits)
56 * addresses (addresses)
59 * second_ticket (data)
62 * name type (32 bits) [omitted in version 1]
63 * count of components (32 bits) [includes realm in version 1]
70 * enctype (16 bits) [repeated twice in version 3; see below]
95 * value (length bytes)
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.
103 #include "k5-input.h"
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.*/
109 get16(struct k5input *in, int version)
111 return (version < 3) ? k5_input_get_uint16_n(in) :
112 k5_input_get_uint16_be(in);
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.*/
118 get32(struct k5input *in, int version)
120 return (version < 3) ? k5_input_get_uint32_n(in) :
121 k5_input_get_uint32_be(in);
124 /* Read a 32-bit length and make a copy of that many bytes. Return NULL on
127 get_len_bytes(struct k5input *in, int version, unsigned int *len_out)
130 unsigned int len = get32(in, version);
131 const void *bytes = k5_input_get_bytes(in, len);
137 copy = k5memdup0(bytes, len, &ret);
139 k5_input_set_status(in, ret);
146 /* Like get_len_bytes, but put the result in data. */
148 get_data(struct k5input *in, int version, krb5_data *data)
151 void *bytes = get_len_bytes(in, version, &len);
153 *data = (bytes == NULL) ? empty_data() : make_data(bytes, len);
156 static krb5_principal
157 unmarshal_princ(struct k5input *in, int version)
160 krb5_principal princ;
163 princ = k5alloc(sizeof(krb5_principal_data), &ret);
165 k5_input_set_status(in, ret);
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);
175 if (ncomps > in->len) { /* Sanity check to avoid large allocations */
180 princ->data = k5calloc(ncomps, sizeof(krb5_data), &ret);
181 if (princ->data == NULL)
183 princ->length = ncomps;
185 get_data(in, version, &princ->realm);
186 for (i = 0; i < ncomps; i++)
187 get_data(in, version, &princ->data[i]);
191 k5_input_set_status(in, ret);
192 krb5_free_principal(NULL, princ);
197 unmarshal_keyblock(struct k5input *in, int version, krb5_keyblock *kb)
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. */
205 (void)get16(in, version);
206 kb->contents = get_len_bytes(in, version, &kb->length);
209 static krb5_address *
210 unmarshal_addr(struct k5input *in, int version)
214 addr = calloc(1, sizeof(*addr));
216 k5_input_set_status(in, ENOMEM);
219 addr->magic = KV5M_ADDRESS;
220 addr->addrtype = get16(in, version);
221 addr->contents = get_len_bytes(in, version, &addr->length);
225 static krb5_address **
226 unmarshal_addrs(struct k5input *in, int version)
228 krb5_address **addrs;
231 count = get32(in, version);
232 if (count > in->len) { /* Sanity check to avoid large allocations */
233 k5_input_set_status(in, EINVAL);
236 addrs = calloc(count + 1, sizeof(*addrs));
238 k5_input_set_status(in, ENOMEM);
241 for (i = 0; i < count; i++)
242 addrs[i] = unmarshal_addr(in, version);
246 static krb5_authdata *
247 unmarshal_authdatum(struct k5input *in, int version)
251 ad = calloc(1, sizeof(*ad));
253 k5_input_set_status(in, ENOMEM);
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);
263 static krb5_authdata **
264 unmarshal_authdata(struct k5input *in, int version)
266 krb5_authdata **authdata;
269 count = get32(in, version);
270 if (count > in->len) { /* Sanity check to avoid large allocations */
271 k5_input_set_status(in, EINVAL);
274 authdata = calloc(count + 1, sizeof(*authdata));
275 if (authdata == NULL) {
276 k5_input_set_status(in, ENOMEM);
279 for (i = 0; i < count; i++)
280 authdata[i] = unmarshal_authdatum(in, version);
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. */
287 k5_unmarshal_cred(const unsigned char *data, size_t len, int version,
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);
307 krb5_free_cred_contents(NULL, creds);
308 memset(creds, 0, sizeof(*creds));
310 return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status;
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. */
316 k5_unmarshal_princ(const unsigned char *data, size_t len, int version,
317 krb5_principal *princ_out)
320 krb5_principal princ;
323 k5_input_init(&in, data, len);
324 princ = unmarshal_princ(&in, version);
326 krb5_free_principal(NULL, princ);
329 return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status;
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.*/
335 put16(struct k5buf *buf, int version, uint16_t num)
343 k5_buf_add_len(buf, n, 2);
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.*/
349 put32(struct k5buf *buf, int version, uint32_t num)
357 k5_buf_add_len(buf, n, 4);
361 put_len_bytes(struct k5buf *buf, int version, const void *bytes,
364 put32(buf, version, len);
365 k5_buf_add_len(buf, bytes, len);
369 put_data(struct k5buf *buf, int version, krb5_data *data)
371 put_len_bytes(buf, version, data->data, data->length);
375 k5_marshal_princ(struct k5buf *buf, int version, krb5_principal princ)
379 /* Version 1 does not store the principal name type, and counts the realm
380 * in the number of components. */
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]);
391 marshal_keyblock(struct k5buf *buf, int version, krb5_keyblock *kb)
393 put16(buf, version, kb->enctype);
394 /* Version 3 stores the enctype twice. */
396 put16(buf, version, kb->enctype);
397 put_len_bytes(buf, version, kb->contents, kb->length);
401 marshal_addrs(struct k5buf *buf, int version, krb5_address **addrs)
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);
414 marshal_authdata(struct k5buf *buf, int version, krb5_authdata **authdata)
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);
427 /* Marshal a credential using the specified file ccache version (expressed as
428 * an integer from 1 to 4). */
430 k5_marshal_cred(struct k5buf *buf, int version, krb5_creds *creds)
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);
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
458 /* Construct the header flags field for a matching credential for the Heimdal
461 mcred_header(krb5_creds *mcred)
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)
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;
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.
488 k5_marshal_mcred(struct k5buf *buf, krb5_creds *mcred)
490 const int version = 4; /* subfields use v4 file format */
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);