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 static krb5_error_code
73 find_alternate_tgs(kdc_realm_t *, krb5_principal, krb5_db_entry **,
74 const char**);
75
76 static krb5_error_code
77 prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *,krb5_ticket *,int,
78 krb5_principal,krb5_data **,const char *, krb5_pa_data **);
79
80 static krb5_error_code
81 decrypt_2ndtkt(kdc_realm_t *, krb5_kdc_req *, krb5_flags, krb5_db_entry **,
82 krb5_keyblock **, const char **);
83
84 static krb5_error_code
85 gen_session_key(kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *,
86 krb5_keyblock *, const char **);
87
88 static krb5_int32
89 find_referral_tgs(kdc_realm_t *, krb5_kdc_req *, krb5_principal *);
90
91 static krb5_error_code
92 db_get_svc_princ(krb5_context, krb5_principal, krb5_flags,
93 krb5_db_entry **, const char **);
94
95 static krb5_error_code
96 search_sprinc(kdc_realm_t *, krb5_kdc_req *, krb5_flags,
97 krb5_db_entry **, const char **);
98
99 /*ARGSUSED*/
100 krb5_error_code
process_tgs_req(krb5_kdc_req * request,krb5_data * pkt,const krb5_fulladdr * from,kdc_realm_t * kdc_active_realm,krb5_data ** response)101 process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
102 const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm,
103 krb5_data **response)
104 {
105 krb5_keyblock * subkey = 0;
106 krb5_keyblock *header_key = NULL;
107 krb5_keyblock *stkt_server_key = NULL;
108 krb5_keyblock *subject_key;
109 krb5_db_entry *server = NULL;
110 krb5_db_entry *stkt_server = NULL;
111 krb5_db_entry *subject_server;
112 krb5_kdc_rep reply;
113 krb5_enc_kdc_rep_part reply_encpart;
114 krb5_ticket ticket_reply, *header_ticket = 0;
115 int st_idx = 0;
116 krb5_enc_tkt_part enc_tkt_reply;
117 int newtransited = 0;
118 krb5_error_code retval = 0;
119 krb5_keyblock server_keyblock, *encrypting_key;
120 krb5_timestamp kdc_time, authtime = 0;
121 krb5_keyblock session_key, local_tgt_key;
122 krb5_keyblock *reply_key = NULL;
123 krb5_key_data *server_key;
124 krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL;
125 krb5_const_principal authdata_client;
126 krb5_principal stkt_authdata_client = NULL;
127 krb5_last_req_entry *nolrarray[2], nolrentry;
128 int errcode;
129 const char *status = 0;
130 krb5_enc_tkt_part *header_enc_tkt = NULL; /* TGT */
131 krb5_enc_tkt_part *subject_tkt = NULL; /* TGT or evidence ticket */
132 krb5_db_entry *client = NULL, *header_server = NULL;
133 krb5_db_entry *local_tgt, *local_tgt_storage = NULL;
134 krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
135 krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
136 unsigned int c_flags = 0, s_flags = 0; /* client/server KDB flags */
137 krb5_boolean is_referral;
138 const char *emsg = NULL;
139 krb5_kvno ticket_kvno = 0;
140 struct kdc_request_state *state = NULL;
141 krb5_pa_data *pa_tgs_req; /*points into request*/
142 krb5_data scratch;
143 krb5_pa_data **e_data = NULL;
144 krb5_audit_state *au_state = NULL;
145 krb5_data **auth_indicators = NULL;
146 void *ad_info = NULL, *stkt_ad_info = NULL;
147
148 memset(&reply, 0, sizeof(reply));
149 memset(&reply_encpart, 0, sizeof(reply_encpart));
150 memset(&ticket_reply, 0, sizeof(ticket_reply));
151 memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply));
152 memset(&server_keyblock, 0, sizeof(server_keyblock));
153 memset(&local_tgt_key, 0, sizeof(local_tgt_key));
154 session_key.contents = NULL;
155
156 /* Save pointer to client-requested service principal, in case of
157 * errors before a successful call to search_sprinc(). */
158 sprinc = request->server;
159
160 if (request->msg_type != KRB5_TGS_REQ) {
161 krb5_free_kdc_req(kdc_context, request);
162 return KRB5_BADMSGTYPE;
163 }
164
165 errcode = kdc_make_rstate(kdc_active_realm, &state);
166 if (errcode !=0) {
167 krb5_free_kdc_req(kdc_context, request);
168 return errcode;
169 }
170
171 /* Initialize audit state. */
172 errcode = kau_init_kdc_req(kdc_context, request, from, &au_state);
173 if (errcode) {
174 krb5_free_kdc_req(kdc_context, request);
175 return errcode;
176 }
177 /* Seed the audit trail with the request ID and basic information. */
178 kau_tgs_req(kdc_context, TRUE, au_state);
179
180 errcode = kdc_process_tgs_req(kdc_active_realm,
181 request, from, pkt, &header_ticket,
182 &header_server, &header_key, &subkey,
183 &pa_tgs_req);
184 if (header_ticket && header_ticket->enc_part2)
185 cprinc = header_ticket->enc_part2->client;
186
187 if (errcode) {
188 status = "PROCESS_TGS";
189 goto cleanup;
190 }
191
192 if (!header_ticket) {
193 errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */
194 goto cleanup;
195 }
196 errcode = kau_make_tkt_id(kdc_context, header_ticket,
197 &au_state->tkt_in_id);
198 if (errcode)
199 goto cleanup;
200
201 scratch.length = pa_tgs_req->length;
202 scratch.data = (char *) pa_tgs_req->contents;
203 errcode = kdc_find_fast(&request, &scratch, subkey,
204 header_ticket->enc_part2->session, state, NULL);
205 /* Reset sprinc because kdc_find_fast() can replace request. */
206 sprinc = request->server;
207 if (errcode !=0) {
208 status = "FIND_FAST";
209 goto cleanup;
210 }
211
212 errcode = get_local_tgt(kdc_context, &sprinc->realm, header_server,
213 &local_tgt, &local_tgt_storage, &local_tgt_key);
214 if (errcode) {
215 status = "GET_LOCAL_TGT";
216 goto cleanup;
217 }
218
219 /* Ignore (for now) the request modification due to FAST processing. */
220 au_state->request = request;
221
222 /*
223 * Pointer to the encrypted part of the header ticket, which may be
224 * replaced to point to the encrypted part of the evidence ticket
225 * if constrained delegation is used. This simplifies the number of
226 * special cases for constrained delegation.
227 */
228 header_enc_tkt = header_ticket->enc_part2;
229
230 /*
231 * We've already dealt with the AP_REQ authentication, so we can
232 * use header_ticket freely. The encrypted part (if any) has been
233 * decrypted with the session key.
234 */
235
236 au_state->stage = SRVC_PRINC;
237
238 /* XXX make sure server here has the proper realm...taken from AP_REQ
239 header? */
240
241 if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
242 setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
243 setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
244 }
245
246 errcode = search_sprinc(kdc_active_realm, request, s_flags, &server,
247 &status);
248 if (errcode != 0)
249 goto cleanup;
250 sprinc = server->princ;
251
252 /* If we got a cross-realm TGS which is not the requested server, we are
253 * issuing a referral (or alternate TGT, which we treat similarly). */
254 is_referral = is_cross_tgs_principal(server->princ) &&
255 !krb5_principal_compare(kdc_context, request->server, server->princ);
256
257 au_state->stage = VALIDATE_POL;
258
259 if ((errcode = krb5_timeofday(kdc_context, &kdc_time)))
260 goto cleanup;
261
262 if ((retval = validate_tgs_request(kdc_active_realm,
263 request, *server, header_ticket,
264 kdc_time, &status, &e_data))) {
265 if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION)
266 au_state->violation = PROT_CONSTRAINT;
267 errcode = retval + ERROR_TABLE_BASE_krb5;
268 goto cleanup;
269 }
270
271 if (!is_local_principal(kdc_active_realm, header_ticket->server))
272 setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);
273 if (is_referral)
274 setflag(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL);
275
276 /* Check for protocol transition */
277 errcode = kdc_process_s4u2self_req(kdc_active_realm,
278 request,
279 header_enc_tkt->client,
280 c_flags,
281 server,
282 subkey,
283 header_enc_tkt->session,
284 kdc_time,
285 &s4u_x509_user,
286 &client,
287 &status);
288 if (s4u_x509_user != NULL || errcode != 0) {
289 if (s4u_x509_user != NULL)
290 au_state->s4u2self_user = s4u_x509_user->user_id.user;
291 if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
292 au_state->violation = PROT_CONSTRAINT;
293 au_state->status = status;
294 kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
295 au_state->s4u2self_user = NULL;
296 }
297
298 if (errcode)
299 goto cleanup;
300
301 if (s4u_x509_user != NULL && client == NULL) {
302 /*
303 * For an S4U2Self referral request (the requesting service is
304 * following a referral back to its own realm), the authdata in the
305 * header ticket should be for the requested client.
306 */
307 setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
308 authdata_client = s4u_x509_user->user_id.user;
309 } else {
310 /* Otherwise (including for initial S4U2Self requests), the authdata
311 * should be for the header ticket client. */
312 authdata_client = header_enc_tkt->client;
313 }
314 errcode = krb5_db_get_authdata_info(kdc_context, c_flags,
315 header_enc_tkt->authorization_data,
316 authdata_client, request->server,
317 header_key, &local_tgt_key, local_tgt,
318 header_enc_tkt->times.authtime,
319 &ad_info, NULL);
320 if (errcode && errcode != KRB5_PLUGIN_OP_NOTSUPP)
321 goto cleanup;
322
323 /* Flag all S4U2Self requests now that we have checked the authdata. */
324 if (s4u_x509_user != NULL)
325 setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
326
327 /* Deal with user-to-user and constrained delegation */
328 errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags,
329 &stkt_server, &stkt_server_key, &status);
330 if (errcode)
331 goto cleanup;
332
333 if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
334 /* Do constrained delegation protocol and authorization checks. */
335 setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
336
337 errcode = kdc_process_s4u2proxy_req(kdc_active_realm, c_flags, request,
338 request->second_ticket[st_idx]->enc_part2,
339 local_tgt, &local_tgt_key,
340 stkt_server, stkt_server_key,
341 header_ticket->enc_part2->client,
342 server, request->server, ad_info,
343 &stkt_ad_info,
344 &stkt_authdata_client,
345 &status);
346 if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
347 au_state->violation = PROT_CONSTRAINT;
348 else if (errcode)
349 au_state->violation = LOCAL_POLICY;
350 au_state->status = status;
351 retval = kau_make_tkt_id(kdc_context, request->second_ticket[st_idx],
352 &au_state->evid_tkt_id);
353 if (retval) {
354 errcode = retval;
355 goto cleanup;
356 }
357 kau_s4u2proxy(kdc_context, errcode ? FALSE : TRUE, au_state);
358 if (errcode)
359 goto cleanup;
360
361 assert(krb5_is_tgs_principal(header_ticket->server));
362
363 /* Use the parsed authdata from the second ticket during authdata
364 * handling. */
365 krb5_db_free_authdata_info(kdc_context, ad_info);
366 ad_info = stkt_ad_info;
367 stkt_ad_info = NULL;
368 }
369
370 au_state->stage = ISSUE_TKT;
371
372 errcode = gen_session_key(kdc_active_realm, request, server, &session_key,
373 &status);
374 if (errcode)
375 goto cleanup;
376
377 /*
378 * subject_tkt will refer to the evidence ticket (for constrained
379 * delegation) or the TGT. The distinction from header_enc_tkt is
380 * necessary because the TGS signature only protects some fields:
381 * the others could be forged by a malicious server.
382 */
383
384 if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
385 subject_tkt = request->second_ticket[st_idx]->enc_part2;
386 subject_server = stkt_server;
387 subject_key = stkt_server_key;
388 } else {
389 subject_tkt = header_enc_tkt;
390 subject_server = header_server;
391 subject_key = header_key;
392 }
393 authtime = subject_tkt->times.authtime;
394
395 /* Extract auth indicators from the subject ticket, except for S4U2Self
396 * requests (where the client didn't authenticate). */
397 if (s4u_x509_user == NULL) {
398 errcode = get_auth_indicators(kdc_context, subject_tkt, local_tgt,
399 &local_tgt_key, &auth_indicators);
400 if (errcode) {
401 status = "GET_AUTH_INDICATORS";
402 goto cleanup;
403 }
404 }
405
406 errcode = check_indicators(kdc_context, server, auth_indicators);
407 if (errcode) {
408 status = "HIGHER_AUTHENTICATION_REQUIRED";
409 goto cleanup;
410 }
411
412 if (is_referral)
413 ticket_reply.server = server->princ;
414 else
415 ticket_reply.server = request->server; /* XXX careful for realm... */
416
417 enc_tkt_reply.flags = get_ticket_flags(request->kdc_options, client,
418 server, header_enc_tkt);
419 enc_tkt_reply.times.starttime = 0;
420
421 /* OK_TO_AUTH_AS_DELEGATE must be set on the service requesting S4U2Self
422 * for forwardable tickets to be issued. */
423 if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
424 !is_referral &&
425 !isflagset(server->attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
426 clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
427
428 /* don't use new addresses unless forwarded, see below */
429
430 enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
431 /* noaddrarray[0] = 0; */
432 reply_encpart.caddrs = 0;/* optional...don't put it in */
433 reply_encpart.enc_padata = NULL;
434
435 /*
436 * It should be noted that local policy may affect the
437 * processing of any of these flags. For example, some
438 * realms may refuse to issue renewable tickets
439 */
440
441 if (isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
442 isflagset(request->kdc_options, KDC_OPT_PROXY)) {
443
444 /* include new addresses in ticket & reply */
445
446 enc_tkt_reply.caddrs = request->addresses;
447 reply_encpart.caddrs = request->addresses;
448 }
449
450 if (isflagset(request->kdc_options, KDC_OPT_POSTDATED))
451 enc_tkt_reply.times.starttime = request->from;
452 else
453 enc_tkt_reply.times.starttime = kdc_time;
454
455 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
456 assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
457 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
458 to the caller */
459 ticket_reply = *(header_ticket);
460 enc_tkt_reply = *(header_ticket->enc_part2);
461 enc_tkt_reply.authorization_data = NULL;
462 clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
463 }
464
465 if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
466 krb5_timestamp old_starttime;
467 krb5_deltat old_life;
468
469 assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
470 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
471 to the caller */
472 ticket_reply = *(header_ticket);
473 enc_tkt_reply = *(header_ticket->enc_part2);
474 enc_tkt_reply.authorization_data = NULL;
475
476 old_starttime = enc_tkt_reply.times.starttime ?
477 enc_tkt_reply.times.starttime : enc_tkt_reply.times.authtime;
478 old_life = ts_delta(enc_tkt_reply.times.endtime, old_starttime);
479
480 enc_tkt_reply.times.starttime = kdc_time;
481 enc_tkt_reply.times.endtime =
482 ts_min(header_ticket->enc_part2->times.renew_till,
483 ts_incr(kdc_time, old_life));
484 } else {
485 /* not a renew request */
486 enc_tkt_reply.times.starttime = kdc_time;
487
488 kdc_get_ticket_endtime(kdc_active_realm, enc_tkt_reply.times.starttime,
489 header_enc_tkt->times.endtime, request->till,
490 client, server, &enc_tkt_reply.times.endtime);
491 }
492
493 kdc_get_ticket_renewtime(kdc_active_realm, request, header_enc_tkt, client,
494 server, &enc_tkt_reply);
495
496 errcode = check_kdcpolicy_tgs(kdc_context, request, server, header_ticket,
497 auth_indicators, kdc_time,
498 &enc_tkt_reply.times, &status);
499 if (errcode)
500 goto cleanup;
501
502 /*
503 * Set authtime to be the same as header or evidence ticket's
504 */
505 enc_tkt_reply.times.authtime = authtime;
506
507 /* starttime is optional, and treated as authtime if not present.
508 so we can nuke it if it matches */
509 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
510 enc_tkt_reply.times.starttime = 0;
511
512 if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
513 altcprinc = s4u_x509_user->user_id.user;
514 } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
515 /* kdc_process_s4u2proxy_req() only allows cross-realm requests if
516 * stkt_authdata_client is set. */
517 altcprinc = isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM) ?
518 stkt_authdata_client : subject_tkt->client;
519 } else {
520 altcprinc = NULL;
521 }
522 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
523 krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
524 encrypting_key = t2enc->session;
525 } else {
526 /*
527 * Find the server key
528 */
529 if ((errcode = krb5_dbe_find_enctype(kdc_context, server,
530 -1, /* ignore keytype */
531 -1, /* Ignore salttype */
532 0, /* Get highest kvno */
533 &server_key))) {
534 status = "FINDING_SERVER_KEY";
535 goto cleanup;
536 }
537
538 /*
539 * Convert server.key into a real key
540 * (it may be encrypted in the database)
541 */
542 if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
543 server_key, &server_keyblock,
544 NULL))) {
545 status = "DECRYPT_SERVER_KEY";
546 goto cleanup;
547 }
548 encrypting_key = &server_keyblock;
549 }
550
551 if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
552 /*
553 * Don't allow authorization data to be disabled if constrained
554 * delegation is requested. We don't want to deny the server
555 * the ability to validate that delegation was used.
556 */
557 clear(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
558 }
559 if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
560 /* If we are not doing protocol transition, try to look up the subject
561 * principal so that KDB modules can add additional authdata. */
562 if (!isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
563 /* Generate authorization data so we can include it in ticket */
564 setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
565 /* Map principals from foreign (possibly non-AD) realms */
566 setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);
567
568 assert(client == NULL); /* should not have been set already */
569
570 errcode = krb5_db_get_principal(kdc_context, subject_tkt->client,
571 c_flags, &client);
572 }
573 }
574
575 if (isflagset(c_flags, KRB5_KDB_FLAGS_S4U) && !is_referral)
576 enc_tkt_reply.client = altcprinc;
577 else
578 enc_tkt_reply.client = header_enc_tkt->client;
579
580 enc_tkt_reply.session = &session_key;
581 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
582 enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
583
584 /*
585 * Only add the realm of the presented tgt to the transited list if
586 * it is different than the local realm (cross-realm) and it is different
587 * than the realm of the client (since the realm of the client is already
588 * implicitly part of the transited list and should not be explicitly
589 * listed).
590 */
591 /* realm compare is like strcmp, but knows how to deal with these args */
592 if (krb5_realm_compare(kdc_context, header_ticket->server, tgs_server) ||
593 krb5_realm_compare(kdc_context, header_ticket->server,
594 enc_tkt_reply.client)) {
595 /* tgt issued by local realm or issued by realm of client */
596 enc_tkt_reply.transited = header_enc_tkt->transited;
597 } else {
598 /* tgt issued by some other realm and not the realm of the client */
599 /* assemble new transited field into allocated storage */
600 if (header_enc_tkt->transited.tr_type !=
601 KRB5_DOMAIN_X500_COMPRESS) {
602 status = "VALIDATE_TRANSIT_TYPE";
603 errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
604 goto cleanup;
605 }
606 memset(&enc_tkt_reply.transited, 0, sizeof(enc_tkt_reply.transited));
607 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
608 if ((errcode =
609 add_to_transited(&header_enc_tkt->transited.tr_contents,
610 &enc_tkt_reply.transited.tr_contents,
611 header_ticket->server,
612 enc_tkt_reply.client,
613 request->server))) {
614 status = "ADD_TO_TRANSITED_LIST";
615 goto cleanup;
616 }
617 newtransited = 1;
618 }
619 if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
620 errcode = kdc_check_transited_list (kdc_active_realm,
621 &enc_tkt_reply.transited.tr_contents,
622 krb5_princ_realm (kdc_context, header_enc_tkt->client),
623 krb5_princ_realm (kdc_context, request->server));
624 if (errcode == 0) {
625 setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED);
626 } else {
627 log_tgs_badtrans(kdc_context, cprinc, sprinc,
628 &enc_tkt_reply.transited.tr_contents, errcode);
629 }
630 } else
631 krb5_klog_syslog(LOG_INFO, _("not checking transit path"));
632 if (kdc_active_realm->realm_reject_bad_transit &&
633 !isflagset(enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
634 errcode = KRB5KDC_ERR_POLICY;
635 status = "BAD_TRANSIT";
636 au_state->violation = LOCAL_POLICY;
637 goto cleanup;
638 }
639
640 errcode = handle_authdata(kdc_context, c_flags, client, server,
641 subject_server, local_tgt, &local_tgt_key,
642 subkey != NULL ? subkey :
643 header_ticket->enc_part2->session,
644 encrypting_key, subject_key, pkt, request,
645 altcprinc, ad_info, subject_tkt,
646 &auth_indicators, &enc_tkt_reply);
647 if (errcode) {
648 krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"),
649 errcode);
650 status = "HANDLE_AUTHDATA";
651 goto cleanup;
652 }
653
654 ticket_reply.enc_part2 = &enc_tkt_reply;
655
656 /*
657 * If we are doing user-to-user authentication, then make sure
658 * that the client for the second ticket matches the request
659 * server, and then encrypt the ticket using the session key of
660 * the second ticket.
661 */
662 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
663 /*
664 * Make sure the client for the second ticket matches
665 * requested server.
666 */
667 krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
668 krb5_principal client2 = t2enc->client;
669 if (!krb5_principal_compare(kdc_context, request->server, client2)) {
670 altcprinc = client2;
671 errcode = KRB5KDC_ERR_SERVER_NOMATCH;
672 status = "2ND_TKT_MISMATCH";
673 au_state->status = status;
674 kau_u2u(kdc_context, FALSE, au_state);
675 goto cleanup;
676 }
677
678 ticket_kvno = 0;
679 ticket_reply.enc_part.enctype = t2enc->session->enctype;
680 kau_u2u(kdc_context, TRUE, au_state);
681 st_idx++;
682 } else {
683 ticket_kvno = server_key->key_data_kvno;
684 }
685
686 errcode = krb5_encrypt_tkt_part(kdc_context, encrypting_key,
687 &ticket_reply);
688 if (errcode)
689 goto cleanup;
690 ticket_reply.enc_part.kvno = ticket_kvno;
691 /* Start assembling the response */
692 au_state->stage = ENCR_REP;
693 reply.msg_type = KRB5_TGS_REP;
694 if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
695 krb5int_find_pa_data(kdc_context, request->padata,
696 KRB5_PADATA_S4U_X509_USER) != NULL) {
697 errcode = kdc_make_s4u2self_rep(kdc_context,
698 subkey,
699 header_ticket->enc_part2->session,
700 s4u_x509_user,
701 &reply,
702 &reply_encpart);
703 if (errcode)
704 au_state->status = status;
705 kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
706 if (errcode)
707 goto cleanup;
708 }
709
710 reply.client = enc_tkt_reply.client;
711 reply.enc_part.kvno = 0;/* We are using the session key */
712 reply.ticket = &ticket_reply;
713
714 reply_encpart.session = &session_key;
715 reply_encpart.nonce = request->nonce;
716
717 /* copy the time fields */
718 reply_encpart.times = enc_tkt_reply.times;
719
720 nolrentry.lr_type = KRB5_LRQ_NONE;
721 nolrentry.value = 0;
722 nolrentry.magic = 0;
723 nolrarray[0] = &nolrentry;
724 nolrarray[1] = 0;
725 reply_encpart.last_req = nolrarray; /* not available for TGS reqs */
726 reply_encpart.key_exp = 0;/* ditto */
727 reply_encpart.flags = enc_tkt_reply.flags;
728 reply_encpart.server = ticket_reply.server;
729
730 /* use the session key in the ticket, unless there's a subsession key
731 in the AP_REQ */
732 reply.enc_part.enctype = subkey ? subkey->enctype :
733 header_ticket->enc_part2->session->enctype;
734 errcode = kdc_fast_response_handle_padata(state, request, &reply,
735 subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype);
736 if (errcode)
737 goto cleanup;
738 errcode =kdc_fast_handle_reply_key(state,
739 subkey?subkey:header_ticket->enc_part2->session, &reply_key);
740 if (errcode)
741 goto cleanup;
742 errcode = return_enc_padata(kdc_context, pkt, request,
743 reply_key, server, &reply_encpart,
744 is_referral &&
745 isflagset(s_flags,
746 KRB5_KDB_FLAG_CANONICALIZE));
747 if (errcode) {
748 status = "KDC_RETURN_ENC_PADATA";
749 goto cleanup;
750 }
751
752 errcode = kau_make_tkt_id(kdc_context, &ticket_reply, &au_state->tkt_out_id);
753 if (errcode)
754 goto cleanup;
755
756 if (kdc_fast_hide_client(state))
757 reply.client = (krb5_principal)krb5_anonymous_principal();
758 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
759 subkey ? 1 : 0,
760 reply_key,
761 &reply, response);
762 if (!errcode)
763 status = "ISSUE";
764
765 memset(ticket_reply.enc_part.ciphertext.data, 0,
766 ticket_reply.enc_part.ciphertext.length);
767 free(ticket_reply.enc_part.ciphertext.data);
768 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
769 can use them in raw form if needed. But, we don't... */
770 memset(reply.enc_part.ciphertext.data, 0,
771 reply.enc_part.ciphertext.length);
772 free(reply.enc_part.ciphertext.data);
773
774 cleanup:
775 if (status == NULL)
776 status = "UNKNOWN_REASON";
777 krb5_free_keyblock_contents(kdc_context, &server_keyblock);
778 if (reply_key)
779 krb5_free_keyblock(kdc_context, reply_key);
780 if (stkt_server_key)
781 krb5_free_keyblock(kdc_context, stkt_server_key);
782 if (errcode)
783 emsg = krb5_get_error_message (kdc_context, errcode);
784
785 au_state->status = status;
786 if (!errcode)
787 au_state->reply = &reply;
788 kau_tgs_req(kdc_context, errcode ? FALSE : TRUE, au_state);
789 kau_free_kdc_req(au_state);
790
791 log_tgs_req(kdc_context, from, request, &reply, cprinc,
792 sprinc, altcprinc, authtime,
793 c_flags, status, errcode, emsg);
794 if (errcode) {
795 krb5_free_error_message (kdc_context, emsg);
796 emsg = NULL;
797 }
798
799 if (errcode) {
800 int got_err = 0;
801 if (status == 0) {
802 status = krb5_get_error_message (kdc_context, errcode);
803 got_err = 1;
804 }
805 errcode -= ERROR_TABLE_BASE_krb5;
806 if (errcode < 0 || errcode > KRB_ERR_MAX)
807 errcode = KRB_ERR_GENERIC;
808
809 retval = prepare_error_tgs(state, request, header_ticket, errcode,
810 (server != NULL) ? server->princ : NULL,
811 response, status, e_data);
812 if (got_err) {
813 krb5_free_error_message (kdc_context, status);
814 status = 0;
815 }
816 }
817
818 if (header_ticket != NULL)
819 krb5_free_ticket(kdc_context, header_ticket);
820 if (request != NULL)
821 krb5_free_kdc_req(kdc_context, request);
822 if (state)
823 kdc_free_rstate(state);
824 krb5_db_free_principal(kdc_context, server);
825 krb5_db_free_principal(kdc_context, stkt_server);
826 krb5_db_free_principal(kdc_context, header_server);
827 krb5_db_free_principal(kdc_context, client);
828 krb5_db_free_principal(kdc_context, local_tgt_storage);
829 if (local_tgt_key.contents != NULL)
830 krb5_free_keyblock_contents(kdc_context, &local_tgt_key);
831 if (session_key.contents != NULL)
832 krb5_free_keyblock_contents(kdc_context, &session_key);
833 if (newtransited)
834 free(enc_tkt_reply.transited.tr_contents.data);
835 if (s4u_x509_user != NULL)
836 krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
837 if (kdc_issued_auth_data != NULL)
838 krb5_free_authdata(kdc_context, kdc_issued_auth_data);
839 if (subkey != NULL)
840 krb5_free_keyblock(kdc_context, subkey);
841 if (header_key != NULL)
842 krb5_free_keyblock(kdc_context, header_key);
843 if (reply.padata)
844 krb5_free_pa_data(kdc_context, reply.padata);
845 if (reply_encpart.enc_padata)
846 krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);
847 if (enc_tkt_reply.authorization_data != NULL)
848 krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
849 krb5_free_pa_data(kdc_context, e_data);
850 k5_free_data_ptr_list(auth_indicators);
851 krb5_db_free_authdata_info(kdc_context, ad_info);
852 krb5_db_free_authdata_info(kdc_context, stkt_ad_info);
853 krb5_free_principal(kdc_context, stkt_authdata_client);
854
855 return retval;
856 }
857
858 static krb5_error_code
prepare_error_tgs(struct kdc_request_state * state,krb5_kdc_req * request,krb5_ticket * ticket,int error,krb5_principal canon_server,krb5_data ** response,const char * status,krb5_pa_data ** e_data)859 prepare_error_tgs (struct kdc_request_state *state,
860 krb5_kdc_req *request, krb5_ticket *ticket, int error,
861 krb5_principal canon_server,
862 krb5_data **response, const char *status,
863 krb5_pa_data **e_data)
864 {
865 krb5_error errpkt;
866 krb5_error_code retval = 0;
867 krb5_data *scratch, *e_data_asn1 = NULL, *fast_edata = NULL;
868 kdc_realm_t *kdc_active_realm = state->realm_data;
869
870 errpkt.magic = KV5M_ERROR;
871 errpkt.ctime = 0;
872 errpkt.cusec = 0;
873
874 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
875 &errpkt.susec)))
876 return(retval);
877 errpkt.error = error;
878 errpkt.server = request->server;
879 if (ticket && ticket->enc_part2)
880 errpkt.client = ticket->enc_part2->client;
881 else
882 errpkt.client = NULL;
883 errpkt.text.length = strlen(status);
884 if (!(errpkt.text.data = strdup(status)))
885 return ENOMEM;
886
887 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
888 free(errpkt.text.data);
889 return ENOMEM;
890 }
891
892 if (e_data != NULL) {
893 retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
894 if (retval) {
895 free(scratch);
896 free(errpkt.text.data);
897 return retval;
898 }
899 errpkt.e_data = *e_data_asn1;
900 } else
901 errpkt.e_data = empty_data();
902
903 retval = kdc_fast_handle_error(kdc_context, state, request, e_data,
904 &errpkt, &fast_edata);
905 if (retval) {
906 free(scratch);
907 free(errpkt.text.data);
908 krb5_free_data(kdc_context, e_data_asn1);
909 return retval;
910 }
911 if (fast_edata)
912 errpkt.e_data = *fast_edata;
913 if (kdc_fast_hide_client(state) && errpkt.client != NULL)
914 errpkt.client = (krb5_principal)krb5_anonymous_principal();
915 retval = krb5_mk_error(kdc_context, &errpkt, scratch);
916 free(errpkt.text.data);
917 krb5_free_data(kdc_context, e_data_asn1);
918 krb5_free_data(kdc_context, fast_edata);
919 if (retval)
920 free(scratch);
921 else
922 *response = scratch;
923
924 return retval;
925 }
926
927 /* KDC options that require a second ticket */
928 #define STKT_OPTIONS (KDC_OPT_CNAME_IN_ADDL_TKT | KDC_OPT_ENC_TKT_IN_SKEY)
929 /*
930 * Get the key for the second ticket, if any, and decrypt it.
931 */
932 static krb5_error_code
decrypt_2ndtkt(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_flags flags,krb5_db_entry ** server_out,krb5_keyblock ** key_out,const char ** status)933 decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
934 krb5_flags flags, krb5_db_entry **server_out,
935 krb5_keyblock **key_out, const char **status)
936 {
937 krb5_error_code retval;
938 krb5_db_entry *server = NULL;
939 krb5_kvno kvno;
940 krb5_ticket *stkt;
941
942 if (!(req->kdc_options & STKT_OPTIONS))
943 return 0;
944
945 stkt = req->second_ticket[0];
946 retval = kdc_get_server_key(kdc_context, stkt, flags, TRUE, &server,
947 key_out, &kvno);
948 if (retval != 0) {
949 *status = "2ND_TKT_SERVER";
950 goto cleanup;
951 }
952 retval = krb5_decrypt_tkt_part(kdc_context, *key_out,
953 req->second_ticket[0]);
954 if (retval != 0) {
955 *status = "2ND_TKT_DECRYPT";
956 goto cleanup;
957 }
958 *server_out = server;
959 server = NULL;
960 cleanup:
961 krb5_db_free_principal(kdc_context, server);
962 return retval;
963 }
964
965 static krb5_error_code
get_2ndtkt_enctype(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_enctype * useenctype,const char ** status)966 get_2ndtkt_enctype(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
967 krb5_enctype *useenctype, const char **status)
968 {
969 krb5_enctype etype;
970 krb5_ticket *stkt = req->second_ticket[0];
971 int i;
972
973 etype = stkt->enc_part2->session->enctype;
974 if (!krb5_c_valid_enctype(etype)) {
975 *status = "BAD_ETYPE_IN_2ND_TKT";
976 return KRB5KDC_ERR_ETYPE_NOSUPP;
977 }
978 for (i = 0; i < req->nktypes; i++) {
979 if (req->ktype[i] == etype) {
980 *useenctype = etype;
981 break;
982 }
983 }
984 return 0;
985 }
986
987 static krb5_error_code
gen_session_key(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_db_entry * server,krb5_keyblock * skey,const char ** status)988 gen_session_key(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
989 krb5_db_entry *server, krb5_keyblock *skey,
990 const char **status)
991 {
992 krb5_error_code retval;
993 krb5_enctype useenctype = 0;
994
995 /*
996 * Some special care needs to be taken in the user-to-user
997 * case, since we don't know what keytypes the application server
998 * which is doing user-to-user authentication can support. We
999 * know that it at least must be able to support the encryption
1000 * type of the session key in the TGT, since otherwise it won't be
1001 * able to decrypt the U2U ticket! So we use that in preference
1002 * to anything else.
1003 */
1004 if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
1005 retval = get_2ndtkt_enctype(kdc_active_realm, req, &useenctype,
1006 status);
1007 if (retval != 0)
1008 return retval;
1009 }
1010 if (useenctype == 0) {
1011 useenctype = select_session_keytype(kdc_active_realm, server,
1012 req->nktypes,
1013 req->ktype);
1014 }
1015 if (useenctype == 0) {
1016 /* unsupported ktype */
1017 *status = "BAD_ENCRYPTION_TYPE";
1018 return KRB5KDC_ERR_ETYPE_NOSUPP;
1019 }
1020
1021 return krb5_c_make_random_key(kdc_context, useenctype, skey);
1022 }
1023
1024 /*
1025 * The request seems to be for a ticket-granting service somewhere else,
1026 * but we don't have a ticket for the final TGS. Try to give the requestor
1027 * some intermediate realm.
1028 */
1029 static krb5_error_code
find_alternate_tgs(kdc_realm_t * kdc_active_realm,krb5_principal princ,krb5_db_entry ** server_ptr,const char ** status)1030 find_alternate_tgs(kdc_realm_t *kdc_active_realm, krb5_principal princ,
1031 krb5_db_entry **server_ptr, const char **status)
1032 {
1033 krb5_error_code retval;
1034 krb5_principal *plist = NULL, *pl2;
1035 krb5_data tmp;
1036 krb5_db_entry *server = NULL;
1037
1038 *server_ptr = NULL;
1039 assert(is_cross_tgs_principal(princ));
1040 if ((retval = krb5_walk_realm_tree(kdc_context,
1041 krb5_princ_realm(kdc_context, princ),
1042 krb5_princ_component(kdc_context, princ, 1),
1043 &plist, KRB5_REALM_BRANCH_CHAR))) {
1044 goto cleanup;
1045 }
1046 /* move to the end */
1047 for (pl2 = plist; *pl2; pl2++);
1048
1049 /* the first entry in this array is for krbtgt/local@local, so we
1050 ignore it */
1051 while (--pl2 > plist) {
1052 tmp = *krb5_princ_realm(kdc_context, *pl2);
1053 krb5_princ_set_realm(kdc_context, *pl2,
1054 krb5_princ_realm(kdc_context, princ));
1055 retval = db_get_svc_princ(kdc_context, *pl2, 0, &server, status);
1056 krb5_princ_set_realm(kdc_context, *pl2, &tmp);
1057 if (retval == KRB5_KDB_NOENTRY)
1058 continue;
1059 else if (retval)
1060 goto cleanup;
1061
1062 log_tgs_alt_tgt(kdc_context, server->princ);
1063 *server_ptr = server;
1064 server = NULL;
1065 goto cleanup;
1066 }
1067 cleanup:
1068 if (retval == 0 && *server_ptr == NULL)
1069 retval = KRB5_KDB_NOENTRY;
1070 if (retval != 0)
1071 *status = "UNKNOWN_SERVER";
1072
1073 krb5_free_realm_tree(kdc_context, plist);
1074 krb5_db_free_principal(kdc_context, server);
1075 return retval;
1076 }
1077
1078 /* Return true if item is an element of the space/comma-separated list. */
1079 static krb5_boolean
in_list(const char * list,const char * item)1080 in_list(const char *list, const char *item)
1081 {
1082 const char *p;
1083 int len = strlen(item);
1084
1085 if (list == NULL)
1086 return FALSE;
1087 for (p = strstr(list, item); p != NULL; p = strstr(p + 1, item)) {
1088 if ((p == list || isspace((unsigned char)p[-1]) || p[-1] == ',') &&
1089 (p[len] == '\0' || isspace((unsigned char)p[len]) ||
1090 p[len] == ','))
1091 return TRUE;
1092 }
1093 return FALSE;
1094 }
1095
1096 /*
1097 * Check whether the request satisfies the conditions for generating a referral
1098 * TGT. The caller checks whether the hostname component looks like a FQDN.
1099 */
1100 static krb5_boolean
is_referral_req(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request)1101 is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request)
1102 {
1103 krb5_boolean ret = FALSE;
1104 char *stype = NULL;
1105 char *hostbased = kdc_active_realm->realm_hostbased;
1106 char *no_referral = kdc_active_realm->realm_no_referral;
1107
1108 if (!(request->kdc_options & KDC_OPT_CANONICALIZE))
1109 return FALSE;
1110
1111 if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)
1112 return FALSE;
1113
1114 if (krb5_princ_size(kdc_context, request->server) != 2)
1115 return FALSE;
1116
1117 stype = data2string(krb5_princ_component(kdc_context, request->server, 0));
1118 if (stype == NULL)
1119 return FALSE;
1120 switch (krb5_princ_type(kdc_context, request->server)) {
1121 case KRB5_NT_UNKNOWN:
1122 /* Allow referrals for NT-UNKNOWN principals, if configured. */
1123 if (!in_list(hostbased, stype) && !in_list(hostbased, "*"))
1124 goto cleanup;
1125 /* FALLTHROUGH */
1126 case KRB5_NT_SRV_HST:
1127 case KRB5_NT_SRV_INST:
1128 /* Deny referrals for specific service types, if configured. */
1129 if (in_list(no_referral, stype) || in_list(no_referral, "*"))
1130 goto cleanup;
1131 ret = TRUE;
1132 break;
1133 default:
1134 goto cleanup;
1135 }
1136 cleanup:
1137 free(stype);
1138 return ret;
1139 }
1140
1141 /*
1142 * Find a remote realm TGS principal for an unknown host-based service
1143 * principal.
1144 */
1145 static krb5_int32
find_referral_tgs(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request,krb5_principal * krbtgt_princ)1146 find_referral_tgs(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request,
1147 krb5_principal *krbtgt_princ)
1148 {
1149 krb5_error_code retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1150 char **realms = NULL, *hostname = NULL;
1151 krb5_data srealm = request->server->realm;
1152
1153 if (!is_referral_req(kdc_active_realm, request))
1154 goto cleanup;
1155
1156 hostname = data2string(krb5_princ_component(kdc_context,
1157 request->server, 1));
1158 if (hostname == NULL) {
1159 retval = ENOMEM;
1160 goto cleanup;
1161 }
1162 /* If the hostname doesn't contain a '.', it's not a FQDN. */
1163 if (strchr(hostname, '.') == NULL)
1164 goto cleanup;
1165 retval = krb5_get_host_realm(kdc_context, hostname, &realms);
1166 if (retval) {
1167 /* no match found */
1168 kdc_err(kdc_context, retval, "unable to find realm of host");
1169 goto cleanup;
1170 }
1171 /* Don't return a referral to the empty realm or the service realm. */
1172 if (realms == NULL || realms[0] == NULL || *realms[0] == '\0' ||
1173 data_eq_string(srealm, realms[0])) {
1174 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1175 goto cleanup;
1176 }
1177 retval = krb5_build_principal(kdc_context, krbtgt_princ,
1178 srealm.length, srealm.data,
1179 "krbtgt", realms[0], (char *)0);
1180 cleanup:
1181 krb5_free_host_realm(kdc_context, realms);
1182 free(hostname);
1183
1184 return retval;
1185 }
1186
1187 static krb5_error_code
db_get_svc_princ(krb5_context ctx,krb5_principal princ,krb5_flags flags,krb5_db_entry ** server,const char ** status)1188 db_get_svc_princ(krb5_context ctx, krb5_principal princ,
1189 krb5_flags flags, krb5_db_entry **server,
1190 const char **status)
1191 {
1192 krb5_error_code ret;
1193
1194 ret = krb5_db_get_principal(ctx, princ, flags, server);
1195 if (ret == KRB5_KDB_CANTLOCK_DB)
1196 ret = KRB5KDC_ERR_SVC_UNAVAILABLE;
1197 if (ret != 0) {
1198 *status = "LOOKING_UP_SERVER";
1199 }
1200 return ret;
1201 }
1202
1203 static krb5_error_code
search_sprinc(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_flags flags,krb5_db_entry ** server,const char ** status)1204 search_sprinc(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
1205 krb5_flags flags, krb5_db_entry **server, const char **status)
1206 {
1207 krb5_error_code ret;
1208 krb5_principal princ = req->server;
1209 krb5_principal reftgs = NULL;
1210 krb5_boolean allow_referral;
1211
1212 /* Do not allow referrals for u2u or ticket modification requests, because
1213 * the server is supposed to match an already-issued ticket. */
1214 allow_referral = !(req->kdc_options & NO_REFERRAL_OPTION);
1215 if (!allow_referral)
1216 flags &= ~KRB5_KDB_FLAG_CANONICALIZE;
1217
1218 ret = db_get_svc_princ(kdc_context, princ, flags, server, status);
1219 if (ret == 0 || ret != KRB5_KDB_NOENTRY || !allow_referral)
1220 goto cleanup;
1221
1222 if (!is_cross_tgs_principal(req->server)) {
1223 ret = find_referral_tgs(kdc_active_realm, req, &reftgs);
1224 if (ret != 0)
1225 goto cleanup;
1226 ret = db_get_svc_princ(kdc_context, reftgs, flags, server, status);
1227 if (ret == 0 || ret != KRB5_KDB_NOENTRY)
1228 goto cleanup;
1229
1230 princ = reftgs;
1231 }
1232 ret = find_alternate_tgs(kdc_active_realm, princ, server, status);
1233
1234 cleanup:
1235 if (ret != 0 && ret != KRB5KDC_ERR_SVC_UNAVAILABLE) {
1236 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1237 if (*status == NULL)
1238 *status = "LOOKING_UP_SERVER";
1239 }
1240 krb5_free_principal(kdc_context, reftgs);
1241 return ret;
1242 }
1243