Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/do_tgs_req.c
34878 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* kdc/do_tgs_req.c - KDC Routines to deal with TGS_REQ's */
3
/*
4
* Copyright 1990, 1991, 2001, 2007, 2008, 2009, 2013, 2014 by the
5
* Massachusetts Institute of Technology. All Rights Reserved.
6
*
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
25
*/
26
/*
27
* Copyright (c) 2006-2008, Novell, Inc.
28
* All rights reserved.
29
*
30
* Redistribution and use in source and binary forms, with or without
31
* modification, are permitted provided that the following conditions are met:
32
*
33
* * Redistributions of source code must retain the above copyright notice,
34
* this list of conditions and the following disclaimer.
35
* * Redistributions in binary form must reproduce the above copyright
36
* notice, this list of conditions and the following disclaimer in the
37
* documentation and/or other materials provided with the distribution.
38
* * The copyright holder's name is not used to endorse or promote products
39
* derived from this software without specific prior written permission.
40
*
41
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51
* POSSIBILITY OF SUCH DAMAGE.
52
*/
53
54
#include "k5-int.h"
55
56
#include <syslog.h>
57
#ifdef HAVE_NETINET_IN_H
58
#include <sys/types.h>
59
#include <netinet/in.h>
60
#ifndef hpux
61
#include <arpa/inet.h>
62
#endif
63
#endif
64
65
#include "kdc_util.h"
66
#include "kdc_audit.h"
67
#include "policy.h"
68
#include "extern.h"
69
#include "adm_proto.h"
70
#include <ctype.h>
71
72
struct tgs_req_info {
73
/* The decoded request. Ownership is transferred to this structure. This
74
* will be replaced with the inner FAST body if present. */
75
krb5_kdc_req *req;
76
77
/*
78
* The decrypted authentication header ticket from the request's
79
* PA-TGS-REQ, the KDB entry for its server, its encryption key, the
80
* PA-TGS-REQ subkey if present, and the decoded and verified header ticket
81
* PAC if present.
82
*/
83
krb5_ticket *header_tkt;
84
krb5_db_entry *header_server;
85
krb5_keyblock *header_key;
86
krb5_keyblock *subkey;
87
krb5_pac header_pac;
88
89
/*
90
* If a second ticket is present and this is a U2U or S4U2Proxy request,
91
* the decoded and verified PAC if present, the KDB entry for the second
92
* ticket server server, and the key used to decrypt the second ticket.
93
*/
94
krb5_pac stkt_pac;
95
krb5_db_entry *stkt_server;
96
krb5_keyblock *stkt_server_key;
97
/* For cross-realm S4U2Proxy requests, the client principal retrieved from
98
* stkt_pac. */
99
krb5_principal stkt_pac_client;
100
101
/* Storage for the local TGT KDB entry for the service realm if that isn't
102
* the header server. */
103
krb5_db_entry *local_tgt_storage;
104
/* The decrypted first key of the local TGT entry. */
105
krb5_keyblock local_tgt_key;
106
107
/* The server KDB entry. Normally the requested server, but for referral
108
* and alternate TGS replies this will be a cross-realm TGT entry. */
109
krb5_db_entry *server;
110
111
/*
112
* The subject client KDB entry for an S4U2Self request, or the header
113
* ticket client KDB entry for other requests. NULL if
114
* NO_AUTH_DATA_REQUIRED is set on the server KDB entry and this isn't an
115
* S4U2Self request, or if the client is in another realm and the KDB
116
* cannot map its principal name.
117
*/
118
krb5_db_entry *client;
119
120
/* The decoded S4U2Self padata from the request, if present. */
121
krb5_pa_s4u_x509_user *s4u2self;
122
123
/* Authentication indicators retrieved from the header ticket, for
124
* non-S4U2Self requests. */
125
krb5_data **auth_indicators;
126
127
/* Storage for a transited list with the header TGT realm added, if that
128
* realm is different from the client and server realm. */
129
krb5_data new_transited;
130
131
/* The KDB flags applicable to this request (a subset of {CROSS_REALM,
132
* ISSUING_REFERRAL, PROTOCOL_TRANSITION, CONSTRAINED_DELEGATION}). */
133
unsigned int flags;
134
135
/* Booleans for two of the above flags, for convenience. */
136
krb5_boolean is_referral;
137
krb5_boolean is_crossrealm;
138
139
/* The authtime of subject_tkt. On early failures this may be 0. */
140
krb5_timestamp authtime;
141
142
/* The following fields are (or contain) alias pointers and should not be
143
* freed. */
144
145
/* The transited list implied by the request, aliasing new_transited or the
146
* header ticket transited field. */
147
krb5_transited transited;
148
149
/* Alias to the decrypted second ticket within req, if one applies to this
150
* request. */
151
const krb5_ticket *stkt;
152
153
/* Alias to stkt for S4U2Proxy requests, header_tkt otherwise. */
154
krb5_enc_tkt_part *subject_tkt;
155
156
/* Alias to local_tgt_storage or header_server. */
157
krb5_db_entry *local_tgt;
158
159
/* For either kind of S4U request, an alias to the requested client
160
* principal name. */
161
krb5_principal s4u_cprinc;
162
163
/* An alias to the client principal name we should issue the ticket for
164
* (either header_tkt->enc_part2->client or s4u_cprinc). */
165
krb5_principal tkt_client;
166
167
/* The client principal of the PA-TGS-REQ header ticket. On early failures
168
* this may be NULL. */
169
krb5_principal cprinc;
170
171
/* The canonicalized request server principal or referral/alternate TGT.
172
* On early failures this may be the requested server instead. */
173
krb5_principal sprinc;
174
175
};
176
177
static krb5_error_code
178
db_get_svc_princ(krb5_context, krb5_principal, krb5_flags,
179
krb5_db_entry **, const char **);
180
181
static krb5_error_code
182
prepare_error_tgs(struct kdc_request_state *state, krb5_kdc_req *request,
183
krb5_ticket *ticket, krb5_error_code code,
184
krb5_principal canon_server, krb5_data **response,
185
const char *status, krb5_pa_data **e_data)
186
{
187
krb5_context context = state->realm_data->realm_context;
188
krb5_error errpkt;
189
krb5_error_code retval = 0;
190
krb5_data *scratch, *e_data_asn1 = NULL, *fast_edata = NULL;
191
192
errpkt.magic = KV5M_ERROR;
193
errpkt.ctime = 0;
194
errpkt.cusec = 0;
195
196
retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec);
197
if (retval)
198
return(retval);
199
errpkt.error = errcode_to_protocol(code);
200
errpkt.server = request->server;
201
if (ticket && ticket->enc_part2)
202
errpkt.client = ticket->enc_part2->client;
203
else
204
errpkt.client = NULL;
205
errpkt.text.length = strlen(status);
206
if (!(errpkt.text.data = strdup(status)))
207
return ENOMEM;
208
209
if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
210
free(errpkt.text.data);
211
return ENOMEM;
212
}
213
214
if (e_data != NULL) {
215
retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
216
if (retval) {
217
free(scratch);
218
free(errpkt.text.data);
219
return retval;
220
}
221
errpkt.e_data = *e_data_asn1;
222
} else
223
errpkt.e_data = empty_data();
224
225
retval = kdc_fast_handle_error(context, state, request, e_data,
226
&errpkt, &fast_edata);
227
if (retval) {
228
free(scratch);
229
free(errpkt.text.data);
230
krb5_free_data(context, e_data_asn1);
231
return retval;
232
}
233
if (fast_edata)
234
errpkt.e_data = *fast_edata;
235
if (kdc_fast_hide_client(state) && errpkt.client != NULL)
236
errpkt.client = (krb5_principal)krb5_anonymous_principal();
237
retval = krb5_mk_error(context, &errpkt, scratch);
238
free(errpkt.text.data);
239
krb5_free_data(context, e_data_asn1);
240
krb5_free_data(context, fast_edata);
241
if (retval)
242
free(scratch);
243
else
244
*response = scratch;
245
246
return retval;
247
}
248
249
/* KDC options that require a second ticket */
250
#define STKT_OPTIONS (KDC_OPT_CNAME_IN_ADDL_TKT | KDC_OPT_ENC_TKT_IN_SKEY)
251
/*
252
* If req is a second-ticket request and a second ticket is present, decrypt
253
* it. Set *stkt_out to an alias to the ticket with populated enc_part2. Set
254
* *server_out to the server DB entry and *key_out to the ticket decryption
255
* key.
256
*/
257
static krb5_error_code
258
decrypt_2ndtkt(krb5_context context, krb5_kdc_req *req, krb5_flags flags,
259
krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
260
const krb5_ticket **stkt_out, krb5_pac *pac_out,
261
krb5_db_entry **server_out, krb5_keyblock **key_out,
262
const char **status)
263
{
264
krb5_error_code retval;
265
krb5_db_entry *server = NULL;
266
krb5_keyblock *key = NULL;
267
krb5_kvno kvno;
268
krb5_ticket *stkt;
269
270
*stkt_out = NULL;
271
*pac_out = NULL;
272
*server_out = NULL;
273
*key_out = NULL;
274
275
if (!(req->kdc_options & STKT_OPTIONS) || req->second_ticket == NULL ||
276
req->second_ticket[0] == NULL)
277
return 0;
278
279
stkt = req->second_ticket[0];
280
retval = kdc_get_server_key(context, stkt, flags, TRUE, &server, &key,
281
&kvno);
282
if (retval != 0) {
283
*status = "2ND_TKT_SERVER";
284
goto cleanup;
285
}
286
retval = krb5_decrypt_tkt_part(context, key, stkt);
287
if (retval != 0) {
288
*status = "2ND_TKT_DECRYPT";
289
goto cleanup;
290
}
291
retval = get_verified_pac(context, stkt->enc_part2, server, key, local_tgt,
292
local_tgt_key, pac_out);
293
if (retval != 0) {
294
*status = "2ND_TKT_PAC";
295
goto cleanup;
296
}
297
*stkt_out = stkt;
298
*server_out = server;
299
*key_out = key;
300
server = NULL;
301
key = NULL;
302
303
cleanup:
304
krb5_db_free_principal(context, server);
305
krb5_free_keyblock(context, key);
306
return retval;
307
}
308
309
static krb5_error_code
310
get_2ndtkt_enctype(krb5_kdc_req *req, krb5_enctype *useenctype,
311
const char **status)
312
{
313
krb5_enctype etype;
314
krb5_ticket *stkt = req->second_ticket[0];
315
int i;
316
317
etype = stkt->enc_part2->session->enctype;
318
if (!krb5_c_valid_enctype(etype)) {
319
*status = "BAD_ETYPE_IN_2ND_TKT";
320
return KRB5KDC_ERR_ETYPE_NOSUPP;
321
}
322
for (i = 0; i < req->nktypes; i++) {
323
if (req->ktype[i] == etype) {
324
*useenctype = etype;
325
break;
326
}
327
}
328
return 0;
329
}
330
331
static krb5_error_code
332
gen_session_key(krb5_context context, krb5_kdc_req *req, krb5_db_entry *server,
333
krb5_keyblock *skey, const char **status)
334
{
335
krb5_error_code retval;
336
krb5_enctype useenctype = 0;
337
338
/*
339
* Some special care needs to be taken in the user-to-user
340
* case, since we don't know what keytypes the application server
341
* which is doing user-to-user authentication can support. We
342
* know that it at least must be able to support the encryption
343
* type of the session key in the TGT, since otherwise it won't be
344
* able to decrypt the U2U ticket! So we use that in preference
345
* to anything else.
346
*/
347
if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
348
retval = get_2ndtkt_enctype(req, &useenctype, status);
349
if (retval != 0)
350
return retval;
351
}
352
if (useenctype == 0) {
353
useenctype = select_session_keytype(context, server,
354
req->nktypes, req->ktype);
355
}
356
if (useenctype == 0) {
357
/* unsupported ktype */
358
*status = "BAD_ENCRYPTION_TYPE";
359
return KRB5KDC_ERR_ETYPE_NOSUPP;
360
}
361
362
return krb5_c_make_random_key(context, useenctype, skey);
363
}
364
365
/*
366
* The request seems to be for a ticket-granting service somewhere else,
367
* but we don't have a ticket for the final TGS. Try to give the requestor
368
* some intermediate realm.
369
*/
370
static krb5_error_code
371
find_alternate_tgs(krb5_context context, krb5_principal princ,
372
krb5_db_entry **server_ptr, const char **status)
373
{
374
krb5_error_code retval;
375
krb5_principal *plist = NULL, *pl2;
376
krb5_data tmp;
377
krb5_db_entry *server = NULL;
378
379
*server_ptr = NULL;
380
assert(is_cross_tgs_principal(princ));
381
retval = krb5_walk_realm_tree(context, &princ->realm, &princ->data[1],
382
&plist, KRB5_REALM_BRANCH_CHAR);
383
if (retval)
384
goto cleanup;
385
/* move to the end */
386
for (pl2 = plist; *pl2; pl2++);
387
388
/* the first entry in this array is for krbtgt/local@local, so we
389
ignore it */
390
while (--pl2 > plist) {
391
tmp = *krb5_princ_realm(context, *pl2);
392
krb5_princ_set_realm(context, *pl2, &princ->realm);
393
retval = db_get_svc_princ(context, *pl2, 0, &server, status);
394
krb5_princ_set_realm(context, *pl2, &tmp);
395
if (retval == KRB5_KDB_NOENTRY)
396
continue;
397
else if (retval)
398
goto cleanup;
399
400
log_tgs_alt_tgt(context, server->princ);
401
*server_ptr = server;
402
server = NULL;
403
goto cleanup;
404
}
405
cleanup:
406
if (retval == 0 && *server_ptr == NULL)
407
retval = KRB5_KDB_NOENTRY;
408
if (retval != 0)
409
*status = "UNKNOWN_SERVER";
410
411
krb5_free_realm_tree(context, plist);
412
krb5_db_free_principal(context, server);
413
return retval;
414
}
415
416
/* Return true if item is an element of the space/comma-separated list. */
417
static krb5_boolean
418
in_list(const char *list, const char *item)
419
{
420
const char *p;
421
int len = strlen(item);
422
423
if (list == NULL)
424
return FALSE;
425
for (p = strstr(list, item); p != NULL; p = strstr(p + 1, item)) {
426
if ((p == list || isspace((unsigned char)p[-1]) || p[-1] == ',') &&
427
(p[len] == '\0' || isspace((unsigned char)p[len]) ||
428
p[len] == ','))
429
return TRUE;
430
}
431
return FALSE;
432
}
433
434
/*
435
* Check whether the request satisfies the conditions for generating a referral
436
* TGT. The caller checks whether the hostname component looks like a FQDN.
437
*/
438
static krb5_boolean
439
is_referral_req(kdc_realm_t *realm, krb5_kdc_req *request)
440
{
441
krb5_boolean ret = FALSE;
442
char *stype = NULL;
443
char *hostbased = realm->realm_hostbased;
444
char *no_referral = realm->realm_no_referral;
445
446
if (!(request->kdc_options & KDC_OPT_CANONICALIZE))
447
return FALSE;
448
449
if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)
450
return FALSE;
451
452
if (request->server->length != 2)
453
return FALSE;
454
455
stype = data2string(&request->server->data[0]);
456
if (stype == NULL)
457
return FALSE;
458
switch (request->server->type) {
459
case KRB5_NT_UNKNOWN:
460
/* Allow referrals for NT-UNKNOWN principals, if configured. */
461
if (!in_list(hostbased, stype) && !in_list(hostbased, "*"))
462
goto cleanup;
463
/* FALLTHROUGH */
464
case KRB5_NT_SRV_HST:
465
case KRB5_NT_SRV_INST:
466
/* Deny referrals for specific service types, if configured. */
467
if (in_list(no_referral, stype) || in_list(no_referral, "*"))
468
goto cleanup;
469
ret = TRUE;
470
break;
471
default:
472
goto cleanup;
473
}
474
cleanup:
475
free(stype);
476
return ret;
477
}
478
479
/*
480
* Find a remote realm TGS principal for an unknown host-based service
481
* principal.
482
*/
483
static krb5_int32
484
find_referral_tgs(kdc_realm_t *realm, krb5_kdc_req *request,
485
krb5_principal *krbtgt_princ)
486
{
487
krb5_context context = realm->realm_context;
488
krb5_error_code retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
489
char **realms = NULL, *hostname = NULL;
490
krb5_data srealm = request->server->realm;
491
492
if (!is_referral_req(realm, request))
493
goto cleanup;
494
495
hostname = data2string(&request->server->data[1]);
496
if (hostname == NULL) {
497
retval = ENOMEM;
498
goto cleanup;
499
}
500
/* If the hostname doesn't contain a '.', it's not a FQDN. */
501
if (strchr(hostname, '.') == NULL)
502
goto cleanup;
503
retval = krb5_get_host_realm(context, hostname, &realms);
504
if (retval) {
505
/* no match found */
506
kdc_err(context, retval, "unable to find realm of host");
507
goto cleanup;
508
}
509
/* Don't return a referral to the empty realm or the service realm. */
510
if (realms == NULL || realms[0] == NULL || *realms[0] == '\0' ||
511
data_eq_string(srealm, realms[0])) {
512
retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
513
goto cleanup;
514
}
515
retval = krb5_build_principal(context, krbtgt_princ,
516
srealm.length, srealm.data,
517
"krbtgt", realms[0], (char *)0);
518
cleanup:
519
krb5_free_host_realm(context, realms);
520
free(hostname);
521
522
return retval;
523
}
524
525
static krb5_error_code
526
db_get_svc_princ(krb5_context ctx, krb5_principal princ,
527
krb5_flags flags, krb5_db_entry **server,
528
const char **status)
529
{
530
krb5_error_code ret;
531
532
ret = krb5_db_get_principal(ctx, princ, flags, server);
533
if (ret == KRB5_KDB_CANTLOCK_DB)
534
ret = KRB5KDC_ERR_SVC_UNAVAILABLE;
535
if (ret != 0) {
536
*status = "LOOKING_UP_SERVER";
537
}
538
return ret;
539
}
540
541
static krb5_error_code
542
search_sprinc(kdc_realm_t *realm, krb5_kdc_req *req,
543
krb5_flags flags, krb5_db_entry **server, const char **status)
544
{
545
krb5_context context = realm->realm_context;
546
krb5_error_code ret;
547
krb5_principal princ = req->server;
548
krb5_principal reftgs = NULL;
549
krb5_boolean allow_referral;
550
551
/* Do not allow referrals for u2u or ticket modification requests, because
552
* the server is supposed to match an already-issued ticket. */
553
allow_referral = !(req->kdc_options & NO_REFERRAL_OPTION);
554
if (!allow_referral)
555
flags &= ~KRB5_KDB_FLAG_REFERRAL_OK;
556
557
ret = db_get_svc_princ(context, princ, flags, server, status);
558
if (ret == 0 || ret != KRB5_KDB_NOENTRY || !allow_referral)
559
goto cleanup;
560
561
if (!is_cross_tgs_principal(req->server)) {
562
ret = find_referral_tgs(realm, req, &reftgs);
563
if (ret != 0)
564
goto cleanup;
565
ret = db_get_svc_princ(context, reftgs, flags, server, status);
566
if (ret == 0 || ret != KRB5_KDB_NOENTRY)
567
goto cleanup;
568
569
princ = reftgs;
570
}
571
ret = find_alternate_tgs(context, princ, server, status);
572
573
cleanup:
574
if (ret != 0 && ret != KRB5KDC_ERR_SVC_UNAVAILABLE) {
575
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
576
if (*status == NULL)
577
*status = "LOOKING_UP_SERVER";
578
}
579
krb5_free_principal(context, reftgs);
580
return ret;
581
}
582
583
/*
584
* Transfer ownership of *reqptr to *t and fill *t with information about the
585
* request. Decode the PA-TGS-REQ header ticket and the second ticket if
586
* applicable, and decode and verify their PACs if present. Decode and verify
587
* the S4U2Self request pa-data if present. Extract authentication indicators
588
* from the subject ticket. Construct the transited list implied by the
589
* request.
590
*/
591
static krb5_error_code
592
gather_tgs_req_info(kdc_realm_t *realm, krb5_kdc_req **reqptr, krb5_data *pkt,
593
const struct sockaddr *from,
594
struct kdc_request_state *fast_state,
595
krb5_audit_state *au_state, struct tgs_req_info *t,
596
const char **status)
597
{
598
krb5_context context = realm->realm_context;
599
krb5_error_code ret;
600
krb5_pa_data *pa_tgs_req;
601
unsigned int s_flags;
602
krb5_enc_tkt_part *header_enc;
603
krb5_data d;
604
605
/* Transfer ownership of *reqptr to *t. */
606
t->req = *reqptr;
607
*reqptr = NULL;
608
609
if (t->req->msg_type != KRB5_TGS_REQ)
610
return KRB5_BADMSGTYPE;
611
612
/* Initially set t->sprinc to the outer request server, for logging of
613
* early failures. */
614
t->sprinc = t->req->server;
615
616
/* Read the PA-TGS-REQ authenticator and decrypt the header ticket. */
617
ret = kdc_process_tgs_req(realm, t->req, from, pkt, &t->header_tkt,
618
&t->header_server, &t->header_key, &t->subkey,
619
&pa_tgs_req);
620
if (t->header_tkt != NULL && t->header_tkt->enc_part2 != NULL)
621
t->cprinc = t->header_tkt->enc_part2->client;
622
if (ret) {
623
*status = "PROCESS_TGS";
624
return ret;
625
}
626
ret = kau_make_tkt_id(context, t->header_tkt, &au_state->tkt_in_id);
627
if (ret)
628
return ret;
629
header_enc = t->header_tkt->enc_part2;
630
631
/* If PA-FX-FAST-REQUEST padata is present, replace t->req with the inner
632
* request body. */
633
d = make_data(pa_tgs_req->contents, pa_tgs_req->length);
634
ret = kdc_find_fast(&t->req, &d, t->subkey, header_enc->session,
635
fast_state, NULL);
636
if (ret) {
637
*status = "FIND_FAST";
638
return ret;
639
}
640
/* Reset t->sprinc for the inner body and check it. */
641
t->sprinc = t->req->server;
642
if (t->sprinc == NULL) {
643
*status = "NULL_SERVER";
644
return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
645
}
646
647
/* The header ticket server is usually a TGT, but if it is not, fetch the
648
* local TGT for the realm. Get the decrypted first local TGT key. */
649
ret = get_local_tgt(context, &t->sprinc->realm, t->header_server,
650
&t->local_tgt, &t->local_tgt_storage,
651
&t->local_tgt_key);
652
if (ret) {
653
*status = "GET_LOCAL_TGT";
654
return ret;
655
}
656
657
/* Decode and verify the header ticket PAC. */
658
ret = get_verified_pac(context, header_enc, t->header_server,
659
t->header_key, t->local_tgt, &t->local_tgt_key,
660
&t->header_pac);
661
if (ret) {
662
*status = "HEADER_PAC";
663
return ret;
664
}
665
666
au_state->request = t->req;
667
au_state->stage = SRVC_PRINC;
668
669
/* Look up the server principal entry, or a referral/alternate TGT. Reset
670
* t->sprinc to the canonical server name (its final value). */
671
s_flags = (t->req->kdc_options & KDC_OPT_CANONICALIZE) ?
672
KRB5_KDB_FLAG_REFERRAL_OK : 0;
673
ret = search_sprinc(realm, t->req, s_flags, &t->server, status);
674
if (ret)
675
return ret;
676
t->sprinc = t->server->princ;
677
678
/* If we got a cross-realm TGS which is not the requested server, we are
679
* issuing a referral (or alternate TGT, which we treat similarly). */
680
if (is_cross_tgs_principal(t->server->princ) &&
681
!krb5_principal_compare(context, t->req->server, t->server->princ))
682
t->flags |= KRB5_KDB_FLAG_ISSUING_REFERRAL;
683
684
/* Mark the request as cross-realm if the header ticket server is not from
685
* this realm. */
686
if (!data_eq(t->header_server->princ->realm, t->sprinc->realm))
687
t->flags |= KRB5_KDB_FLAG_CROSS_REALM;
688
689
t->is_referral = (t->flags & KRB5_KDB_FLAG_ISSUING_REFERRAL);
690
t->is_crossrealm = (t->flags & KRB5_KDB_FLAG_CROSS_REALM);
691
692
/* If S4U2Self padata is present, read it to get the requested principal
693
* name. Look up the requested client if it is in this realm. */
694
ret = kdc_process_s4u2self_req(context, t->req, t->server, t->subkey,
695
header_enc->session, &t->s4u2self,
696
&t->client, status);
697
if (t->s4u2self != NULL || ret) {
698
if (t->s4u2self != NULL)
699
au_state->s4u2self_user = t->s4u2self->user_id.user;
700
au_state->status = *status;
701
kau_s4u2self(context, !ret, au_state);
702
au_state->s4u2self_user = NULL;
703
}
704
if (ret)
705
return ret;
706
if (t->s4u2self != NULL) {
707
t->flags |= KRB5_KDB_FLAG_PROTOCOL_TRANSITION;
708
t->s4u_cprinc = t->s4u2self->user_id.user;
709
710
/*
711
* For consistency with Active Directory, don't allow authorization
712
* data to be disabled if S4U2Self is requested. The requesting
713
* service likely needs a PAC for an S4U2Proxy operation, even if it
714
* doesn't need authorization data in tickets received from clients.
715
*/
716
t->server->attributes &= ~KRB5_KDB_NO_AUTH_DATA_REQUIRED;
717
}
718
719
/* For U2U or S4U2Proxy requests, decrypt the second ticket and read its
720
* PAC. */
721
ret = decrypt_2ndtkt(context, t->req, t->flags, t->local_tgt,
722
&t->local_tgt_key, &t->stkt, &t->stkt_pac,
723
&t->stkt_server, &t->stkt_server_key, status);
724
if (ret)
725
return ret;
726
727
/* Determine the subject ticket and set the authtime for logging. For
728
* S4U2Proxy requests determine the requested client principal. */
729
if (t->req->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) {
730
t->flags |= KRB5_KDB_FLAG_CONSTRAINED_DELEGATION;
731
ret = kau_make_tkt_id(context, t->stkt, &au_state->evid_tkt_id);
732
if (ret)
733
return ret;
734
if (t->is_crossrealm) {
735
/* For cross-realm S4U2PROXY requests, the second ticket is a
736
* cross TGT with the requested client principal in its PAC. */
737
if (t->stkt_pac == NULL ||
738
get_pac_princ_with_realm(context, t->stkt_pac,
739
&t->stkt_pac_client, NULL) != 0) {
740
au_state->status = *status = "RBCD_PAC_PRINC";
741
au_state->violation = PROT_CONSTRAINT;
742
kau_s4u2proxy(context, FALSE, au_state);
743
return KRB5KDC_ERR_BADOPTION;
744
}
745
t->s4u_cprinc = t->stkt_pac_client;
746
} else {
747
/* Otherwise the requested client is the evidence ticket client. */
748
t->s4u_cprinc = t->stkt->enc_part2->client;
749
}
750
t->subject_tkt = t->stkt->enc_part2;
751
} else {
752
t->subject_tkt = header_enc;
753
}
754
t->authtime = t->subject_tkt->times.authtime;
755
756
/* For final S4U requests (either type) the issued ticket will be for the
757
* requested name; otherwise it will be for the header ticket client. */
758
t->tkt_client = ((t->flags & KRB5_KDB_FLAGS_S4U) && !t->is_referral) ?
759
t->s4u_cprinc : header_enc->client;
760
761
if (t->s4u2self == NULL) {
762
/* Extract auth indicators from the subject ticket. Skip this for
763
* S4U2Self requests as the subject didn't authenticate. */
764
ret = get_auth_indicators(context, t->subject_tkt, t->local_tgt,
765
&t->local_tgt_key, &t->auth_indicators);
766
if (ret) {
767
*status = "GET_AUTH_INDICATORS";
768
return ret;
769
}
770
771
if (!(t->server->attributes & KRB5_KDB_NO_AUTH_DATA_REQUIRED)) {
772
/* Try to look up the subject principal so that KDB modules can add
773
* additional authdata. Ask the KDB to map foreign principals. */
774
assert(t->client == NULL);
775
(void)krb5_db_get_principal(context, t->subject_tkt->client,
776
t->flags | KRB5_KDB_FLAG_CLIENT |
777
KRB5_KDB_FLAG_MAP_PRINCIPALS,
778
&t->client);
779
}
780
}
781
782
/*
783
* Compute the transited list implied by the request. Use the existing
784
* transited list if the realm of the header ticket server is the same as
785
* the subject or server realm.
786
*/
787
if (!t->is_crossrealm ||
788
data_eq(t->header_tkt->server->realm, t->tkt_client->realm)) {
789
t->transited = header_enc->transited;
790
} else {
791
if (header_enc->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) {
792
*status = "VALIDATE_TRANSIT_TYPE";
793
return KRB5KDC_ERR_TRTYPE_NOSUPP;
794
}
795
ret = add_to_transited(&header_enc->transited.tr_contents,
796
&t->new_transited, t->header_tkt->server,
797
t->tkt_client, t->req->server);
798
if (ret) {
799
*status = "ADD_TO_TRANSITED_LIST";
800
return ret;
801
}
802
t->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
803
t->transited.tr_contents = t->new_transited;
804
}
805
806
return 0;
807
}
808
809
/* Fill in *times_out with the times of the ticket to be issued. Set the
810
* TKT_FLG_RENEWABLE bit in *tktflags if the ticket will be renewable. */
811
static void
812
compute_ticket_times(kdc_realm_t *realm, struct tgs_req_info *t,
813
krb5_timestamp kdc_time, krb5_flags *tktflags,
814
krb5_ticket_times *times)
815
{
816
krb5_timestamp hstarttime;
817
krb5_deltat hlife;
818
krb5_ticket_times *htimes = &t->header_tkt->enc_part2->times;
819
820
if (t->req->kdc_options & KDC_OPT_VALIDATE) {
821
/* Validation requests preserve the header ticket times. */
822
*times = *htimes;
823
return;
824
}
825
826
/* Preserve the authtime from the subject ticket. */
827
times->authtime = t->authtime;
828
829
times->starttime = (t->req->kdc_options & KDC_OPT_POSTDATED) ?
830
t->req->from : kdc_time;
831
832
if (t->req->kdc_options & KDC_OPT_RENEW) {
833
/* Give the new ticket the same lifetime as the header ticket, but no
834
* later than the renewable end time. */
835
hstarttime = htimes->starttime ? htimes->starttime : htimes->authtime;
836
hlife = ts_delta(htimes->endtime, hstarttime);
837
times->endtime = ts_min(htimes->renew_till,
838
ts_incr(times->starttime, hlife));
839
} else {
840
kdc_get_ticket_endtime(realm, times->starttime, htimes->endtime,
841
t->req->till, t->client, t->server,
842
&times->endtime);
843
}
844
845
kdc_get_ticket_renewtime(realm, t->req, t->header_tkt->enc_part2,
846
t->client, t->server, tktflags, times);
847
848
/* starttime is optional, and treated as authtime if not present.
849
* so we can omit it if it matches. */
850
if (times->starttime == times->authtime)
851
times->starttime = 0;
852
}
853
854
/* Check the request in *t against semantic protocol constraints and local
855
* policy. Determine flags and times for the ticket to be issued. */
856
static krb5_error_code
857
check_tgs_req(kdc_realm_t *realm, struct tgs_req_info *t,
858
krb5_audit_state *au_state, krb5_flags *tktflags,
859
krb5_ticket_times *times, const char **status,
860
krb5_pa_data ***e_data)
861
{
862
krb5_context context = realm->realm_context;
863
krb5_error_code ret;
864
krb5_timestamp kdc_time;
865
866
au_state->stage = VALIDATE_POL;
867
868
ret = krb5_timeofday(context, &kdc_time);
869
if (ret)
870
return ret;
871
872
ret = check_tgs_constraints(realm, t->req, t->server, t->header_tkt,
873
t->header_pac, t->stkt, t->stkt_pac,
874
t->stkt_server, kdc_time, t->s4u2self,
875
t->client, t->is_crossrealm, t->is_referral,
876
status, e_data);
877
if (ret) {
878
au_state->violation = PROT_CONSTRAINT;
879
return ret;
880
}
881
882
ret = check_tgs_policy(realm, t->req, t->server, t->header_tkt,
883
t->header_pac, t->stkt, t->stkt_pac,
884
t->stkt_pac_client, t->stkt_server, kdc_time,
885
t->is_crossrealm, t->is_referral, status, e_data);
886
if (ret) {
887
au_state->violation = LOCAL_POLICY;
888
if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
889
au_state->status = *status;
890
kau_s4u2proxy(context, FALSE, au_state);
891
}
892
return ret;
893
}
894
895
/* Check auth indicators from the subject ticket, except for S4U2Self
896
* requests (where the client didn't authenticate). */
897
if (t->s4u2self == NULL) {
898
ret = check_indicators(context, t->server, t->auth_indicators);
899
if (ret) {
900
*status = "HIGHER_AUTHENTICATION_REQUIRED";
901
return ret;
902
}
903
}
904
905
*tktflags = get_ticket_flags(t->req->kdc_options, t->client, t->server,
906
t->header_tkt->enc_part2);
907
compute_ticket_times(realm, t, kdc_time, tktflags, times);
908
909
/* For S4U2Self requests, check if we need to suppress the forwardable
910
* ticket flag. */
911
if (t->s4u2self != NULL && !t->is_referral) {
912
ret = s4u2self_forwardable(context, t->server, tktflags);
913
if (ret)
914
return ret;
915
}
916
917
/* Consult kdcpolicy modules, giving them a chance to modify the times of
918
* the issued ticket. */
919
ret = check_kdcpolicy_tgs(context, t->req, t->server, t->header_tkt,
920
t->auth_indicators, kdc_time, times, status);
921
if (ret)
922
return ret;
923
924
if (!(t->req->kdc_options & KDC_OPT_DISABLE_TRANSITED_CHECK)) {
925
/* Check the transited path for the issued ticket and set the
926
* transited-policy-checked flag if successful. */
927
ret = kdc_check_transited_list(context, &t->transited.tr_contents,
928
&t->subject_tkt->client->realm,
929
&t->req->server->realm);
930
if (ret) {
931
/* Log the transited-check failure and continue. */
932
log_tgs_badtrans(context, t->cprinc, t->sprinc,
933
&t->transited.tr_contents, ret);
934
} else {
935
*tktflags |= TKT_FLG_TRANSIT_POLICY_CHECKED;
936
}
937
} else {
938
krb5_klog_syslog(LOG_INFO, _("not checking transit path"));
939
}
940
941
/* By default, reject the request if the transited path was not checked
942
* successfully. */
943
if (realm->realm_reject_bad_transit &&
944
!(*tktflags & TKT_FLG_TRANSIT_POLICY_CHECKED)) {
945
*status = "BAD_TRANSIT";
946
au_state->violation = LOCAL_POLICY;
947
return KRB5KDC_ERR_POLICY;
948
}
949
950
return 0;
951
}
952
953
/* Construct a response issuing a ticket for the request in *t, using tktflags
954
* and *times for the ticket flags and times. */
955
static krb5_error_code
956
tgs_issue_ticket(kdc_realm_t *realm, struct tgs_req_info *t,
957
krb5_flags tktflags, krb5_ticket_times *times, krb5_data *pkt,
958
const struct sockaddr *from,
959
struct kdc_request_state *fast_state,
960
krb5_audit_state *au_state, const char **status,
961
krb5_data **response)
962
{
963
krb5_context context = realm->realm_context;
964
krb5_error_code ret;
965
krb5_keyblock session_key = { 0 }, server_key = { 0 };
966
krb5_keyblock *ticket_encrypting_key, *subject_key;
967
krb5_keyblock *initial_reply_key, *fast_reply_key = NULL;
968
krb5_enc_tkt_part enc_tkt_reply = { 0 };
969
krb5_ticket ticket_reply = { 0 };
970
krb5_enc_kdc_rep_part reply_encpart = { 0 };
971
krb5_kdc_rep reply = { 0 };
972
krb5_pac subject_pac;
973
krb5_db_entry *subject_server;
974
krb5_enc_tkt_part *header_enc_tkt = t->header_tkt->enc_part2;
975
krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
976
krb5_last_req_entry *nolrarray[2] = { &nolrentry, NULL };
977
978
au_state->stage = ISSUE_TKT;
979
980
ret = gen_session_key(context, t->req, t->server, &session_key, status);
981
if (ret)
982
goto cleanup;
983
984
if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
985
subject_pac = t->stkt_pac;
986
subject_server = t->stkt_server;
987
subject_key = t->stkt_server_key;
988
} else {
989
subject_pac = t->header_pac;
990
subject_server = t->header_server;
991
subject_key = t->header_key;
992
}
993
994
initial_reply_key = (t->subkey != NULL) ? t->subkey :
995
t->header_tkt->enc_part2->session;
996
997
if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
998
/* For user-to-user, encrypt the ticket with the second ticket's
999
* session key. */
1000
ticket_encrypting_key = t->stkt->enc_part2->session;
1001
} else {
1002
/* Otherwise encrypt the ticket with the server entry's first long-term
1003
* key. */
1004
ret = get_first_current_key(context, t->server, &server_key);
1005
if (ret) {
1006
*status = "FINDING_SERVER_KEY";
1007
goto cleanup;
1008
}
1009
ticket_encrypting_key = &server_key;
1010
}
1011
1012
if (t->req->kdc_options & (KDC_OPT_VALIDATE | KDC_OPT_RENEW)) {
1013
/* Copy the header ticket server and all enc-part fields except for
1014
* authorization data. */
1015
ticket_reply.server = t->header_tkt->server;
1016
enc_tkt_reply = *t->header_tkt->enc_part2;
1017
enc_tkt_reply.authorization_data = NULL;
1018
} else {
1019
if (t->req->kdc_options & (KDC_OPT_FORWARDED | KDC_OPT_PROXY)) {
1020
/* Include the requested addresses in the ticket and reply. */
1021
enc_tkt_reply.caddrs = t->req->addresses;
1022
reply_encpart.caddrs = t->req->addresses;
1023
} else {
1024
/* Use the header ticket addresses and omit them from the reply. */
1025
enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
1026
reply_encpart.caddrs = NULL;
1027
}
1028
1029
ticket_reply.server = t->is_referral ? t->sprinc : t->req->server;
1030
}
1031
1032
enc_tkt_reply.flags = tktflags;
1033
enc_tkt_reply.times = *times;
1034
enc_tkt_reply.client = t->tkt_client;
1035
enc_tkt_reply.session = &session_key;
1036
enc_tkt_reply.transited = t->transited;
1037
1038
ret = handle_authdata(realm, t->flags, t->client, t->server,
1039
subject_server, t->local_tgt, &t->local_tgt_key,
1040
initial_reply_key, ticket_encrypting_key,
1041
subject_key, NULL, pkt, t->req, t->s4u_cprinc,
1042
subject_pac, t->subject_tkt, &t->auth_indicators,
1043
&enc_tkt_reply);
1044
if (ret) {
1045
krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"), ret);
1046
*status = "HANDLE_AUTHDATA";
1047
goto cleanup;
1048
}
1049
1050
ticket_reply.enc_part2 = &enc_tkt_reply;
1051
1052
ret = krb5_encrypt_tkt_part(context, ticket_encrypting_key, &ticket_reply);
1053
if (ret)
1054
goto cleanup;
1055
1056
if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
1057
ticket_reply.enc_part.kvno = 0;
1058
kau_u2u(context, TRUE, au_state);
1059
} else {
1060
ticket_reply.enc_part.kvno = current_kvno(t->server);
1061
}
1062
1063
au_state->stage = ENCR_REP;
1064
1065
if (t->s4u2self != NULL &&
1066
krb5int_find_pa_data(context, t->req->padata,
1067
KRB5_PADATA_S4U_X509_USER) != NULL) {
1068
/* Add an S4U2Self response to the encrypted padata (skipped if the
1069
* request only included PA-FOR-USER padata). */
1070
ret = kdc_make_s4u2self_rep(context, t->subkey,
1071
t->header_tkt->enc_part2->session,
1072
t->s4u2self, &reply, &reply_encpart);
1073
if (ret)
1074
goto cleanup;
1075
}
1076
1077
reply_encpart.session = &session_key;
1078
reply_encpart.nonce = t->req->nonce;
1079
reply_encpart.times = enc_tkt_reply.times;
1080
reply_encpart.last_req = nolrarray;
1081
reply_encpart.key_exp = 0;
1082
reply_encpart.flags = enc_tkt_reply.flags;
1083
reply_encpart.server = ticket_reply.server;
1084
1085
reply.msg_type = KRB5_TGS_REP;
1086
reply.client = enc_tkt_reply.client;
1087
reply.ticket = &ticket_reply;
1088
reply.enc_part.kvno = 0;
1089
reply.enc_part.enctype = initial_reply_key->enctype;
1090
ret = kdc_fast_response_handle_padata(fast_state, t->req, &reply,
1091
initial_reply_key->enctype);
1092
if (ret)
1093
goto cleanup;
1094
ret = kdc_fast_handle_reply_key(fast_state, initial_reply_key,
1095
&fast_reply_key);
1096
if (ret)
1097
goto cleanup;
1098
ret = return_enc_padata(context, pkt, t->req, fast_reply_key, t->server,
1099
&reply_encpart,
1100
t->is_referral &&
1101
(t->req->kdc_options & KDC_OPT_CANONICALIZE));
1102
if (ret) {
1103
*status = "KDC_RETURN_ENC_PADATA";
1104
goto cleanup;
1105
}
1106
1107
ret = kau_make_tkt_id(context, &ticket_reply, &au_state->tkt_out_id);
1108
if (ret)
1109
goto cleanup;
1110
1111
if (kdc_fast_hide_client(fast_state))
1112
reply.client = (krb5_principal)krb5_anonymous_principal();
1113
ret = krb5_encode_kdc_rep(context, KRB5_TGS_REP, &reply_encpart,
1114
t->subkey != NULL, fast_reply_key, &reply,
1115
response);
1116
if (ret)
1117
goto cleanup;
1118
1119
log_tgs_req(context, from, t->req, &reply, t->cprinc, t->sprinc,
1120
t->s4u_cprinc, t->authtime, t->flags, "ISSUE", 0, NULL);
1121
au_state->status = "ISSUE";
1122
au_state->reply = &reply;
1123
if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)
1124
kau_s4u2proxy(context, TRUE, au_state);
1125
kau_tgs_req(context, TRUE, au_state);
1126
au_state->reply = NULL;
1127
1128
cleanup:
1129
zapfree(ticket_reply.enc_part.ciphertext.data,
1130
ticket_reply.enc_part.ciphertext.length);
1131
zapfree(reply.enc_part.ciphertext.data, reply.enc_part.ciphertext.length);
1132
krb5_free_pa_data(context, reply.padata);
1133
krb5_free_pa_data(context, reply_encpart.enc_padata);
1134
krb5_free_authdata(context, enc_tkt_reply.authorization_data);
1135
krb5_free_keyblock_contents(context, &session_key);
1136
krb5_free_keyblock_contents(context, &server_key);
1137
krb5_free_keyblock(context, fast_reply_key);
1138
return ret;
1139
}
1140
1141
static void
1142
free_req_info(krb5_context context, struct tgs_req_info *t)
1143
{
1144
krb5_free_kdc_req(context, t->req);
1145
krb5_free_ticket(context, t->header_tkt);
1146
krb5_db_free_principal(context, t->header_server);
1147
krb5_free_keyblock(context, t->header_key);
1148
krb5_free_keyblock(context, t->subkey);
1149
krb5_pac_free(context, t->header_pac);
1150
krb5_pac_free(context, t->stkt_pac);
1151
krb5_db_free_principal(context, t->stkt_server);
1152
krb5_free_keyblock(context, t->stkt_server_key);
1153
krb5_db_free_principal(context, t->local_tgt_storage);
1154
krb5_free_keyblock_contents(context, &t->local_tgt_key);
1155
krb5_db_free_principal(context, t->server);
1156
krb5_db_free_principal(context, t->client);
1157
krb5_free_pa_s4u_x509_user(context, t->s4u2self);
1158
krb5_free_principal(context, t->stkt_pac_client);
1159
k5_free_data_ptr_list(t->auth_indicators);
1160
krb5_free_data_contents(context, &t->new_transited);
1161
}
1162
1163
krb5_error_code
1164
process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
1165
const struct sockaddr *from, kdc_realm_t *realm,
1166
krb5_data **response)
1167
{
1168
krb5_context context = realm->realm_context;
1169
krb5_error_code ret;
1170
struct tgs_req_info t = { 0 };
1171
struct kdc_request_state *fast_state = NULL;
1172
krb5_audit_state *au_state = NULL;
1173
krb5_pa_data **e_data = NULL;
1174
krb5_flags tktflags;
1175
krb5_ticket_times times = { 0 };
1176
const char *emsg = NULL, *status = NULL;
1177
1178
ret = kdc_make_rstate(realm, &fast_state);
1179
if (ret)
1180
goto cleanup;
1181
ret = kau_init_kdc_req(context, request, from, &au_state);
1182
if (ret)
1183
goto cleanup;
1184
kau_tgs_req(context, TRUE, au_state);
1185
1186
ret = gather_tgs_req_info(realm, &request, pkt, from, fast_state, au_state,
1187
&t, &status);
1188
if (ret)
1189
goto cleanup;
1190
1191
ret = check_tgs_req(realm, &t, au_state, &tktflags, &times, &status,
1192
&e_data);
1193
if (ret)
1194
goto cleanup;
1195
1196
ret = tgs_issue_ticket(realm, &t, tktflags, &times, pkt, from, fast_state,
1197
au_state, &status, response);
1198
if (ret)
1199
goto cleanup;
1200
1201
cleanup:
1202
if (status == NULL)
1203
status = "UNKNOWN_REASON";
1204
1205
if (ret) {
1206
emsg = krb5_get_error_message(context, ret);
1207
log_tgs_req(context, from, t.req, NULL, t.cprinc, t.sprinc,
1208
t.s4u_cprinc, t.authtime, t.flags, status, ret, emsg);
1209
krb5_free_error_message(context, emsg);
1210
1211
if (au_state != NULL) {
1212
au_state->status = status;
1213
kau_tgs_req(context, FALSE, au_state);
1214
}
1215
}
1216
1217
if (ret && fast_state != NULL) {
1218
ret = prepare_error_tgs(fast_state, t.req, t.header_tkt, ret,
1219
(t.server != NULL) ? t.server->princ : NULL,
1220
response, status, e_data);
1221
}
1222
1223
krb5_free_kdc_req(context, request);
1224
kdc_free_rstate(fast_state);
1225
kau_free_kdc_req(au_state);
1226
free_req_info(context, &t);
1227
krb5_free_pa_data(context, e_data);
1228
return ret;
1229
}
1230
1231