Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / kdc / do_as_req.c
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 by the Massachusetts Institute of Technology.
6  * 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 "policy.h"
72 #include "adm.h"
73 #include "adm_proto.h"
74 #include "extern.h"
75
76 #if APPLE_PKINIT
77 #define     AS_REQ_DEBUG    0
78 #if         AS_REQ_DEBUG
79 #define     asReqDebug(args...)       printf(args)
80 #else
81 #define     asReqDebug(args...)
82 #endif
83 #endif /* APPLE_PKINIT */
84
85 static krb5_error_code
86 prepare_error_as(struct kdc_request_state *, krb5_kdc_req *,
87                  int, krb5_pa_data **, krb5_boolean, krb5_principal,
88                  krb5_data **, const char *);
89
90 /* Determine the key-expiration value according to RFC 4120 section 5.4.2. */
91 static krb5_timestamp
92 get_key_exp(krb5_db_entry *entry)
93 {
94     if (entry->expiration == 0)
95         return entry->pw_expiration;
96     if (entry->pw_expiration == 0)
97         return entry->expiration;
98     return min(entry->expiration, entry->pw_expiration);
99 }
100
101 struct as_req_state {
102     loop_respond_fn respond;
103     void *arg;
104
105     krb5_principal_data client_princ;
106     krb5_enc_tkt_part enc_tkt_reply;
107     krb5_enc_kdc_rep_part reply_encpart;
108     krb5_ticket ticket_reply;
109     krb5_keyblock server_keyblock;
110     krb5_keyblock client_keyblock;
111     krb5_db_entry *client;
112     krb5_db_entry *server;
113     krb5_kdc_req *request;
114     struct krb5_kdcpreauth_rock_st rock;
115     const char *status;
116     krb5_pa_data **e_data;
117     krb5_boolean typed_e_data;
118     krb5_kdc_rep reply;
119     krb5_timestamp kdc_time;
120     krb5_timestamp authtime;
121     krb5_keyblock session_key;
122     unsigned int c_flags;
123     krb5_data *req_pkt;
124     krb5_data *inner_body;
125     struct kdc_request_state *rstate;
126     char *sname, *cname;
127     void *pa_context;
128     const krb5_fulladdr *from;
129
130     krb5_error_code preauth_err;
131 };
132
133 static void
134 finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
135 {
136     krb5_key_data *server_key;
137     krb5_key_data *client_key;
138     krb5_keyblock *as_encrypting_key = NULL;
139     krb5_data *response = NULL;
140     const char *emsg = 0;
141     int did_log = 0;
142     register int i;
143     krb5_enctype useenctype;
144     loop_respond_fn oldrespond;
145     void *oldarg;
146
147     assert(state);
148     oldrespond = state->respond;
149     oldarg = state->arg;
150
151     if (errcode)
152         goto egress;
153
154     if ((errcode = validate_forwardable(state->request, *state->client,
155                                         *state->server, state->kdc_time,
156                                         &state->status))) {
157         errcode += ERROR_TABLE_BASE_krb5;
158         goto egress;
159     }
160
161     state->ticket_reply.enc_part2 = &state->enc_tkt_reply;
162
163     /*
164      * Find the server key
165      */
166     if ((errcode = krb5_dbe_find_enctype(kdc_context, state->server,
167                                          -1, /* ignore keytype   */
168                                          -1, /* Ignore salttype  */
169                                          0,  /* Get highest kvno */
170                                          &server_key))) {
171         state->status = "FINDING_SERVER_KEY";
172         goto egress;
173     }
174
175     /*
176      * Convert server->key into a real key
177      * (it may be encrypted in the database)
178      *
179      *  server_keyblock is later used to generate auth data signatures
180      */
181     if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
182                                              server_key,
183                                              &state->server_keyblock,
184                                              NULL))) {
185         state->status = "DECRYPT_SERVER_KEY";
186         goto egress;
187     }
188
189     /*
190      * Find the appropriate client key.  We search in the order specified
191      * by request keytype list.
192      */
193     client_key = NULL;
194     for (i = 0; i < state->request->nktypes; i++) {
195         useenctype = state->request->ktype[i];
196         if (!krb5_c_valid_enctype(useenctype))
197             continue;
198
199         if (!krb5_dbe_find_enctype(kdc_context, state->client,
200                                    useenctype, -1, 0, &client_key))
201             break;
202     }
203     if (!(client_key)) {
204         /* Cannot find an appropriate key */
205         state->status = "CANT_FIND_CLIENT_KEY";
206         errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
207         goto egress;
208     }
209     state->rock.client_key = client_key;
210
211     /* convert client.key_data into a real key */
212     if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
213                                              client_key,
214                                              &state->client_keyblock,
215                                              NULL))) {
216         state->status = "DECRYPT_CLIENT_KEY";
217         goto egress;
218     }
219     state->client_keyblock.enctype = useenctype;
220
221     /* Start assembling the response */
222     state->reply.msg_type = KRB5_AS_REP;
223     state->reply.client = state->enc_tkt_reply.client; /* post canonization */
224     state->reply.ticket = &state->ticket_reply;
225     state->reply_encpart.session = &state->session_key;
226     if ((errcode = fetch_last_req_info(state->client,
227                                        &state->reply_encpart.last_req))) {
228         state->status = "FETCH_LAST_REQ";
229         goto egress;
230     }
231     state->reply_encpart.nonce = state->request->nonce;
232     state->reply_encpart.key_exp = get_key_exp(state->client);
233     state->reply_encpart.flags = state->enc_tkt_reply.flags;
234     state->reply_encpart.server = state->ticket_reply.server;
235
236     /* copy the time fields EXCEPT for authtime; it's location
237      *  is used for ktime
238      */
239     state->reply_encpart.times = state->enc_tkt_reply.times;
240     state->reply_encpart.times.authtime = state->authtime = state->kdc_time;
241
242     state->reply_encpart.caddrs = state->enc_tkt_reply.caddrs;
243     state->reply_encpart.enc_padata = NULL;
244
245     /* Fetch the padata info to be returned (do this before
246      *  authdata to handle possible replacement of reply key
247      */
248     errcode = return_padata(kdc_context, &state->rock, state->req_pkt,
249                             state->request, &state->reply,
250                             &state->client_keyblock, &state->pa_context);
251     if (errcode) {
252         state->status = "KDC_RETURN_PADATA";
253         goto egress;
254     }
255
256 #if APPLE_PKINIT
257     asReqDebug("process_as_req reply realm %s name %s\n",
258                reply.client->realm.data, reply.client->data->data);
259 #endif /* APPLE_PKINIT */
260
261
262
263     errcode = handle_authdata(kdc_context,
264                               state->c_flags,
265                               state->client,
266                               state->server,
267                               state->server,
268                               &state->client_keyblock,
269                               &state->server_keyblock,
270                               &state->server_keyblock,
271                               state->req_pkt,
272                               state->request,
273                               NULL, /* for_user_princ */
274                               NULL, /* enc_tkt_request */
275                               &state->enc_tkt_reply);
276     if (errcode) {
277         krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"),
278                          errcode);
279         state->status = "HANDLE_AUTHDATA";
280         goto egress;
281     }
282
283     errcode = krb5_encrypt_tkt_part(kdc_context, &state->server_keyblock,
284                                     &state->ticket_reply);
285     if (errcode) {
286         state->status = "ENCRYPTING_TICKET";
287         goto egress;
288     }
289     state->ticket_reply.enc_part.kvno = server_key->key_data_kvno;
290     errcode = kdc_fast_response_handle_padata(state->rstate,
291                                               state->request,
292                                               &state->reply,
293                                               state->client_keyblock.enctype);
294     if (errcode) {
295         state->status = "fast response handling";
296         goto egress;
297     }
298
299     /* now encode/encrypt the response */
300
301     state->reply.enc_part.enctype = state->client_keyblock.enctype;
302
303     errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock,
304                                         &as_encrypting_key);
305     if (errcode) {
306         state->status = "generating reply key";
307         goto egress;
308     }
309     errcode = return_enc_padata(kdc_context, state->req_pkt, state->request,
310                                 as_encrypting_key, state->server,
311                                 &state->reply_encpart, FALSE);
312     if (errcode) {
313         state->status = "KDC_RETURN_ENC_PADATA";
314         goto egress;
315     }
316
317     errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP,
318                                   &state->reply_encpart, 0,
319                                   as_encrypting_key,
320                                   &state->reply, &response);
321     state->reply.enc_part.kvno = client_key->key_data_kvno;
322     if (errcode) {
323         state->status = "ENCODE_KDC_REP";
324         goto egress;
325     }
326
327     /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
328        can use them in raw form if needed.  But, we don't... */
329     memset(state->reply.enc_part.ciphertext.data, 0,
330            state->reply.enc_part.ciphertext.length);
331     free(state->reply.enc_part.ciphertext.data);
332
333     log_as_req(state->from, state->request, &state->reply,
334                state->client, state->cname, state->server,
335                state->sname, state->authtime, 0, 0, 0);
336     did_log = 1;
337
338 egress:
339     if (errcode != 0)
340         assert (state->status != 0);
341     free_padata_context(kdc_context, state->pa_context);
342     if (as_encrypting_key)
343         krb5_free_keyblock(kdc_context, as_encrypting_key);
344     if (errcode)
345         emsg = krb5_get_error_message(kdc_context, errcode);
346
347     if (state->status) {
348         log_as_req(state->from, state->request, &state->reply, state->client,
349                    state->cname, state->server, state->sname, state->authtime,
350                    state->status, errcode, emsg);
351         did_log = 1;
352     }
353     if (errcode) {
354         if (state->status == 0) {
355             state->status = emsg;
356         }
357         if (errcode != KRB5KDC_ERR_DISCARD) {
358             errcode -= ERROR_TABLE_BASE_krb5;
359             if (errcode < 0 || errcode > 128)
360                 errcode = KRB_ERR_GENERIC;
361
362             errcode = prepare_error_as(state->rstate, state->request,
363                                        errcode, state->e_data,
364                                        state->typed_e_data,
365                                        ((state->client != NULL) ?
366                                         state->client->princ : NULL),
367                                        &response, state->status);
368             state->status = 0;
369         }
370     }
371
372     if (emsg)
373         krb5_free_error_message(kdc_context, emsg);
374     if (state->enc_tkt_reply.authorization_data != NULL)
375         krb5_free_authdata(kdc_context,
376                            state->enc_tkt_reply.authorization_data);
377     if (state->server_keyblock.contents != NULL)
378         krb5_free_keyblock_contents(kdc_context, &state->server_keyblock);
379     if (state->client_keyblock.contents != NULL)
380         krb5_free_keyblock_contents(kdc_context, &state->client_keyblock);
381     if (state->reply.padata != NULL)
382         krb5_free_pa_data(kdc_context, state->reply.padata);
383     if (state->reply_encpart.enc_padata)
384         krb5_free_pa_data(kdc_context, state->reply_encpart.enc_padata);
385
386     if (state->cname != NULL)
387         free(state->cname);
388     if (state->sname != NULL)
389         free(state->sname);
390     krb5_db_free_principal(kdc_context, state->client);
391     krb5_db_free_principal(kdc_context, state->server);
392     if (state->session_key.contents != NULL)
393         krb5_free_keyblock_contents(kdc_context, &state->session_key);
394     if (state->ticket_reply.enc_part.ciphertext.data != NULL) {
395         memset(state->ticket_reply.enc_part.ciphertext.data , 0,
396                state->ticket_reply.enc_part.ciphertext.length);
397         free(state->ticket_reply.enc_part.ciphertext.data);
398     }
399
400     krb5_free_pa_data(kdc_context, state->e_data);
401     krb5_free_data(kdc_context, state->inner_body);
402     kdc_free_rstate(state->rstate);
403     krb5_free_kdc_req(kdc_context, state->request);
404     assert(did_log != 0);
405
406     free(state);
407     (*oldrespond)(oldarg, errcode, response);
408 }
409
410 static void
411 finish_missing_required_preauth(void *arg)
412 {
413     struct as_req_state *state = (struct as_req_state *)arg;
414
415     finish_process_as_req(state, state->preauth_err);
416 }
417
418 static void
419 finish_preauth(void *arg, krb5_error_code code)
420 {
421     struct as_req_state *state = arg;
422     krb5_error_code real_code = code;
423
424     if (code) {
425         if (vague_errors)
426             code = KRB5KRB_ERR_GENERIC;
427         state->status = "PREAUTH_FAILED";
428         if (real_code == KRB5KDC_ERR_PREAUTH_FAILED) {
429             state->preauth_err = code;
430             get_preauth_hint_list(state->request, &state->rock, &state->e_data,
431                                   finish_missing_required_preauth, state);
432             return;
433         }
434     } else {
435         /*
436          * Final check before handing out ticket: If the client requires
437          * preauthentication, verify that the proper kind of
438          * preauthentication was carried out.
439          */
440         state->status = missing_required_preauth(state->client, state->server,
441                                                  &state->enc_tkt_reply);
442         if (state->status) {
443             state->preauth_err = KRB5KDC_ERR_PREAUTH_REQUIRED;
444             get_preauth_hint_list(state->request, &state->rock, &state->e_data,
445                                   finish_missing_required_preauth, state);
446             return;
447         }
448     }
449
450     finish_process_as_req(state, code);
451 }
452
453 /*ARGSUSED*/
454 void
455 process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
456                const krb5_fulladdr *from, verto_ctx *vctx,
457                loop_respond_fn respond, void *arg)
458 {
459     krb5_error_code errcode;
460     krb5_timestamp rtime;
461     unsigned int s_flags = 0;
462     krb5_data encoded_req_body;
463     krb5_enctype useenctype;
464     struct as_req_state *state;
465
466     state = malloc(sizeof(*state));
467     if (!state) {
468         (*respond)(arg, ENOMEM, NULL);
469         return;
470     }
471     state->session_key.contents = 0;
472     state->enc_tkt_reply.authorization_data = NULL;
473     state->reply.padata = 0;
474     memset(&state->reply, 0, sizeof(state->reply));
475     state->respond = respond;
476     state->arg = arg;
477     state->ticket_reply.enc_part.ciphertext.data = 0;
478     state->server_keyblock.contents = NULL;
479     state->client_keyblock.contents = NULL;
480     state->reply_encpart.enc_padata = 0;
481     state->client = NULL;
482     state->server = NULL;
483     state->request = request;
484     state->e_data = NULL;
485     state->typed_e_data = FALSE;
486     state->authtime = 0;
487     state->c_flags = 0;
488     state->req_pkt = req_pkt;
489     state->rstate = NULL;
490     state->sname = 0;
491     state->cname = 0;
492     state->pa_context = NULL;
493     state->from = from;
494     memset(&state->rock, 0, sizeof(state->rock));
495
496 #if APPLE_PKINIT
497     asReqDebug("process_as_req top realm %s name %s\n",
498                request->client->realm.data, request->client->data->data);
499 #endif /* APPLE_PKINIT */
500
501     if (state->request->msg_type != KRB5_AS_REQ) {
502         state->status = "msg_type mismatch";
503         errcode = KRB5_BADMSGTYPE;
504         goto errout;
505     }
506     errcode = kdc_make_rstate(&state->rstate);
507     if (errcode != 0) {
508         state->status = "constructing state";
509         goto errout;
510     }
511     if (fetch_asn1_field((unsigned char *) req_pkt->data,
512                          1, 4, &encoded_req_body) != 0) {
513         errcode = ASN1_BAD_ID;
514         state->status = "Finding req_body";
515         goto errout;
516     }
517     errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL,
518                             state->rstate, &state->inner_body);
519     if (errcode) {
520         state->status = "error decoding FAST";
521         goto errout;
522     }
523     if (state->inner_body == NULL) {
524         /* Not a FAST request; copy the encoded request body. */
525         errcode = krb5_copy_data(kdc_context, &encoded_req_body,
526                                  &state->inner_body);
527         if (errcode) {
528             state->status = "storing req body";
529             goto errout;
530         }
531     }
532     state->rock.request = state->request;
533     state->rock.inner_body = state->inner_body;
534     state->rock.rstate = state->rstate;
535     state->rock.vctx = vctx;
536     if (!state->request->client) {
537         state->status = "NULL_CLIENT";
538         errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
539         goto errout;
540     }
541     if ((errcode = krb5_unparse_name(kdc_context,
542                                      state->request->client,
543                                      &state->cname))) {
544         state->status = "UNPARSING_CLIENT";
545         goto errout;
546     }
547     limit_string(state->cname);
548     if (!state->request->server) {
549         state->status = "NULL_SERVER";
550         errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
551         goto errout;
552     }
553     if ((errcode = krb5_unparse_name(kdc_context,
554                                      state->request->server,
555                                      &state->sname))) {
556         state->status = "UNPARSING_SERVER";
557         goto errout;
558     }
559     limit_string(state->sname);
560
561     /*
562      * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint
563      * to the backend to return naming information in lieu
564      * of cross realm TGS entries.
565      */
566     setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY);
567     /*
568      * Note that according to the referrals draft we should
569      * always canonicalize enterprise principal names.
570      */
571     if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) ||
572         state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
573         setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE);
574         setflag(state->c_flags, KRB5_KDB_FLAG_ALIAS_OK);
575     }
576     if (include_pac_p(kdc_context, state->request)) {
577         setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
578     }
579     errcode = krb5_db_get_principal(kdc_context, state->request->client,
580                                     state->c_flags, &state->client);
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     /*
595      * If the backend returned a principal that is not in the local
596      * realm, then we need to refer the client to that realm.
597      */
598     if (!is_local_principal(state->client->princ)) {
599         /* Entry is a referral to another realm */
600         state->status = "REFERRAL";
601         errcode = KRB5KDC_ERR_WRONG_REALM;
602         goto errout;
603     }
604
605 #if 0
606     /*
607      * Turn off canonicalization if client is marked DES only
608      * (unless enterprise principal name was requested)
609      */
610     if (isflagset(client->attributes, KRB5_KDB_NON_MS_PRINCIPAL) &&
611         krb5_princ_type(kdc_context,
612                         request->client) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
613         clear(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
614     }
615 #endif
616
617     s_flags = 0;
618     setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK);
619     if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) {
620         setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
621     }
622     errcode = krb5_db_get_principal(kdc_context, state->request->server,
623                                     s_flags, &state->server);
624     if (errcode == KRB5_KDB_NOENTRY) {
625         state->status = "SERVER_NOT_FOUND";
626         errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
627         goto errout;
628     } else if (errcode) {
629         state->status = "LOOKING_UP_SERVER";
630         goto errout;
631     }
632
633     if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) {
634         state->status = "TIMEOFDAY";
635         goto errout;
636     }
637     state->authtime = state->kdc_time; /* for audit_as_request() */
638
639     if ((errcode = validate_as_request(state->request, *state->client,
640                                        *state->server, state->kdc_time,
641                                        &state->status, &state->e_data))) {
642         if (!state->status)
643             state->status = "UNKNOWN_REASON";
644         errcode += ERROR_TABLE_BASE_krb5;
645         goto errout;
646     }
647
648     /*
649      * Select the keytype for the ticket session key.
650      */
651     if ((useenctype = select_session_keytype(kdc_context, state->server,
652                                              state->request->nktypes,
653                                              state->request->ktype)) == 0) {
654         /* unsupported ktype */
655         state->status = "BAD_ENCRYPTION_TYPE";
656         errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
657         goto errout;
658     }
659
660     if ((errcode = krb5_c_make_random_key(kdc_context, useenctype,
661                                           &state->session_key))) {
662         state->status = "RANDOM_KEY_FAILED";
663         goto errout;
664     }
665
666     /*
667      * Canonicalization is only effective if we are issuing a TGT
668      * (the intention is to allow support for Windows "short" realm
669      * aliases, nothing more).
670      */
671     if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) &&
672         krb5_is_tgs_principal(state->request->server) &&
673         krb5_is_tgs_principal(state->server->princ)) {
674         state->ticket_reply.server = state->server->princ;
675     } else {
676         state->ticket_reply.server = state->request->server;
677     }
678
679     state->enc_tkt_reply.flags = 0;
680     state->enc_tkt_reply.times.authtime = state->authtime;
681
682     setflag(state->enc_tkt_reply.flags, TKT_FLG_INITIAL);
683     setflag(state->enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP);
684
685     /*
686      * It should be noted that local policy may affect the
687      * processing of any of these flags.  For example, some
688      * realms may refuse to issue renewable tickets
689      */
690
691     if (isflagset(state->request->kdc_options, KDC_OPT_FORWARDABLE))
692         setflag(state->enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
693
694     if (isflagset(state->request->kdc_options, KDC_OPT_PROXIABLE))
695         setflag(state->enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
696
697     if (isflagset(state->request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
698         setflag(state->enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
699
700     state->enc_tkt_reply.session = &state->session_key;
701     if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
702         state->client_princ = *(state->client->princ);
703     } else {
704         state->client_princ = *(state->request->client);
705         /* The realm is always canonicalized */
706         state->client_princ.realm = state->client->princ->realm;
707     }
708     state->enc_tkt_reply.client = &state->client_princ;
709     state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
710     state->enc_tkt_reply.transited.tr_contents = empty_string;
711
712     if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED)) {
713         setflag(state->enc_tkt_reply.flags, TKT_FLG_POSTDATED);
714         setflag(state->enc_tkt_reply.flags, TKT_FLG_INVALID);
715         state->enc_tkt_reply.times.starttime = state->request->from;
716     } else
717         state->enc_tkt_reply.times.starttime = state->kdc_time;
718
719     kdc_get_ticket_endtime(kdc_context, state->enc_tkt_reply.times.starttime,
720                            kdc_infinity, state->request->till, state->client,
721                            state->server, &state->enc_tkt_reply.times.endtime);
722
723     if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
724         !isflagset(state->client->attributes, KRB5_KDB_DISALLOW_RENEWABLE) &&
725         (state->enc_tkt_reply.times.endtime < state->request->till)) {
726
727         /* we set the RENEWABLE option for later processing */
728
729         setflag(state->request->kdc_options, KDC_OPT_RENEWABLE);
730         state->request->rtime = state->request->till;
731     }
732     rtime = (state->request->rtime == 0) ? kdc_infinity :
733         state->request->rtime;
734
735     if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE)) {
736         /*
737          * XXX Should we squelch the output renew_till to be no
738          * earlier than the endtime of the ticket?
739          */
740         setflag(state->enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
741         state->enc_tkt_reply.times.renew_till =
742             min(rtime, state->enc_tkt_reply.times.starttime +
743                 min(state->client->max_renewable_life,
744                     min(state->server->max_renewable_life,
745                         max_renewable_life_for_realm)));
746     } else
747         state->enc_tkt_reply.times.renew_till = 0; /* XXX */
748
749     /*
750      * starttime is optional, and treated as authtime if not present.
751      * so we can nuke it if it matches
752      */
753     if (state->enc_tkt_reply.times.starttime ==
754         state->enc_tkt_reply.times.authtime)
755         state->enc_tkt_reply.times.starttime = 0;
756
757     state->enc_tkt_reply.caddrs = state->request->addresses;
758     state->enc_tkt_reply.authorization_data = 0;
759
760     /* If anonymous requests are being used, adjust the realm of the client
761      * principal. */
762     if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) {
763         if (!krb5_principal_compare_any_realm(kdc_context,
764                                               state->request->client,
765                                               krb5_anonymous_principal())) {
766             errcode = KRB5KDC_ERR_BADOPTION;
767             state->status = "Anonymous requested but anonymous "
768                 "principal not used.";
769             goto errout;
770         }
771         setflag(state->enc_tkt_reply.flags, TKT_FLG_ANONYMOUS);
772         krb5_free_principal(kdc_context, state->request->client);
773         errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(),
774                                       &state->request->client);
775         if (errcode) {
776             state->status = "Copying anonymous principal";
777             goto errout;
778         }
779         state->enc_tkt_reply.client = state->request->client;
780         setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH);
781     }
782
783     /*
784      * Check the preauthentication if it is there.
785      */
786     if (state->request->padata) {
787         check_padata(kdc_context, &state->rock, state->req_pkt,
788                      state->request, &state->enc_tkt_reply, &state->pa_context,
789                      &state->e_data, &state->typed_e_data, finish_preauth,
790                      state);
791     } else
792         finish_preauth(state, 0);
793     return;
794
795 errout:
796     finish_process_as_req(state, errcode);
797 }
798
799 static krb5_error_code
800 prepare_error_as (struct kdc_request_state *rstate, krb5_kdc_req *request,
801                   int error, krb5_pa_data **e_data, krb5_boolean typed_e_data,
802                   krb5_principal canon_client, krb5_data **response,
803                   const char *status)
804 {
805     krb5_error errpkt;
806     krb5_error_code retval;
807     krb5_data *scratch = NULL, *e_data_asn1 = NULL, *fast_edata = NULL;
808
809     errpkt.ctime = request->nonce;
810     errpkt.cusec = 0;
811
812     retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
813     if (retval)
814         return retval;
815     errpkt.error = error;
816     errpkt.server = request->server;
817     errpkt.client = (error == KRB5KDC_ERR_WRONG_REALM) ? canon_client :
818         request->client;
819     errpkt.text = string2data((char *)status);
820
821     if (e_data != NULL) {
822         if (typed_e_data) {
823             retval = encode_krb5_typed_data((const krb5_typed_data **)e_data,
824                                             &e_data_asn1);
825         } else
826             retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
827         if (retval)
828             goto cleanup;
829         errpkt.e_data = *e_data_asn1;
830     } else
831         errpkt.e_data = empty_data();
832
833     retval = kdc_fast_handle_error(kdc_context, rstate, request, e_data,
834                                    &errpkt, &fast_edata);
835     if (retval)
836         goto cleanup;
837     if (fast_edata != NULL)
838         errpkt.e_data = *fast_edata;
839
840     scratch = k5alloc(sizeof(*scratch), &retval);
841     if (scratch == NULL)
842         goto cleanup;
843     retval = krb5_mk_error(kdc_context, &errpkt, scratch);
844     if (retval)
845         goto cleanup;
846
847     *response = scratch;
848     scratch = NULL;
849
850 cleanup:
851     krb5_free_data(kdc_context, fast_edata);
852     krb5_free_data(kdc_context, e_data_asn1);
853     free(scratch);
854     return retval;
855 }