1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/adata.c - Test harness for KDC authorization data */
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 * Usage: ./adata [-c ccname] [-p clientprinc] serviceprinc
35 * [ad-type ad-contents ...]
36 *
37 * This program acquires credentials for the specified service principal, using
38 * the specified or default ccache, possibly including requested authdata. The
39 * resulting ticket is decrypted using the default keytab, and the authdata in
40 * the ticket are displayed to stdout.
41 *
42 * In the requested authdata types, the type may be prefixed with '?' for an
43 * AD-IF-RELEVANT container, '!' for an AD-MANDATORY-FOR-KDC container, or '^'
44 * for an AD-KDC-ISSUED container checksummed with a random AES256 key.
45 * Multiple prefixes may be specified for nested container.
46 *
47 * In the output, authdata containers will be flattened and displayed with the
48 * above prefixes or '+' for an AD-CAMMAC container. AD-KDC-ISSUED and
49 * AD-CAMMAC containers will be verified with the appropriate key. Nested
50 * containers only display the prefix for the innermost container.
51 */
52
53 #include <k5-int.h>
54 #include <ctype.h>
55
56 static krb5_context ctx;
57
58 static void display_authdata_list(krb5_authdata **list,
59 krb5_enc_tkt_part *enc_tkt,
60 krb5_keyblock *tktkey, char prefix_byte,
61 krb5_boolean pac_ok);
62
63 static void
check(krb5_error_code code)64 check(krb5_error_code code)
65 {
66 const char *errmsg;
67
68 if (code) {
69 errmsg = krb5_get_error_message(ctx, code);
70 fprintf(stderr, "%s\n", errmsg);
71 krb5_free_error_message(ctx, errmsg);
72 exit(1);
73 }
74 }
75
76 static krb5_authdatatype
get_type_for_prefix(int prefix_byte)77 get_type_for_prefix(int prefix_byte)
78 {
79 if (prefix_byte == '?')
80 return KRB5_AUTHDATA_IF_RELEVANT;
81 if (prefix_byte == '!')
82 return KRB5_AUTHDATA_MANDATORY_FOR_KDC;
83 if (prefix_byte == '^')
84 return KRB5_AUTHDATA_KDC_ISSUED;
85 if (prefix_byte == '+')
86 return KRB5_AUTHDATA_CAMMAC;
87 abort();
88 }
89
90 static int
get_prefix_byte(krb5_authdata * ad)91 get_prefix_byte(krb5_authdata *ad)
92 {
93 if (ad->ad_type == KRB5_AUTHDATA_IF_RELEVANT)
94 return '?';
95 if (ad->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC)
96 return '!';
97 if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
98 return '^';
99 if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
100 return '+';
101 abort();
102 }
103
104 /* Construct a container of type ad_type for the single authdata element
105 * content. For KDC-ISSUED containers, use a random checksum key. */
106 static krb5_authdata *
make_container(krb5_authdatatype ad_type,krb5_authdata * content)107 make_container(krb5_authdatatype ad_type, krb5_authdata *content)
108 {
109 krb5_authdata *list[2], **enclist, *ad;
110 krb5_keyblock kb;
111
112 list[0] = content;
113 list[1] = NULL;
114
115 if (ad_type == KRB5_AUTHDATA_KDC_ISSUED) {
116 check(krb5_c_make_random_key(ctx, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
117 &kb));
118 check(krb5_make_authdata_kdc_issued(ctx, &kb, NULL, list, &enclist));
119 krb5_free_keyblock_contents(ctx, &kb);
120 } else {
121 check(krb5_encode_authdata_container(ctx, ad_type, list, &enclist));
122 }
123
124 /* Grab the first element from the encoded list and free the array. */
125 ad = enclist[0];
126 free(enclist);
127 return ad;
128 }
129
130 /* Parse typestr and contents into an authdata element. */
131 static krb5_authdata *
make_authdata(const char * typestr,const char * contents)132 make_authdata(const char *typestr, const char *contents)
133 {
134 krb5_authdata *inner_ad, *ad;
135
136 if (*typestr == '?' || *typestr == '!' || *typestr == '^') {
137 inner_ad = make_authdata(typestr + 1, contents);
138 ad = make_container(get_type_for_prefix(*typestr), inner_ad);
139 free(inner_ad->contents);
140 free(inner_ad);
141 return ad;
142 }
143
144 ad = malloc(sizeof(*ad));
145 assert(ad != NULL);
146 ad->magic = KV5M_AUTHDATA;
147 ad->ad_type = atoi(typestr);
148 ad->length = strlen(contents);
149 ad->contents = (unsigned char *)strdup(contents);
150 assert(ad->contents != NULL);
151 return ad;
152 }
153
154 static krb5_authdata **
get_container_contents(krb5_authdata * ad,krb5_keyblock * skey,krb5_keyblock * tktkey)155 get_container_contents(krb5_authdata *ad, krb5_keyblock *skey,
156 krb5_keyblock *tktkey)
157 {
158 krb5_authdata **inner_ad;
159
160 if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
161 check(krb5_verify_authdata_kdc_issued(ctx, skey, ad, NULL, &inner_ad));
162 else if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
163 check(k5_unwrap_cammac_svc(ctx, ad, tktkey, &inner_ad));
164 else
165 check(krb5_decode_authdata_container(ctx, ad->ad_type, ad, &inner_ad));
166 return inner_ad;
167 }
168
169 static int
compare_uint32(const void * p1,const void * p2)170 compare_uint32(const void *p1, const void *p2)
171 {
172 uint32_t t1 = *(uint32_t *)p1, t2 = *(uint32_t *)p2;
173
174 return (t1 > t2) ? 1 : (t1 == t2) ? 0 : -1;
175 }
176
177 static void
display_pac(krb5_authdata * ad,krb5_enc_tkt_part * enc_tkt,krb5_keyblock * tktkey)178 display_pac(krb5_authdata *ad, krb5_enc_tkt_part *enc_tkt,
179 krb5_keyblock *tktkey)
180 {
181 krb5_pac pac;
182 size_t tlen, i;
183 uint32_t *types;
184
185 assert(ad->ad_type == KRB5_AUTHDATA_WIN2K_PAC);
186 check(krb5_pac_parse(ctx, ad->contents, ad->length, &pac));
187
188 check(krb5_pac_verify(ctx, pac, enc_tkt->times.authtime, enc_tkt->client,
189 tktkey, NULL));
190
191 check(krb5_pac_get_types(ctx, pac, &tlen, &types));
192 qsort(types, tlen, sizeof(*types), compare_uint32);
193
194 printf("[");
195 for (i = 0; i < tlen; i++) {
196 printf("%d", (int)types[i]);
197 if (i + 1 < tlen)
198 printf(", ");
199 }
200 printf("]");
201
202 free(types);
203 krb5_pac_free(ctx, pac);
204 }
205
206 /* Decode and display authentication indicator authdata. */
207 static void
display_auth_indicator(krb5_authdata * ad)208 display_auth_indicator(krb5_authdata *ad)
209 {
210 krb5_data **strs = NULL, **p;
211
212 check(k5_authind_decode(ad, &strs));
213 assert(strs != NULL);
214
215 printf("[");
216 for (p = strs; *p != NULL; p++) {
217 printf("%.*s", (int)(*p)->length, (*p)->data);
218 if (*(p + 1) != NULL)
219 printf(", ");
220 }
221 printf("]");
222 k5_free_data_ptr_list(strs);
223 }
224
225 /* Display ad as either a hex dump or ASCII text. */
226 static void
display_binary_or_ascii(krb5_authdata * ad)227 display_binary_or_ascii(krb5_authdata *ad)
228 {
229 krb5_boolean binary = FALSE;
230 unsigned char *p;
231
232 for (p = ad->contents; p < ad->contents + ad->length; p++) {
233 if (!isascii(*p) || !isprint(*p))
234 binary = TRUE;
235 }
236 if (binary) {
237 for (p = ad->contents; p < ad->contents + ad->length; p++)
238 printf("%02X", *p);
239 } else {
240 printf("%.*s", (int)ad->length, ad->contents);
241 }
242 }
243
244 /* Display the contents of an authdata element, prefixed by prefix_byte. skey
245 * must be the ticket session key. */
246 static void
display_authdata(krb5_authdata * ad,krb5_enc_tkt_part * enc_tkt,krb5_keyblock * tktkey,int prefix_byte,krb5_boolean pac_ok)247 display_authdata(krb5_authdata *ad, krb5_enc_tkt_part *enc_tkt,
248 krb5_keyblock *tktkey, int prefix_byte, krb5_boolean pac_ok)
249 {
250 krb5_authdata **inner_ad;
251
252 if (ad->ad_type == KRB5_AUTHDATA_IF_RELEVANT ||
253 ad->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC ||
254 ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED ||
255 ad->ad_type == KRB5_AUTHDATA_CAMMAC) {
256 if (ad->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
257 pac_ok = FALSE;
258 /* Decode and display the contents. */
259 inner_ad = get_container_contents(ad, enc_tkt->session, tktkey);
260 display_authdata_list(inner_ad, enc_tkt, tktkey, get_prefix_byte(ad),
261 pac_ok);
262 krb5_free_authdata(ctx, inner_ad);
263 return;
264 }
265
266 assert(pac_ok || ad->ad_type != KRB5_AUTHDATA_WIN2K_PAC);
267
268 printf("%c", prefix_byte);
269 printf("%d: ", (int)ad->ad_type);
270
271 if (ad->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
272 display_pac(ad, enc_tkt, tktkey);
273 else if (ad->ad_type == KRB5_AUTHDATA_AUTH_INDICATOR)
274 display_auth_indicator(ad);
275 else
276 display_binary_or_ascii(ad);
277 printf("\n");
278 }
279
280 static void
display_authdata_list(krb5_authdata ** list,krb5_enc_tkt_part * tkt_enc,krb5_keyblock * tktkey,char prefix_byte,krb5_boolean pac_ok)281 display_authdata_list(krb5_authdata **list, krb5_enc_tkt_part *tkt_enc,
282 krb5_keyblock *tktkey, char prefix_byte,
283 krb5_boolean pac_ok)
284 {
285 if (list == NULL)
286 return;
287 /* Only expect a PAC in the first element, if at all. */
288 for (; *list != NULL; list++) {
289 display_authdata(*list, tkt_enc, tktkey, prefix_byte, pac_ok);
290 pac_ok = FALSE;
291 }
292 }
293
294 int
main(int argc,char ** argv)295 main(int argc, char **argv)
296 {
297 const char *ccname = NULL, *clientname = NULL;
298 krb5_principal client, server;
299 krb5_ccache ccache;
300 krb5_keytab keytab;
301 krb5_creds in_creds, *creds;
302 krb5_ticket *ticket;
303 krb5_authdata **req_authdata = NULL, *ad;
304 krb5_keytab_entry ktent;
305 size_t count;
306 int c;
307
308 check(krb5_init_context(&ctx));
309
310 while ((c = getopt(argc, argv, "+c:p:")) != -1) {
311 switch (c) {
312 case 'c':
313 ccname = optarg;
314 break;
315 case 'p':
316 clientname = optarg;
317 break;
318 default:
319 abort();
320 }
321 }
322 argv += optind;
323 /* Parse arguments. */
324 assert(*argv != NULL);
325 check(krb5_parse_name(ctx, *argv++, &server));
326
327 count = 0;
328 for (; argv[0] != NULL && argv[1] != NULL; argv += 2) {
329 ad = make_authdata(argv[0], argv[1]);
330 req_authdata = realloc(req_authdata,
331 (count + 2) * sizeof(*req_authdata));
332 assert(req_authdata != NULL);
333 req_authdata[count++] = ad;
334 req_authdata[count] = NULL;
335 }
336 assert(*argv == NULL);
337
338 if (ccname != NULL)
339 check(krb5_cc_resolve(ctx, ccname, &ccache));
340 else
341 check(krb5_cc_default(ctx, &ccache));
342
343 if (clientname != NULL)
344 check(krb5_parse_name(ctx, clientname, &client));
345 else
346 check(krb5_cc_get_principal(ctx, ccache, &client));
347
348 memset(&in_creds, 0, sizeof(in_creds));
349 in_creds.client = client;
350 in_creds.server = server;
351 in_creds.authdata = req_authdata;
352
353 check(krb5_get_credentials(ctx, KRB5_GC_NO_STORE, ccache, &in_creds,
354 &creds));
355
356 assert(in_creds.authdata == NULL || creds->authdata != NULL);
357
358 check(krb5_decode_ticket(&creds->ticket, &ticket));
359 check(krb5_kt_default(ctx, &keytab));
360 check(krb5_kt_get_entry(ctx, keytab, ticket->server, ticket->enc_part.kvno,
361 ticket->enc_part.enctype, &ktent));
362 check(krb5_decrypt_tkt_part(ctx, &ktent.key, ticket));
363
364 display_authdata_list(ticket->enc_part2->authorization_data,
365 ticket->enc_part2, &ktent.key, ' ', TRUE);
366
367 while (count > 0) {
368 free(req_authdata[--count]->contents);
369 free(req_authdata[count]);
370 }
371 free(req_authdata);
372 krb5_free_keytab_entry_contents(ctx, &ktent);
373 krb5_free_creds(ctx, creds);
374 krb5_free_ticket(ctx, ticket);
375 krb5_free_principal(ctx, client);
376 krb5_free_principal(ctx, server);
377 krb5_cc_close(ctx, ccache);
378 krb5_kt_close(ctx, keytab);
379 krb5_free_context(ctx);
380 return 0;
381 }
382