Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/do_as_req.c
34869 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* kdc/do_as_req.c */
3
/*
4
* Portions Copyright (C) 2007 Apple Inc.
5
* Copyright 1990, 1991, 2007, 2008, 2009, 2013, 2014 by the
6
* Massachusetts Institute of Technology. All Rights Reserved.
7
*
8
* Export of this software from the United States of America may
9
* require a specific license from the United States Government.
10
* It is the responsibility of any person or organization contemplating
11
* export to obtain such a license before exporting.
12
*
13
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14
* distribute this software and its documentation for any purpose and
15
* without fee is hereby granted, provided that the above copyright
16
* notice appear in all copies and that both that copyright notice and
17
* this permission notice appear in supporting documentation, and that
18
* the name of M.I.T. not be used in advertising or publicity pertaining
19
* to distribution of the software without specific, written prior
20
* permission. Furthermore if you modify this software you must label
21
* your software as modified software and not distribute it in such a
22
* fashion that it might be confused with the original M.I.T. software.
23
* M.I.T. makes no representations about the suitability of
24
* this software for any purpose. It is provided "as is" without express
25
* or implied warranty.
26
*
27
*
28
* KDC Routines to deal with AS_REQ's
29
*/
30
/*
31
* Copyright (c) 2006-2008, Novell, Inc.
32
* All rights reserved.
33
*
34
* Redistribution and use in source and binary forms, with or without
35
* modification, are permitted provided that the following conditions are met:
36
*
37
* * Redistributions of source code must retain the above copyright notice,
38
* this list of conditions and the following disclaimer.
39
* * Redistributions in binary form must reproduce the above copyright
40
* notice, this list of conditions and the following disclaimer in the
41
* documentation and/or other materials provided with the distribution.
42
* * The copyright holder's name is not used to endorse or promote products
43
* derived from this software without specific prior written permission.
44
*
45
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
46
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
49
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55
* POSSIBILITY OF SUCH DAMAGE.
56
*/
57
58
#include "k5-int.h"
59
#include "com_err.h"
60
61
#include <syslog.h>
62
#ifdef HAVE_NETINET_IN_H
63
#include <sys/types.h>
64
#include <netinet/in.h>
65
#ifndef hpux
66
#include <arpa/inet.h>
67
#endif /* hpux */
68
#endif /* HAVE_NETINET_IN_H */
69
70
#include "kdc_util.h"
71
#include "kdc_audit.h"
72
#include "policy.h"
73
#include <kadm5/admin.h>
74
#include "adm_proto.h"
75
#include "extern.h"
76
77
static krb5_error_code
78
prepare_error_as(struct kdc_request_state *, krb5_kdc_req *, krb5_db_entry *,
79
krb5_keyblock *, krb5_error_code, krb5_pa_data **,
80
krb5_boolean, krb5_principal, krb5_data **, const char *);
81
82
/* Determine the key-expiration value according to RFC 4120 section 5.4.2. */
83
static krb5_timestamp
84
get_key_exp(krb5_db_entry *entry)
85
{
86
if (entry->expiration == 0)
87
return entry->pw_expiration;
88
if (entry->pw_expiration == 0)
89
return entry->expiration;
90
return ts_min(entry->expiration, entry->pw_expiration);
91
}
92
93
/*
94
* Find the key in client for the most preferred enctype in req_enctypes. Fill
95
* in *kb_out with the decrypted keyblock (which the caller must free) and set
96
* *kd_out to an alias to that key data entry. Set *kd_out to NULL and leave
97
* *kb_out zeroed if no key is found for any of the requested enctypes.
98
* kb_out->enctype may differ from the enctype of *kd_out for DES enctypes; in
99
* this case, kb_out->enctype is the requested enctype used to match the key
100
* data entry.
101
*/
102
static krb5_error_code
103
select_client_key(krb5_context context, krb5_db_entry *client,
104
krb5_enctype *req_enctypes, int n_req_enctypes,
105
krb5_keyblock *kb_out, krb5_key_data **kd_out)
106
{
107
krb5_error_code ret;
108
krb5_key_data *kd;
109
krb5_enctype etype;
110
int i;
111
112
memset(kb_out, 0, sizeof(*kb_out));
113
*kd_out = NULL;
114
115
for (i = 0; i < n_req_enctypes; i++) {
116
etype = req_enctypes[i];
117
if (!krb5_c_valid_enctype(etype))
118
continue;
119
if (krb5_dbe_find_enctype(context, client, etype, -1, 0, &kd) == 0) {
120
/* Decrypt the client key data and set its enctype to the request
121
* enctype (which may differ from the key data enctype for DES). */
122
ret = krb5_dbe_decrypt_key_data(context, NULL, kd, kb_out, NULL);
123
if (ret)
124
return ret;
125
kb_out->enctype = etype;
126
*kd_out = kd;
127
return 0;
128
}
129
}
130
return 0;
131
}
132
133
static krb5_error_code
134
lookup_client(krb5_context context, krb5_kdc_req *req, unsigned int flags,
135
krb5_db_entry **entry_out)
136
{
137
krb5_pa_data *pa;
138
krb5_data cert;
139
140
*entry_out = NULL;
141
pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_S4U_X509_USER);
142
if (pa != NULL && pa->length != 0 &&
143
req->client->type == KRB5_NT_X500_PRINCIPAL) {
144
cert = make_data(pa->contents, pa->length);
145
flags |= KRB5_KDB_FLAG_REFERRAL_OK;
146
return krb5_db_get_s4u_x509_principal(context, &cert, req->client,
147
flags, entry_out);
148
} else {
149
return krb5_db_get_principal(context, req->client, flags, entry_out);
150
}
151
}
152
153
struct as_req_state {
154
loop_respond_fn respond;
155
void *arg;
156
157
krb5_principal_data client_princ;
158
krb5_enc_tkt_part enc_tkt_reply;
159
krb5_enc_kdc_rep_part reply_encpart;
160
krb5_ticket ticket_reply;
161
krb5_keyblock local_tgt_key;
162
krb5_keyblock server_keyblock;
163
krb5_keyblock client_keyblock;
164
krb5_db_entry *client;
165
krb5_db_entry *server;
166
krb5_db_entry *local_tgt;
167
krb5_db_entry *local_tgt_storage;
168
krb5_key_data *client_key;
169
krb5_kdc_req *request;
170
struct krb5_kdcpreauth_rock_st rock;
171
const char *status;
172
krb5_pa_data **e_data;
173
krb5_boolean typed_e_data;
174
krb5_kdc_rep reply;
175
krb5_timestamp kdc_time;
176
krb5_keyblock session_key;
177
unsigned int c_flags;
178
krb5_data *req_pkt;
179
krb5_data *inner_body;
180
struct kdc_request_state *rstate;
181
char *sname, *cname;
182
void *pa_context;
183
const struct sockaddr *local_addr;
184
const struct sockaddr *remote_addr;
185
krb5_data **auth_indicators;
186
187
krb5_error_code preauth_err;
188
189
kdc_realm_t *active_realm;
190
krb5_audit_state *au_state;
191
};
192
193
static void
194
finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
195
{
196
kdc_realm_t *realm = state->active_realm;
197
krb5_context context = realm->realm_context;
198
krb5_keyblock *as_encrypting_key = NULL;
199
krb5_data *response = NULL;
200
const char *emsg = 0;
201
int did_log = 0;
202
loop_respond_fn oldrespond;
203
void *oldarg;
204
krb5_audit_state *au_state = state->au_state;
205
krb5_keyblock *replaced_reply_key = NULL;
206
207
assert(state);
208
oldrespond = state->respond;
209
oldarg = state->arg;
210
211
if (errcode)
212
goto egress;
213
214
au_state->stage = ENCR_REP;
215
216
state->ticket_reply.enc_part2 = &state->enc_tkt_reply;
217
218
errcode = check_kdcpolicy_as(context, state->request, state->client,
219
state->server, state->auth_indicators,
220
state->kdc_time, &state->enc_tkt_reply.times,
221
&state->status);
222
if (errcode)
223
goto egress;
224
225
errcode = get_first_current_key(context, state->server,
226
&state->server_keyblock);
227
if (errcode) {
228
state->status = "FINDING_SERVER_KEY";
229
goto egress;
230
}
231
232
/* Start assembling the response */
233
state->reply.msg_type = KRB5_AS_REP;
234
state->reply.client = state->enc_tkt_reply.client; /* post canonization */
235
state->reply.ticket = &state->ticket_reply;
236
state->reply_encpart.session = &state->session_key;
237
if ((errcode = fetch_last_req_info(state->client,
238
&state->reply_encpart.last_req)))
239
goto egress;
240
state->reply_encpart.nonce = state->request->nonce;
241
state->reply_encpart.key_exp = get_key_exp(state->client);
242
state->reply_encpart.flags = state->enc_tkt_reply.flags;
243
state->reply_encpart.server = state->ticket_reply.server;
244
state->reply_encpart.times = state->enc_tkt_reply.times;
245
state->reply_encpart.caddrs = state->enc_tkt_reply.caddrs;
246
state->reply_encpart.enc_padata = NULL;
247
248
/* Fetch the padata info to be returned (do this before
249
* authdata to handle possible replacement of reply key
250
*/
251
errcode = return_padata(context, &state->rock, state->req_pkt,
252
state->request, &state->reply,
253
&state->client_keyblock, &state->pa_context);
254
if (errcode) {
255
state->status = "KDC_RETURN_PADATA";
256
goto egress;
257
}
258
259
/* If we didn't find a client long-term key and no preauth mechanism
260
* replaced the reply key, error out now. */
261
if (state->client_keyblock.enctype == ENCTYPE_NULL) {
262
state->status = "CANT_FIND_CLIENT_KEY";
263
errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
264
goto egress;
265
}
266
267
if (state->rock.replaced_reply_key)
268
replaced_reply_key = &state->client_keyblock;
269
270
errcode = handle_authdata(realm, state->c_flags, state->client,
271
state->server, NULL, state->local_tgt,
272
&state->local_tgt_key, &state->client_keyblock,
273
&state->server_keyblock, NULL,
274
replaced_reply_key, state->req_pkt,
275
state->request, NULL, NULL, NULL,
276
&state->auth_indicators, &state->enc_tkt_reply);
277
if (errcode) {
278
krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"),
279
errcode);
280
state->status = "HANDLE_AUTHDATA";
281
goto egress;
282
}
283
284
errcode = check_indicators(context, state->server, state->auth_indicators);
285
if (errcode) {
286
state->status = "HIGHER_AUTHENTICATION_REQUIRED";
287
goto egress;
288
}
289
290
errcode = krb5_encrypt_tkt_part(context, &state->server_keyblock,
291
&state->ticket_reply);
292
if (errcode)
293
goto egress;
294
295
errcode = kau_make_tkt_id(context, &state->ticket_reply,
296
&au_state->tkt_out_id);
297
if (errcode)
298
goto egress;
299
300
state->ticket_reply.enc_part.kvno = current_kvno(state->server);
301
errcode = kdc_fast_response_handle_padata(state->rstate,
302
state->request,
303
&state->reply,
304
state->client_keyblock.enctype);
305
if (errcode)
306
goto egress;
307
308
/* now encode/encrypt the response */
309
310
state->reply.enc_part.enctype = state->client_keyblock.enctype;
311
312
errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock,
313
&as_encrypting_key);
314
if (errcode)
315
goto egress;
316
errcode = return_enc_padata(context, state->req_pkt, state->request,
317
as_encrypting_key, state->server,
318
&state->reply_encpart, FALSE);
319
if (errcode) {
320
state->status = "KDC_RETURN_ENC_PADATA";
321
goto egress;
322
}
323
324
if (kdc_fast_hide_client(state->rstate))
325
state->reply.client = (krb5_principal)krb5_anonymous_principal();
326
errcode = krb5_encode_kdc_rep(context, KRB5_AS_REP, &state->reply_encpart,
327
0, as_encrypting_key, &state->reply,
328
&response);
329
if (state->client_key != NULL)
330
state->reply.enc_part.kvno = state->client_key->key_data_kvno;
331
if (errcode)
332
goto egress;
333
334
/* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
335
can use them in raw form if needed. But, we don't... */
336
memset(state->reply.enc_part.ciphertext.data, 0,
337
state->reply.enc_part.ciphertext.length);
338
free(state->reply.enc_part.ciphertext.data);
339
340
log_as_req(context, state->local_addr, state->remote_addr,
341
state->request, &state->reply, state->client, state->cname,
342
state->server, state->sname, state->kdc_time, 0, 0, 0);
343
did_log = 1;
344
345
egress:
346
if (errcode != 0 && state->status == NULL)
347
state->status = "UNKNOWN_REASON";
348
349
au_state->status = state->status;
350
au_state->reply = &state->reply;
351
kau_as_req(context, (errcode || state->preauth_err) ? FALSE : TRUE,
352
au_state);
353
kau_free_kdc_req(au_state);
354
355
free_padata_context(context, state->pa_context);
356
if (as_encrypting_key)
357
krb5_free_keyblock(context, as_encrypting_key);
358
if (errcode)
359
emsg = krb5_get_error_message(context, errcode);
360
361
if (state->status) {
362
log_as_req(context, state->local_addr, state->remote_addr,
363
state->request, &state->reply, state->client,
364
state->cname, state->server, state->sname, state->kdc_time,
365
state->status, errcode, emsg);
366
did_log = 1;
367
}
368
if (errcode) {
369
if (state->status == 0) {
370
state->status = emsg;
371
}
372
if (errcode != KRB5KDC_ERR_DISCARD) {
373
errcode = prepare_error_as(state->rstate, state->request,
374
state->local_tgt, &state->local_tgt_key,
375
errcode, state->e_data,
376
state->typed_e_data,
377
((state->client != NULL) ?
378
state->client->princ : NULL),
379
&response, state->status);
380
state->status = 0;
381
}
382
}
383
384
if (emsg)
385
krb5_free_error_message(context, emsg);
386
if (state->enc_tkt_reply.authorization_data != NULL)
387
krb5_free_authdata(context, state->enc_tkt_reply.authorization_data);
388
if (state->local_tgt_key.contents != NULL)
389
krb5_free_keyblock_contents(context, &state->local_tgt_key);
390
if (state->server_keyblock.contents != NULL)
391
krb5_free_keyblock_contents(context, &state->server_keyblock);
392
if (state->client_keyblock.contents != NULL)
393
krb5_free_keyblock_contents(context, &state->client_keyblock);
394
if (state->reply.padata != NULL)
395
krb5_free_pa_data(context, state->reply.padata);
396
if (state->reply_encpart.enc_padata)
397
krb5_free_pa_data(context, state->reply_encpart.enc_padata);
398
399
if (state->cname != NULL)
400
free(state->cname);
401
if (state->sname != NULL)
402
free(state->sname);
403
krb5_db_free_principal(context, state->client);
404
krb5_db_free_principal(context, state->server);
405
krb5_db_free_principal(context, state->local_tgt_storage);
406
if (state->session_key.contents != NULL)
407
krb5_free_keyblock_contents(context, &state->session_key);
408
if (state->ticket_reply.enc_part.ciphertext.data != NULL) {
409
memset(state->ticket_reply.enc_part.ciphertext.data , 0,
410
state->ticket_reply.enc_part.ciphertext.length);
411
free(state->ticket_reply.enc_part.ciphertext.data);
412
}
413
414
krb5_free_pa_data(context, state->e_data);
415
krb5_free_data(context, state->inner_body);
416
kdc_free_rstate(state->rstate);
417
krb5_free_kdc_req(context, state->request);
418
k5_free_data_ptr_list(state->auth_indicators);
419
assert(did_log != 0);
420
421
free(state);
422
(*oldrespond)(oldarg, errcode, response);
423
}
424
425
static void
426
finish_missing_required_preauth(void *arg)
427
{
428
struct as_req_state *state = (struct as_req_state *)arg;
429
430
finish_process_as_req(state, state->preauth_err);
431
}
432
433
static void
434
finish_preauth(void *arg, krb5_error_code code)
435
{
436
struct as_req_state *state = arg;
437
krb5_error_code real_code = code;
438
439
if (code) {
440
if (vague_errors)
441
code = KRB5KRB_ERR_GENERIC;
442
state->status = "PREAUTH_FAILED";
443
if (real_code == KRB5KDC_ERR_PREAUTH_FAILED) {
444
state->preauth_err = code;
445
get_preauth_hint_list(state->request, &state->rock, &state->e_data,
446
finish_missing_required_preauth, state);
447
return;
448
}
449
} else {
450
/*
451
* Final check before handing out ticket: If the client requires
452
* preauthentication, verify that the proper kind of
453
* preauthentication was carried out.
454
*/
455
state->status = missing_required_preauth(state->client, state->server,
456
&state->enc_tkt_reply);
457
if (state->status) {
458
state->preauth_err = KRB5KDC_ERR_PREAUTH_REQUIRED;
459
get_preauth_hint_list(state->request, &state->rock, &state->e_data,
460
finish_missing_required_preauth, state);
461
return;
462
}
463
}
464
465
finish_process_as_req(state, code);
466
}
467
468
/*ARGSUSED*/
469
void
470
process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
471
const struct sockaddr *local_addr,
472
const struct sockaddr *remote_addr, kdc_realm_t *realm,
473
verto_ctx *vctx, loop_respond_fn respond, void *arg)
474
{
475
krb5_context context = realm->realm_context;
476
krb5_error_code errcode;
477
krb5_data encoded_req_body;
478
krb5_enctype useenctype;
479
struct as_req_state *state;
480
krb5_audit_state *au_state = NULL;
481
482
state = k5alloc(sizeof(*state), &errcode);
483
if (state == NULL) {
484
(*respond)(arg, errcode, NULL);
485
return;
486
}
487
state->respond = respond;
488
state->arg = arg;
489
state->request = request;
490
state->req_pkt = req_pkt;
491
state->local_addr = local_addr;
492
state->remote_addr = remote_addr;
493
state->active_realm = realm;
494
495
errcode = kdc_make_rstate(realm, &state->rstate);
496
if (errcode != 0) {
497
(*respond)(arg, errcode, NULL);
498
free(state);
499
return;
500
}
501
502
/* Initialize audit state. */
503
errcode = kau_init_kdc_req(context, state->request, remote_addr,
504
&au_state);
505
if (errcode) {
506
(*respond)(arg, errcode, NULL);
507
kdc_free_rstate(state->rstate);
508
free(state);
509
return;
510
}
511
state->au_state = au_state;
512
513
if (state->request->msg_type != KRB5_AS_REQ) {
514
state->status = "VALIDATE_MESSAGE_TYPE";
515
errcode = KRB5_BADMSGTYPE;
516
goto errout;
517
}
518
519
/* Seed the audit trail with the request ID and basic information. */
520
kau_as_req(context, TRUE, au_state);
521
522
errcode = krb5_timeofday(context, &state->kdc_time);
523
if (errcode)
524
goto errout;
525
526
if (fetch_asn1_field((unsigned char *) req_pkt->data,
527
1, 4, &encoded_req_body) != 0) {
528
errcode = ASN1_BAD_ID;
529
goto errout;
530
}
531
errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL,
532
state->rstate, &state->inner_body);
533
if (errcode) {
534
state->status = "FIND_FAST";
535
goto errout;
536
}
537
if (state->inner_body == NULL) {
538
/* Not a FAST request; copy the encoded request body. */
539
errcode = krb5_copy_data(context, &encoded_req_body,
540
&state->inner_body);
541
if (errcode)
542
goto errout;
543
}
544
au_state->request = state->request;
545
state->rock.request = state->request;
546
state->rock.inner_body = state->inner_body;
547
state->rock.rstate = state->rstate;
548
state->rock.vctx = vctx;
549
state->rock.auth_indicators = &state->auth_indicators;
550
state->rock.send_freshness_token = FALSE;
551
if (!state->request->client) {
552
state->status = "NULL_CLIENT";
553
errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
554
goto errout;
555
}
556
errcode = krb5_unparse_name(context, state->request->client,
557
&state->cname);
558
if (errcode)
559
goto errout;
560
limit_string(state->cname);
561
562
if (!state->request->server) {
563
state->status = "NULL_SERVER";
564
errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
565
goto errout;
566
}
567
errcode = krb5_unparse_name(context, state->request->server,
568
&state->sname);
569
if (errcode)
570
goto errout;
571
limit_string(state->sname);
572
573
setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT);
574
if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) ||
575
state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL)
576
setflag(state->c_flags, KRB5_KDB_FLAG_REFERRAL_OK);
577
errcode = lookup_client(context, state->request, state->c_flags,
578
&state->client);
579
if (errcode == KRB5_KDB_CANTLOCK_DB)
580
errcode = KRB5KDC_ERR_SVC_UNAVAILABLE;
581
if (errcode == KRB5_KDB_NOENTRY) {
582
state->status = "CLIENT_NOT_FOUND";
583
if (vague_errors)
584
errcode = KRB5KRB_ERR_GENERIC;
585
else
586
errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
587
goto errout;
588
} else if (errcode) {
589
state->status = "LOOKING_UP_CLIENT";
590
goto errout;
591
}
592
state->rock.client = state->client;
593
594
au_state->stage = SRVC_PRINC;
595
596
errcode = krb5_db_get_principal(context, state->request->server, 0,
597
&state->server);
598
if (errcode == KRB5_KDB_CANTLOCK_DB)
599
errcode = KRB5KDC_ERR_SVC_UNAVAILABLE;
600
if (errcode == KRB5_KDB_NOENTRY) {
601
state->status = "SERVER_NOT_FOUND";
602
errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
603
goto errout;
604
} else if (errcode) {
605
state->status = "LOOKING_UP_SERVER";
606
goto errout;
607
}
608
609
/* If the KDB module returned a different realm for the client and server,
610
* we need to issue a client realm referral. */
611
if (!data_eq(state->server->princ->realm, state->client->princ->realm)) {
612
state->status = "REFERRAL";
613
au_state->cl_realm = &state->client->princ->realm;
614
errcode = KRB5KDC_ERR_WRONG_REALM;
615
goto errout;
616
}
617
618
errcode = get_local_tgt(context, &state->request->server->realm,
619
state->server, &state->local_tgt,
620
&state->local_tgt_storage, &state->local_tgt_key);
621
if (errcode) {
622
state->status = "GET_LOCAL_TGT";
623
goto errout;
624
}
625
state->rock.local_tgt = state->local_tgt;
626
state->rock.local_tgt_key = &state->local_tgt_key;
627
628
au_state->stage = VALIDATE_POL;
629
630
errcode = validate_as_request(realm, state->request, state->client,
631
state->server, state->kdc_time,
632
&state->status, &state->e_data);
633
if (errcode)
634
goto errout;
635
636
au_state->stage = ISSUE_TKT;
637
638
/*
639
* Select the keytype for the ticket session key.
640
*/
641
useenctype = select_session_keytype(context, state->server,
642
state->request->nktypes,
643
state->request->ktype);
644
if (useenctype == 0) {
645
/* unsupported ktype */
646
state->status = "BAD_ENCRYPTION_TYPE";
647
errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
648
goto errout;
649
}
650
651
errcode = krb5_c_make_random_key(context, useenctype, &state->session_key);
652
if (errcode)
653
goto errout;
654
655
/*
656
* Canonicalization is only effective if we are issuing a TGT
657
* (the intention is to allow support for Windows "short" realm
658
* aliases, nothing more).
659
*/
660
if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) &&
661
krb5_is_tgs_principal(state->request->server) &&
662
krb5_is_tgs_principal(state->server->princ)) {
663
state->ticket_reply.server = state->server->princ;
664
} else {
665
state->ticket_reply.server = state->request->server;
666
}
667
668
/* Copy options that request the corresponding ticket flags. */
669
state->enc_tkt_reply.flags = get_ticket_flags(state->request->kdc_options,
670
state->client, state->server,
671
NULL);
672
state->enc_tkt_reply.times.authtime = state->kdc_time;
673
674
/*
675
* It should be noted that local policy may affect the
676
* processing of any of these flags. For example, some
677
* realms may refuse to issue renewable tickets
678
*/
679
680
state->enc_tkt_reply.session = &state->session_key;
681
if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) {
682
state->client_princ = *(state->client->princ);
683
} else {
684
state->client_princ = *(state->request->client);
685
/* The realm is always canonicalized */
686
state->client_princ.realm = state->client->princ->realm;
687
}
688
state->enc_tkt_reply.client = &state->client_princ;
689
state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
690
state->enc_tkt_reply.transited.tr_contents = empty_string;
691
692
if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED))
693
state->enc_tkt_reply.times.starttime = state->request->from;
694
else
695
state->enc_tkt_reply.times.starttime = state->kdc_time;
696
697
kdc_get_ticket_endtime(realm, state->enc_tkt_reply.times.starttime,
698
kdc_infinity, state->request->till, state->client,
699
state->server, &state->enc_tkt_reply.times.endtime);
700
701
kdc_get_ticket_renewtime(realm, state->request, NULL, state->client,
702
state->server, &state->enc_tkt_reply.flags,
703
&state->enc_tkt_reply.times);
704
705
/*
706
* starttime is optional, and treated as authtime if not present.
707
* so we can nuke it if it matches
708
*/
709
if (state->enc_tkt_reply.times.starttime ==
710
state->enc_tkt_reply.times.authtime)
711
state->enc_tkt_reply.times.starttime = 0;
712
713
state->enc_tkt_reply.caddrs = state->request->addresses;
714
state->enc_tkt_reply.authorization_data = 0;
715
716
/* If anonymous requests are being used, adjust the realm of the client
717
* principal. */
718
if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) {
719
if (!krb5_principal_compare_any_realm(context, state->request->client,
720
krb5_anonymous_principal())) {
721
errcode = KRB5KDC_ERR_BADOPTION;
722
/* Anonymous requested but anonymous principal not used.*/
723
state->status = "VALIDATE_ANONYMOUS_PRINCIPAL";
724
goto errout;
725
}
726
krb5_free_principal(context, state->request->client);
727
state->request->client = NULL;
728
errcode = krb5_copy_principal(context, krb5_anonymous_principal(),
729
&state->request->client);
730
if (errcode)
731
goto errout;
732
state->enc_tkt_reply.client = state->request->client;
733
setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH);
734
}
735
736
errcode = select_client_key(context, state->client, state->request->ktype,
737
state->request->nktypes,
738
&state->client_keyblock, &state->client_key);
739
if (errcode) {
740
state->status = "DECRYPT_CLIENT_KEY";
741
goto errout;
742
}
743
if (state->client_key != NULL)
744
state->rock.client_key = state->client_key;
745
state->rock.client_keyblock = &state->client_keyblock;
746
747
errcode = kdc_fast_read_cookie(context, state->rstate, state->request,
748
state->local_tgt, &state->local_tgt_key);
749
if (errcode) {
750
state->status = "READ_COOKIE";
751
goto errout;
752
}
753
754
/*
755
* Check the preauthentication if it is there.
756
*/
757
if (state->request->padata) {
758
check_padata(context, &state->rock, state->req_pkt, state->request,
759
&state->enc_tkt_reply, &state->pa_context, &state->e_data,
760
&state->typed_e_data, finish_preauth, state);
761
} else
762
finish_preauth(state, 0);
763
return;
764
765
errout:
766
finish_process_as_req(state, errcode);
767
}
768
769
static krb5_error_code
770
prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request,
771
krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
772
krb5_error_code code, krb5_pa_data **e_data_in,
773
krb5_boolean typed_e_data, krb5_principal canon_client,
774
krb5_data **response, const char *status)
775
{
776
krb5_context context = rstate->realm_data->realm_context;
777
krb5_error errpkt;
778
krb5_error_code retval;
779
krb5_data *scratch = NULL, *e_data_asn1 = NULL, *fast_edata = NULL;
780
krb5_pa_data **e_data = NULL, *cookie = NULL;
781
size_t count;
782
783
errpkt.magic = KV5M_ERROR;
784
785
if (e_data_in != NULL) {
786
/* Add a PA-FX-COOKIE to e_data_in. e_data is a shallow copy
787
* containing aliases. */
788
for (count = 0; e_data_in[count] != NULL; count++);
789
e_data = calloc(count + 2, sizeof(*e_data));
790
if (e_data == NULL)
791
return ENOMEM;
792
memcpy(e_data, e_data_in, count * sizeof(*e_data));
793
retval = kdc_fast_make_cookie(context, rstate, local_tgt,
794
local_tgt_key, request->client, &cookie);
795
e_data[count] = cookie;
796
}
797
798
errpkt.ctime = 0;
799
errpkt.cusec = 0;
800
801
retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec);
802
if (retval)
803
goto cleanup;
804
errpkt.error = errcode_to_protocol(code);
805
errpkt.server = request->server;
806
errpkt.client = (code == KRB5KDC_ERR_WRONG_REALM) ? canon_client :
807
request->client;
808
errpkt.text = string2data((char *)status);
809
810
if (e_data != NULL) {
811
if (typed_e_data)
812
retval = encode_krb5_typed_data(e_data, &e_data_asn1);
813
else
814
retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
815
if (retval)
816
goto cleanup;
817
errpkt.e_data = *e_data_asn1;
818
} else
819
errpkt.e_data = empty_data();
820
821
retval = kdc_fast_handle_error(context, rstate, request, e_data, &errpkt,
822
&fast_edata);
823
if (retval)
824
goto cleanup;
825
if (fast_edata != NULL)
826
errpkt.e_data = *fast_edata;
827
828
scratch = k5alloc(sizeof(*scratch), &retval);
829
if (scratch == NULL)
830
goto cleanup;
831
if (kdc_fast_hide_client(rstate) && errpkt.client != NULL)
832
errpkt.client = (krb5_principal)krb5_anonymous_principal();
833
retval = krb5_mk_error(context, &errpkt, scratch);
834
if (retval)
835
goto cleanup;
836
837
*response = scratch;
838
scratch = NULL;
839
840
cleanup:
841
krb5_free_data(context, fast_edata);
842
krb5_free_data(context, e_data_asn1);
843
free(scratch);
844
free(e_data);
845
if (cookie != NULL)
846
free(cookie->contents);
847
free(cookie);
848
return retval;
849
}
850
851