1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* kdc/kdc_util.c - Utility functions for the KDC implementation */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright 1990,1991,2007,2008,2009 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All Rights Reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Export of this software from the United States of America may
8*7f2fe78bSCy Schubert * require a specific license from the United States Government.
9*7f2fe78bSCy Schubert * It is the responsibility of any person or organization contemplating
10*7f2fe78bSCy Schubert * export to obtain such a license before exporting.
11*7f2fe78bSCy Schubert *
12*7f2fe78bSCy Schubert * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13*7f2fe78bSCy Schubert * distribute this software and its documentation for any purpose and
14*7f2fe78bSCy Schubert * without fee is hereby granted, provided that the above copyright
15*7f2fe78bSCy Schubert * notice appear in all copies and that both that copyright notice and
16*7f2fe78bSCy Schubert * this permission notice appear in supporting documentation, and that
17*7f2fe78bSCy Schubert * the name of M.I.T. not be used in advertising or publicity pertaining
18*7f2fe78bSCy Schubert * to distribution of the software without specific, written prior
19*7f2fe78bSCy Schubert * permission. Furthermore if you modify this software you must label
20*7f2fe78bSCy Schubert * your software as modified software and not distribute it in such a
21*7f2fe78bSCy Schubert * fashion that it might be confused with the original M.I.T. software.
22*7f2fe78bSCy Schubert * M.I.T. makes no representations about the suitability of
23*7f2fe78bSCy Schubert * this software for any purpose. It is provided "as is" without express
24*7f2fe78bSCy Schubert * or implied warranty.
25*7f2fe78bSCy Schubert */
26*7f2fe78bSCy Schubert /*
27*7f2fe78bSCy Schubert * Copyright (c) 2006-2008, Novell, Inc.
28*7f2fe78bSCy Schubert * All rights reserved.
29*7f2fe78bSCy Schubert *
30*7f2fe78bSCy Schubert * Redistribution and use in source and binary forms, with or without
31*7f2fe78bSCy Schubert * modification, are permitted provided that the following conditions are met:
32*7f2fe78bSCy Schubert *
33*7f2fe78bSCy Schubert * * Redistributions of source code must retain the above copyright notice,
34*7f2fe78bSCy Schubert * this list of conditions and the following disclaimer.
35*7f2fe78bSCy Schubert * * Redistributions in binary form must reproduce the above copyright
36*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer in the
37*7f2fe78bSCy Schubert * documentation and/or other materials provided with the distribution.
38*7f2fe78bSCy Schubert * * The copyright holder's name is not used to endorse or promote products
39*7f2fe78bSCy Schubert * derived from this software without specific prior written permission.
40*7f2fe78bSCy Schubert *
41*7f2fe78bSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42*7f2fe78bSCy Schubert * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43*7f2fe78bSCy Schubert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44*7f2fe78bSCy Schubert * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45*7f2fe78bSCy Schubert * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46*7f2fe78bSCy Schubert * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47*7f2fe78bSCy Schubert * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48*7f2fe78bSCy Schubert * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49*7f2fe78bSCy Schubert * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50*7f2fe78bSCy Schubert * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51*7f2fe78bSCy Schubert * POSSIBILITY OF SUCH DAMAGE.
52*7f2fe78bSCy Schubert */
53*7f2fe78bSCy Schubert
54*7f2fe78bSCy Schubert #include "k5-int.h"
55*7f2fe78bSCy Schubert #include "kdc_util.h"
56*7f2fe78bSCy Schubert #include "extern.h"
57*7f2fe78bSCy Schubert #include <stdio.h>
58*7f2fe78bSCy Schubert #include <ctype.h>
59*7f2fe78bSCy Schubert #include <syslog.h>
60*7f2fe78bSCy Schubert #include <kadm5/admin.h>
61*7f2fe78bSCy Schubert #include "adm_proto.h"
62*7f2fe78bSCy Schubert #include "net-server.h"
63*7f2fe78bSCy Schubert #include <limits.h>
64*7f2fe78bSCy Schubert
65*7f2fe78bSCy Schubert #ifdef KRBCONF_VAGUE_ERRORS
66*7f2fe78bSCy Schubert const int vague_errors = 1;
67*7f2fe78bSCy Schubert #else
68*7f2fe78bSCy Schubert const int vague_errors = 0;
69*7f2fe78bSCy Schubert #endif
70*7f2fe78bSCy Schubert
71*7f2fe78bSCy Schubert static krb5_error_code kdc_rd_ap_req(kdc_realm_t *realm, krb5_ap_req *apreq,
72*7f2fe78bSCy Schubert krb5_auth_context auth_context,
73*7f2fe78bSCy Schubert krb5_db_entry **server,
74*7f2fe78bSCy Schubert krb5_keyblock **tgskey);
75*7f2fe78bSCy Schubert static krb5_error_code find_server_key(krb5_context,
76*7f2fe78bSCy Schubert krb5_db_entry *, krb5_enctype,
77*7f2fe78bSCy Schubert krb5_kvno, krb5_keyblock **,
78*7f2fe78bSCy Schubert krb5_kvno *);
79*7f2fe78bSCy Schubert
80*7f2fe78bSCy Schubert /*
81*7f2fe78bSCy Schubert * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
82*7f2fe78bSCy Schubert * service.
83*7f2fe78bSCy Schubert */
84*7f2fe78bSCy Schubert krb5_boolean
krb5_is_tgs_principal(krb5_const_principal principal)85*7f2fe78bSCy Schubert krb5_is_tgs_principal(krb5_const_principal principal)
86*7f2fe78bSCy Schubert {
87*7f2fe78bSCy Schubert if (krb5_princ_size(kdc_context, principal) != 2)
88*7f2fe78bSCy Schubert return FALSE;
89*7f2fe78bSCy Schubert if (data_eq_string(*krb5_princ_component(kdc_context, principal, 0),
90*7f2fe78bSCy Schubert KRB5_TGS_NAME))
91*7f2fe78bSCy Schubert return TRUE;
92*7f2fe78bSCy Schubert else
93*7f2fe78bSCy Schubert return FALSE;
94*7f2fe78bSCy Schubert }
95*7f2fe78bSCy Schubert
96*7f2fe78bSCy Schubert /* Returns TRUE if principal is the name of a cross-realm TGS. */
97*7f2fe78bSCy Schubert krb5_boolean
is_cross_tgs_principal(krb5_const_principal principal)98*7f2fe78bSCy Schubert is_cross_tgs_principal(krb5_const_principal principal)
99*7f2fe78bSCy Schubert {
100*7f2fe78bSCy Schubert return krb5_is_tgs_principal(principal) &&
101*7f2fe78bSCy Schubert !data_eq(principal->data[1], principal->realm);
102*7f2fe78bSCy Schubert }
103*7f2fe78bSCy Schubert
104*7f2fe78bSCy Schubert /* Return true if princ is the name of a local TGS for any realm. */
105*7f2fe78bSCy Schubert krb5_boolean
is_local_tgs_principal(krb5_const_principal principal)106*7f2fe78bSCy Schubert is_local_tgs_principal(krb5_const_principal principal)
107*7f2fe78bSCy Schubert {
108*7f2fe78bSCy Schubert return krb5_is_tgs_principal(principal) &&
109*7f2fe78bSCy Schubert data_eq(principal->data[1], principal->realm);
110*7f2fe78bSCy Schubert }
111*7f2fe78bSCy Schubert
112*7f2fe78bSCy Schubert /*
113*7f2fe78bSCy Schubert * given authentication data (provides seed for checksum), verify checksum
114*7f2fe78bSCy Schubert * for source data.
115*7f2fe78bSCy Schubert */
116*7f2fe78bSCy Schubert static krb5_error_code
comp_cksum(krb5_context kcontext,krb5_data * source,krb5_ticket * ticket,krb5_checksum * his_cksum)117*7f2fe78bSCy Schubert comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
118*7f2fe78bSCy Schubert krb5_checksum *his_cksum)
119*7f2fe78bSCy Schubert {
120*7f2fe78bSCy Schubert krb5_error_code retval;
121*7f2fe78bSCy Schubert krb5_boolean valid;
122*7f2fe78bSCy Schubert
123*7f2fe78bSCy Schubert if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
124*7f2fe78bSCy Schubert return KRB5KDC_ERR_SUMTYPE_NOSUPP;
125*7f2fe78bSCy Schubert
126*7f2fe78bSCy Schubert /* must be collision proof */
127*7f2fe78bSCy Schubert if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
128*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_INAPP_CKSUM;
129*7f2fe78bSCy Schubert
130*7f2fe78bSCy Schubert /* verify checksum */
131*7f2fe78bSCy Schubert if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
132*7f2fe78bSCy Schubert KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
133*7f2fe78bSCy Schubert source, his_cksum, &valid)))
134*7f2fe78bSCy Schubert return(retval);
135*7f2fe78bSCy Schubert
136*7f2fe78bSCy Schubert if (!valid)
137*7f2fe78bSCy Schubert return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
138*7f2fe78bSCy Schubert
139*7f2fe78bSCy Schubert return(0);
140*7f2fe78bSCy Schubert }
141*7f2fe78bSCy Schubert
142*7f2fe78bSCy Schubert /* If a header ticket is decrypted, *ticket_out is filled in even on error. */
143*7f2fe78bSCy Schubert krb5_error_code
kdc_process_tgs_req(kdc_realm_t * realm,krb5_kdc_req * request,const krb5_fulladdr * from,krb5_data * pkt,krb5_ticket ** ticket_out,krb5_db_entry ** krbtgt_ptr,krb5_keyblock ** tgskey,krb5_keyblock ** subkey,krb5_pa_data ** pa_tgs_req)144*7f2fe78bSCy Schubert kdc_process_tgs_req(kdc_realm_t *realm, krb5_kdc_req *request,
145*7f2fe78bSCy Schubert const krb5_fulladdr *from, krb5_data *pkt,
146*7f2fe78bSCy Schubert krb5_ticket **ticket_out, krb5_db_entry **krbtgt_ptr,
147*7f2fe78bSCy Schubert krb5_keyblock **tgskey, krb5_keyblock **subkey,
148*7f2fe78bSCy Schubert krb5_pa_data **pa_tgs_req)
149*7f2fe78bSCy Schubert {
150*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
151*7f2fe78bSCy Schubert krb5_pa_data * tmppa;
152*7f2fe78bSCy Schubert krb5_ap_req * apreq;
153*7f2fe78bSCy Schubert krb5_error_code retval;
154*7f2fe78bSCy Schubert krb5_authdata **authdata = NULL;
155*7f2fe78bSCy Schubert krb5_data scratch1;
156*7f2fe78bSCy Schubert krb5_data * scratch = NULL;
157*7f2fe78bSCy Schubert krb5_auth_context auth_context = NULL;
158*7f2fe78bSCy Schubert krb5_authenticator * authenticator = NULL;
159*7f2fe78bSCy Schubert krb5_checksum * his_cksum = NULL;
160*7f2fe78bSCy Schubert krb5_db_entry * krbtgt = NULL;
161*7f2fe78bSCy Schubert krb5_ticket * ticket;
162*7f2fe78bSCy Schubert
163*7f2fe78bSCy Schubert *ticket_out = NULL;
164*7f2fe78bSCy Schubert *krbtgt_ptr = NULL;
165*7f2fe78bSCy Schubert *tgskey = NULL;
166*7f2fe78bSCy Schubert
167*7f2fe78bSCy Schubert tmppa = krb5int_find_pa_data(context, request->padata, KRB5_PADATA_AP_REQ);
168*7f2fe78bSCy Schubert if (!tmppa)
169*7f2fe78bSCy Schubert return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
170*7f2fe78bSCy Schubert
171*7f2fe78bSCy Schubert scratch1.length = tmppa->length;
172*7f2fe78bSCy Schubert scratch1.data = (char *)tmppa->contents;
173*7f2fe78bSCy Schubert if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
174*7f2fe78bSCy Schubert return retval;
175*7f2fe78bSCy Schubert ticket = apreq->ticket;
176*7f2fe78bSCy Schubert
177*7f2fe78bSCy Schubert if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
178*7f2fe78bSCy Schubert isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
179*7f2fe78bSCy Schubert krb5_klog_syslog(LOG_INFO, _("TGS_REQ: SESSION KEY or MUTUAL"));
180*7f2fe78bSCy Schubert retval = KRB5KDC_ERR_POLICY;
181*7f2fe78bSCy Schubert goto cleanup;
182*7f2fe78bSCy Schubert }
183*7f2fe78bSCy Schubert
184*7f2fe78bSCy Schubert retval = krb5_auth_con_init(context, &auth_context);
185*7f2fe78bSCy Schubert if (retval)
186*7f2fe78bSCy Schubert goto cleanup;
187*7f2fe78bSCy Schubert
188*7f2fe78bSCy Schubert /* Don't use a replay cache. */
189*7f2fe78bSCy Schubert retval = krb5_auth_con_setflags(context, auth_context, 0);
190*7f2fe78bSCy Schubert if (retval)
191*7f2fe78bSCy Schubert goto cleanup;
192*7f2fe78bSCy Schubert
193*7f2fe78bSCy Schubert retval = krb5_auth_con_setaddrs(context, auth_context, NULL,
194*7f2fe78bSCy Schubert from->address);
195*7f2fe78bSCy Schubert if (retval)
196*7f2fe78bSCy Schubert goto cleanup_auth_context;
197*7f2fe78bSCy Schubert
198*7f2fe78bSCy Schubert retval = kdc_rd_ap_req(realm, apreq, auth_context, &krbtgt, tgskey);
199*7f2fe78bSCy Schubert if (retval)
200*7f2fe78bSCy Schubert goto cleanup_auth_context;
201*7f2fe78bSCy Schubert
202*7f2fe78bSCy Schubert retval = krb5_auth_con_getrecvsubkey(context, auth_context, subkey);
203*7f2fe78bSCy Schubert if (retval)
204*7f2fe78bSCy Schubert goto cleanup_auth_context;
205*7f2fe78bSCy Schubert
206*7f2fe78bSCy Schubert retval = krb5_auth_con_getauthenticator(context, auth_context,
207*7f2fe78bSCy Schubert &authenticator);
208*7f2fe78bSCy Schubert if (retval)
209*7f2fe78bSCy Schubert goto cleanup_auth_context;
210*7f2fe78bSCy Schubert
211*7f2fe78bSCy Schubert retval = krb5_find_authdata(context, ticket->enc_part2->authorization_data,
212*7f2fe78bSCy Schubert authenticator->authorization_data,
213*7f2fe78bSCy Schubert KRB5_AUTHDATA_FX_ARMOR, &authdata);
214*7f2fe78bSCy Schubert if (retval != 0)
215*7f2fe78bSCy Schubert goto cleanup_authenticator;
216*7f2fe78bSCy Schubert if (authdata&& authdata[0]) {
217*7f2fe78bSCy Schubert k5_setmsg(context, KRB5KDC_ERR_POLICY,
218*7f2fe78bSCy Schubert "ticket valid only as FAST armor");
219*7f2fe78bSCy Schubert retval = KRB5KDC_ERR_POLICY;
220*7f2fe78bSCy Schubert krb5_free_authdata(context, authdata);
221*7f2fe78bSCy Schubert goto cleanup_authenticator;
222*7f2fe78bSCy Schubert }
223*7f2fe78bSCy Schubert krb5_free_authdata(context, authdata);
224*7f2fe78bSCy Schubert
225*7f2fe78bSCy Schubert
226*7f2fe78bSCy Schubert /* Check for a checksum */
227*7f2fe78bSCy Schubert if (!(his_cksum = authenticator->checksum)) {
228*7f2fe78bSCy Schubert retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
229*7f2fe78bSCy Schubert goto cleanup_authenticator;
230*7f2fe78bSCy Schubert }
231*7f2fe78bSCy Schubert
232*7f2fe78bSCy Schubert /*
233*7f2fe78bSCy Schubert * Check application checksum vs. tgs request
234*7f2fe78bSCy Schubert *
235*7f2fe78bSCy Schubert * We try checksumming the req-body two different ways: first we
236*7f2fe78bSCy Schubert * try reaching into the raw asn.1 stream (if available), and
237*7f2fe78bSCy Schubert * checksum that directly; if that fails, then we try encoding
238*7f2fe78bSCy Schubert * using our local asn.1 library.
239*7f2fe78bSCy Schubert */
240*7f2fe78bSCy Schubert if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
241*7f2fe78bSCy Schubert 1, 4, &scratch1) >= 0)) {
242*7f2fe78bSCy Schubert if (comp_cksum(context, &scratch1, ticket, his_cksum)) {
243*7f2fe78bSCy Schubert if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
244*7f2fe78bSCy Schubert retval = comp_cksum(context, scratch, ticket, his_cksum);
245*7f2fe78bSCy Schubert krb5_free_data(context, scratch);
246*7f2fe78bSCy Schubert if (retval)
247*7f2fe78bSCy Schubert goto cleanup_authenticator;
248*7f2fe78bSCy Schubert }
249*7f2fe78bSCy Schubert }
250*7f2fe78bSCy Schubert
251*7f2fe78bSCy Schubert *pa_tgs_req = tmppa;
252*7f2fe78bSCy Schubert *krbtgt_ptr = krbtgt;
253*7f2fe78bSCy Schubert krbtgt = NULL;
254*7f2fe78bSCy Schubert
255*7f2fe78bSCy Schubert cleanup_authenticator:
256*7f2fe78bSCy Schubert krb5_free_authenticator(context, authenticator);
257*7f2fe78bSCy Schubert
258*7f2fe78bSCy Schubert cleanup_auth_context:
259*7f2fe78bSCy Schubert krb5_auth_con_free(context, auth_context);
260*7f2fe78bSCy Schubert
261*7f2fe78bSCy Schubert cleanup:
262*7f2fe78bSCy Schubert if (retval != 0) {
263*7f2fe78bSCy Schubert krb5_free_keyblock(context, *tgskey);
264*7f2fe78bSCy Schubert *tgskey = NULL;
265*7f2fe78bSCy Schubert }
266*7f2fe78bSCy Schubert if (apreq->ticket->enc_part2 != NULL) {
267*7f2fe78bSCy Schubert /* Steal the decrypted ticket pointer, even on error. */
268*7f2fe78bSCy Schubert *ticket_out = apreq->ticket;
269*7f2fe78bSCy Schubert apreq->ticket = NULL;
270*7f2fe78bSCy Schubert }
271*7f2fe78bSCy Schubert krb5_free_ap_req(context, apreq);
272*7f2fe78bSCy Schubert krb5_db_free_principal(context, krbtgt);
273*7f2fe78bSCy Schubert return retval;
274*7f2fe78bSCy Schubert }
275*7f2fe78bSCy Schubert
276*7f2fe78bSCy Schubert /*
277*7f2fe78bSCy Schubert * This is a KDC wrapper around krb5_rd_req_decoded_anyflag().
278*7f2fe78bSCy Schubert *
279*7f2fe78bSCy Schubert * We can't depend on KDB-as-keytab for handling the AP-REQ here for
280*7f2fe78bSCy Schubert * optimization reasons: we want to minimize the number of KDB lookups. We'll
281*7f2fe78bSCy Schubert * need the KDB entry for the TGS principal, and the TGS key used to decrypt
282*7f2fe78bSCy Schubert * the TGT, elsewhere in the TGS code.
283*7f2fe78bSCy Schubert *
284*7f2fe78bSCy Schubert * This function also implements key rollover support for kvno 0 cross-realm
285*7f2fe78bSCy Schubert * TGTs issued by AD.
286*7f2fe78bSCy Schubert */
287*7f2fe78bSCy Schubert static
288*7f2fe78bSCy Schubert krb5_error_code
kdc_rd_ap_req(kdc_realm_t * realm,krb5_ap_req * apreq,krb5_auth_context auth_context,krb5_db_entry ** server,krb5_keyblock ** tgskey)289*7f2fe78bSCy Schubert kdc_rd_ap_req(kdc_realm_t *realm, krb5_ap_req *apreq,
290*7f2fe78bSCy Schubert krb5_auth_context auth_context, krb5_db_entry **server,
291*7f2fe78bSCy Schubert krb5_keyblock **tgskey)
292*7f2fe78bSCy Schubert {
293*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
294*7f2fe78bSCy Schubert krb5_error_code retval;
295*7f2fe78bSCy Schubert krb5_enctype search_enctype = apreq->ticket->enc_part.enctype;
296*7f2fe78bSCy Schubert krb5_boolean match_enctype = 1;
297*7f2fe78bSCy Schubert krb5_kvno kvno;
298*7f2fe78bSCy Schubert size_t tries = 3;
299*7f2fe78bSCy Schubert
300*7f2fe78bSCy Schubert /*
301*7f2fe78bSCy Schubert * When we issue tickets we use the first key in the principals' highest
302*7f2fe78bSCy Schubert * kvno keyset. For non-cross-realm krbtgt principals we want to only
303*7f2fe78bSCy Schubert * allow the use of the first key of the principal's keyset that matches
304*7f2fe78bSCy Schubert * the given kvno.
305*7f2fe78bSCy Schubert */
306*7f2fe78bSCy Schubert if (krb5_is_tgs_principal(apreq->ticket->server) &&
307*7f2fe78bSCy Schubert !is_cross_tgs_principal(apreq->ticket->server)) {
308*7f2fe78bSCy Schubert search_enctype = -1;
309*7f2fe78bSCy Schubert match_enctype = 0;
310*7f2fe78bSCy Schubert }
311*7f2fe78bSCy Schubert
312*7f2fe78bSCy Schubert retval = kdc_get_server_key(context, apreq->ticket, 0, match_enctype,
313*7f2fe78bSCy Schubert server, NULL, NULL);
314*7f2fe78bSCy Schubert if (retval)
315*7f2fe78bSCy Schubert return retval;
316*7f2fe78bSCy Schubert
317*7f2fe78bSCy Schubert *tgskey = NULL;
318*7f2fe78bSCy Schubert kvno = apreq->ticket->enc_part.kvno;
319*7f2fe78bSCy Schubert do {
320*7f2fe78bSCy Schubert krb5_free_keyblock(context, *tgskey);
321*7f2fe78bSCy Schubert retval = find_server_key(context, *server, search_enctype, kvno,
322*7f2fe78bSCy Schubert tgskey, &kvno);
323*7f2fe78bSCy Schubert if (retval)
324*7f2fe78bSCy Schubert continue;
325*7f2fe78bSCy Schubert
326*7f2fe78bSCy Schubert /* Make the TGS key available to krb5_rd_req_decoded_anyflag() */
327*7f2fe78bSCy Schubert retval = krb5_auth_con_setuseruserkey(context, auth_context, *tgskey);
328*7f2fe78bSCy Schubert if (retval)
329*7f2fe78bSCy Schubert return retval;
330*7f2fe78bSCy Schubert
331*7f2fe78bSCy Schubert retval = krb5_rd_req_decoded_anyflag(context, &auth_context, apreq,
332*7f2fe78bSCy Schubert apreq->ticket->server,
333*7f2fe78bSCy Schubert realm->realm_keytab, NULL, NULL);
334*7f2fe78bSCy Schubert
335*7f2fe78bSCy Schubert /* If the ticket was decrypted, don't try any more keys. */
336*7f2fe78bSCy Schubert if (apreq->ticket->enc_part2 != NULL)
337*7f2fe78bSCy Schubert break;
338*7f2fe78bSCy Schubert
339*7f2fe78bSCy Schubert } while (retval && apreq->ticket->enc_part.kvno == 0 && kvno-- > 1 &&
340*7f2fe78bSCy Schubert --tries > 0);
341*7f2fe78bSCy Schubert
342*7f2fe78bSCy Schubert return retval;
343*7f2fe78bSCy Schubert }
344*7f2fe78bSCy Schubert
345*7f2fe78bSCy Schubert /*
346*7f2fe78bSCy Schubert * The KDC should take the keytab associated with the realm and pass
347*7f2fe78bSCy Schubert * that to the krb5_rd_req_decoded_anyflag(), but we still need to use
348*7f2fe78bSCy Schubert * the service (TGS, here) key elsewhere. This approach is faster than
349*7f2fe78bSCy Schubert * the KDB keytab approach too.
350*7f2fe78bSCy Schubert *
351*7f2fe78bSCy Schubert * This is also used by do_tgs_req() for u2u auth.
352*7f2fe78bSCy Schubert */
353*7f2fe78bSCy Schubert krb5_error_code
kdc_get_server_key(krb5_context context,krb5_ticket * ticket,unsigned int flags,krb5_boolean match_enctype,krb5_db_entry ** server_ptr,krb5_keyblock ** key,krb5_kvno * kvno)354*7f2fe78bSCy Schubert kdc_get_server_key(krb5_context context,
355*7f2fe78bSCy Schubert krb5_ticket *ticket, unsigned int flags,
356*7f2fe78bSCy Schubert krb5_boolean match_enctype, krb5_db_entry **server_ptr,
357*7f2fe78bSCy Schubert krb5_keyblock **key, krb5_kvno *kvno)
358*7f2fe78bSCy Schubert {
359*7f2fe78bSCy Schubert krb5_error_code retval;
360*7f2fe78bSCy Schubert krb5_db_entry * server = NULL;
361*7f2fe78bSCy Schubert krb5_enctype search_enctype = -1;
362*7f2fe78bSCy Schubert krb5_kvno search_kvno = -1;
363*7f2fe78bSCy Schubert
364*7f2fe78bSCy Schubert if (match_enctype)
365*7f2fe78bSCy Schubert search_enctype = ticket->enc_part.enctype;
366*7f2fe78bSCy Schubert if (ticket->enc_part.kvno)
367*7f2fe78bSCy Schubert search_kvno = ticket->enc_part.kvno;
368*7f2fe78bSCy Schubert
369*7f2fe78bSCy Schubert *server_ptr = NULL;
370*7f2fe78bSCy Schubert
371*7f2fe78bSCy Schubert retval = krb5_db_get_principal(context, ticket->server, flags,
372*7f2fe78bSCy Schubert &server);
373*7f2fe78bSCy Schubert if (retval == KRB5_KDB_NOENTRY) {
374*7f2fe78bSCy Schubert char *sname;
375*7f2fe78bSCy Schubert if (!krb5_unparse_name(context, ticket->server, &sname)) {
376*7f2fe78bSCy Schubert limit_string(sname);
377*7f2fe78bSCy Schubert krb5_klog_syslog(LOG_ERR,
378*7f2fe78bSCy Schubert _("TGS_REQ: UNKNOWN SERVER: server='%s'"), sname);
379*7f2fe78bSCy Schubert free(sname);
380*7f2fe78bSCy Schubert }
381*7f2fe78bSCy Schubert return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
382*7f2fe78bSCy Schubert } else if (retval)
383*7f2fe78bSCy Schubert return retval;
384*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
385*7f2fe78bSCy Schubert server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
386*7f2fe78bSCy Schubert retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
387*7f2fe78bSCy Schubert goto errout;
388*7f2fe78bSCy Schubert }
389*7f2fe78bSCy Schubert
390*7f2fe78bSCy Schubert if (key) {
391*7f2fe78bSCy Schubert retval = find_server_key(context, server, search_enctype, search_kvno,
392*7f2fe78bSCy Schubert key, kvno);
393*7f2fe78bSCy Schubert if (retval)
394*7f2fe78bSCy Schubert goto errout;
395*7f2fe78bSCy Schubert }
396*7f2fe78bSCy Schubert *server_ptr = server;
397*7f2fe78bSCy Schubert server = NULL;
398*7f2fe78bSCy Schubert return 0;
399*7f2fe78bSCy Schubert
400*7f2fe78bSCy Schubert errout:
401*7f2fe78bSCy Schubert krb5_db_free_principal(context, server);
402*7f2fe78bSCy Schubert return retval;
403*7f2fe78bSCy Schubert }
404*7f2fe78bSCy Schubert
405*7f2fe78bSCy Schubert /*
406*7f2fe78bSCy Schubert * A utility function to get the right key from a KDB entry. Used in handling
407*7f2fe78bSCy Schubert * of kvno 0 TGTs, for example.
408*7f2fe78bSCy Schubert */
409*7f2fe78bSCy Schubert static
410*7f2fe78bSCy Schubert krb5_error_code
find_server_key(krb5_context context,krb5_db_entry * server,krb5_enctype enctype,krb5_kvno kvno,krb5_keyblock ** key_out,krb5_kvno * kvno_out)411*7f2fe78bSCy Schubert find_server_key(krb5_context context,
412*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_enctype enctype, krb5_kvno kvno,
413*7f2fe78bSCy Schubert krb5_keyblock **key_out, krb5_kvno *kvno_out)
414*7f2fe78bSCy Schubert {
415*7f2fe78bSCy Schubert krb5_error_code retval;
416*7f2fe78bSCy Schubert krb5_key_data * server_key;
417*7f2fe78bSCy Schubert krb5_keyblock * key;
418*7f2fe78bSCy Schubert
419*7f2fe78bSCy Schubert *key_out = NULL;
420*7f2fe78bSCy Schubert retval = krb5_dbe_find_enctype(context, server, enctype, -1,
421*7f2fe78bSCy Schubert kvno ? (krb5_int32)kvno : -1, &server_key);
422*7f2fe78bSCy Schubert if (retval)
423*7f2fe78bSCy Schubert return retval;
424*7f2fe78bSCy Schubert if (!server_key)
425*7f2fe78bSCy Schubert return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
426*7f2fe78bSCy Schubert if ((key = (krb5_keyblock *)malloc(sizeof *key)) == NULL)
427*7f2fe78bSCy Schubert return ENOMEM;
428*7f2fe78bSCy Schubert retval = krb5_dbe_decrypt_key_data(context, NULL, server_key,
429*7f2fe78bSCy Schubert key, NULL);
430*7f2fe78bSCy Schubert if (retval)
431*7f2fe78bSCy Schubert goto errout;
432*7f2fe78bSCy Schubert if (enctype != -1) {
433*7f2fe78bSCy Schubert krb5_boolean similar;
434*7f2fe78bSCy Schubert retval = krb5_c_enctype_compare(context, enctype, key->enctype,
435*7f2fe78bSCy Schubert &similar);
436*7f2fe78bSCy Schubert if (retval)
437*7f2fe78bSCy Schubert goto errout;
438*7f2fe78bSCy Schubert if (!similar) {
439*7f2fe78bSCy Schubert retval = KRB5_KDB_NO_PERMITTED_KEY;
440*7f2fe78bSCy Schubert goto errout;
441*7f2fe78bSCy Schubert }
442*7f2fe78bSCy Schubert key->enctype = enctype;
443*7f2fe78bSCy Schubert }
444*7f2fe78bSCy Schubert *key_out = key;
445*7f2fe78bSCy Schubert key = NULL;
446*7f2fe78bSCy Schubert if (kvno_out)
447*7f2fe78bSCy Schubert *kvno_out = server_key->key_data_kvno;
448*7f2fe78bSCy Schubert errout:
449*7f2fe78bSCy Schubert krb5_free_keyblock(context, key);
450*7f2fe78bSCy Schubert return retval;
451*7f2fe78bSCy Schubert }
452*7f2fe78bSCy Schubert
453*7f2fe78bSCy Schubert /* Find the first key data entry (of a valid enctype) of the highest kvno in
454*7f2fe78bSCy Schubert * entry, and decrypt it into *key_out. */
455*7f2fe78bSCy Schubert krb5_error_code
get_first_current_key(krb5_context context,krb5_db_entry * entry,krb5_keyblock * key_out)456*7f2fe78bSCy Schubert get_first_current_key(krb5_context context, krb5_db_entry *entry,
457*7f2fe78bSCy Schubert krb5_keyblock *key_out)
458*7f2fe78bSCy Schubert {
459*7f2fe78bSCy Schubert krb5_error_code ret;
460*7f2fe78bSCy Schubert krb5_key_data *kd;
461*7f2fe78bSCy Schubert
462*7f2fe78bSCy Schubert memset(key_out, 0, sizeof(*key_out));
463*7f2fe78bSCy Schubert ret = krb5_dbe_find_enctype(context, entry, -1, -1, 0, &kd);
464*7f2fe78bSCy Schubert if (ret)
465*7f2fe78bSCy Schubert return ret;
466*7f2fe78bSCy Schubert return krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
467*7f2fe78bSCy Schubert }
468*7f2fe78bSCy Schubert
469*7f2fe78bSCy Schubert /*
470*7f2fe78bSCy Schubert * If candidate is the local TGT for realm, set *alias_out to candidate and
471*7f2fe78bSCy Schubert * *storage_out to NULL. Otherwise, load the local TGT into *storage_out and
472*7f2fe78bSCy Schubert * set *alias_out to *storage_out. In either case, set *key_out to the
473*7f2fe78bSCy Schubert * decrypted first key of the local TGT.
474*7f2fe78bSCy Schubert *
475*7f2fe78bSCy Schubert * In the future we might generalize this to a small per-request principal
476*7f2fe78bSCy Schubert * cache. For now, it saves a load operation in the common case where the AS
477*7f2fe78bSCy Schubert * server or TGS header ticket server is the local TGT.
478*7f2fe78bSCy Schubert */
479*7f2fe78bSCy Schubert krb5_error_code
get_local_tgt(krb5_context context,const krb5_data * realm,krb5_db_entry * candidate,krb5_db_entry ** alias_out,krb5_db_entry ** storage_out,krb5_keyblock * key_out)480*7f2fe78bSCy Schubert get_local_tgt(krb5_context context, const krb5_data *realm,
481*7f2fe78bSCy Schubert krb5_db_entry *candidate, krb5_db_entry **alias_out,
482*7f2fe78bSCy Schubert krb5_db_entry **storage_out, krb5_keyblock *key_out)
483*7f2fe78bSCy Schubert {
484*7f2fe78bSCy Schubert krb5_error_code ret;
485*7f2fe78bSCy Schubert krb5_principal princ;
486*7f2fe78bSCy Schubert krb5_db_entry *storage = NULL, *tgt;
487*7f2fe78bSCy Schubert
488*7f2fe78bSCy Schubert *alias_out = NULL;
489*7f2fe78bSCy Schubert *storage_out = NULL;
490*7f2fe78bSCy Schubert memset(key_out, 0, sizeof(*key_out));
491*7f2fe78bSCy Schubert
492*7f2fe78bSCy Schubert ret = krb5_build_principal_ext(context, &princ, realm->length, realm->data,
493*7f2fe78bSCy Schubert KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
494*7f2fe78bSCy Schubert realm->length, realm->data, 0);
495*7f2fe78bSCy Schubert if (ret)
496*7f2fe78bSCy Schubert goto cleanup;
497*7f2fe78bSCy Schubert
498*7f2fe78bSCy Schubert if (!krb5_principal_compare(context, candidate->princ, princ)) {
499*7f2fe78bSCy Schubert ret = krb5_db_get_principal(context, princ, 0, &storage);
500*7f2fe78bSCy Schubert if (ret)
501*7f2fe78bSCy Schubert goto cleanup;
502*7f2fe78bSCy Schubert tgt = storage;
503*7f2fe78bSCy Schubert } else {
504*7f2fe78bSCy Schubert tgt = candidate;
505*7f2fe78bSCy Schubert }
506*7f2fe78bSCy Schubert
507*7f2fe78bSCy Schubert ret = get_first_current_key(context, tgt, key_out);
508*7f2fe78bSCy Schubert if (ret)
509*7f2fe78bSCy Schubert goto cleanup;
510*7f2fe78bSCy Schubert
511*7f2fe78bSCy Schubert *alias_out = tgt;
512*7f2fe78bSCy Schubert *storage_out = storage;
513*7f2fe78bSCy Schubert storage = NULL;
514*7f2fe78bSCy Schubert
515*7f2fe78bSCy Schubert cleanup:
516*7f2fe78bSCy Schubert krb5_db_free_principal(context, storage);
517*7f2fe78bSCy Schubert krb5_free_principal(context, princ);
518*7f2fe78bSCy Schubert return ret;
519*7f2fe78bSCy Schubert }
520*7f2fe78bSCy Schubert
521*7f2fe78bSCy Schubert /* If server has a pac_privsvr_enctype attribute and it differs from tgt_key's
522*7f2fe78bSCy Schubert * enctype, derive a key of the specified enctype. Otherwise copy tgt_key. */
523*7f2fe78bSCy Schubert krb5_error_code
pac_privsvr_key(krb5_context context,krb5_db_entry * server,const krb5_keyblock * tgt_key,krb5_keyblock ** key_out)524*7f2fe78bSCy Schubert pac_privsvr_key(krb5_context context, krb5_db_entry *server,
525*7f2fe78bSCy Schubert const krb5_keyblock *tgt_key, krb5_keyblock **key_out)
526*7f2fe78bSCy Schubert {
527*7f2fe78bSCy Schubert krb5_error_code ret;
528*7f2fe78bSCy Schubert char *attrval = NULL;
529*7f2fe78bSCy Schubert krb5_enctype privsvr_enctype;
530*7f2fe78bSCy Schubert krb5_data prf_input = string2data("pac_privsvr");
531*7f2fe78bSCy Schubert
532*7f2fe78bSCy Schubert ret = krb5_dbe_get_string(context, server, KRB5_KDB_SK_PAC_PRIVSVR_ENCTYPE,
533*7f2fe78bSCy Schubert &attrval);
534*7f2fe78bSCy Schubert if (ret)
535*7f2fe78bSCy Schubert return ret;
536*7f2fe78bSCy Schubert if (attrval == NULL)
537*7f2fe78bSCy Schubert return krb5_copy_keyblock(context, tgt_key, key_out);
538*7f2fe78bSCy Schubert
539*7f2fe78bSCy Schubert ret = krb5_string_to_enctype(attrval, &privsvr_enctype);
540*7f2fe78bSCy Schubert if (ret) {
541*7f2fe78bSCy Schubert k5_setmsg(context, ret, _("Invalid pac_privsvr_enctype value %s"),
542*7f2fe78bSCy Schubert attrval);
543*7f2fe78bSCy Schubert goto cleanup;
544*7f2fe78bSCy Schubert }
545*7f2fe78bSCy Schubert
546*7f2fe78bSCy Schubert if (tgt_key->enctype == privsvr_enctype) {
547*7f2fe78bSCy Schubert ret = krb5_copy_keyblock(context, tgt_key, key_out);
548*7f2fe78bSCy Schubert } else {
549*7f2fe78bSCy Schubert ret = krb5_c_derive_prfplus(context, tgt_key, &prf_input,
550*7f2fe78bSCy Schubert privsvr_enctype, key_out);
551*7f2fe78bSCy Schubert }
552*7f2fe78bSCy Schubert
553*7f2fe78bSCy Schubert cleanup:
554*7f2fe78bSCy Schubert krb5_dbe_free_string(context, attrval);
555*7f2fe78bSCy Schubert return ret;
556*7f2fe78bSCy Schubert }
557*7f2fe78bSCy Schubert
558*7f2fe78bSCy Schubert /* Try verifying a ticket's PAC using a privsvr key either equal to or derived
559*7f2fe78bSCy Schubert * from tgt_key, respecting the server's pac_privsvr_enctype value if set. */
560*7f2fe78bSCy Schubert static krb5_error_code
try_verify_pac(krb5_context context,const krb5_enc_tkt_part * enc_tkt,krb5_db_entry * server,krb5_keyblock * server_key,const krb5_keyblock * tgt_key,krb5_pac * pac_out)561*7f2fe78bSCy Schubert try_verify_pac(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
562*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_keyblock *server_key,
563*7f2fe78bSCy Schubert const krb5_keyblock *tgt_key, krb5_pac *pac_out)
564*7f2fe78bSCy Schubert {
565*7f2fe78bSCy Schubert krb5_error_code ret;
566*7f2fe78bSCy Schubert krb5_keyblock *privsvr_key;
567*7f2fe78bSCy Schubert
568*7f2fe78bSCy Schubert ret = pac_privsvr_key(context, server, tgt_key, &privsvr_key);
569*7f2fe78bSCy Schubert if (ret)
570*7f2fe78bSCy Schubert return ret;
571*7f2fe78bSCy Schubert ret = krb5_kdc_verify_ticket(context, enc_tkt, server->princ, server_key,
572*7f2fe78bSCy Schubert privsvr_key, pac_out);
573*7f2fe78bSCy Schubert krb5_free_keyblock(context, privsvr_key);
574*7f2fe78bSCy Schubert return ret;
575*7f2fe78bSCy Schubert }
576*7f2fe78bSCy Schubert
577*7f2fe78bSCy Schubert /*
578*7f2fe78bSCy Schubert * If a PAC is present in enc_tkt, verify it and place it in *pac_out. sprinc
579*7f2fe78bSCy Schubert * is the canonical name of the server principal entry used to decrypt enc_tkt.
580*7f2fe78bSCy Schubert * server_key is the ticket decryption key. tgt is the local krbtgt entry for
581*7f2fe78bSCy Schubert * the ticket server realm, and tgt_key is its first key.
582*7f2fe78bSCy Schubert */
583*7f2fe78bSCy Schubert krb5_error_code
get_verified_pac(krb5_context context,const krb5_enc_tkt_part * enc_tkt,krb5_db_entry * server,krb5_keyblock * server_key,krb5_db_entry * tgt,krb5_keyblock * tgt_key,krb5_pac * pac_out)584*7f2fe78bSCy Schubert get_verified_pac(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
585*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_keyblock *server_key,
586*7f2fe78bSCy Schubert krb5_db_entry *tgt, krb5_keyblock *tgt_key, krb5_pac *pac_out)
587*7f2fe78bSCy Schubert {
588*7f2fe78bSCy Schubert krb5_error_code ret;
589*7f2fe78bSCy Schubert krb5_key_data *kd;
590*7f2fe78bSCy Schubert krb5_keyblock old_key;
591*7f2fe78bSCy Schubert krb5_kvno kvno;
592*7f2fe78bSCy Schubert int tries;
593*7f2fe78bSCy Schubert
594*7f2fe78bSCy Schubert *pac_out = NULL;
595*7f2fe78bSCy Schubert
596*7f2fe78bSCy Schubert /* For local or cross-realm TGTs we only check the server signature. */
597*7f2fe78bSCy Schubert if (krb5_is_tgs_principal(server->princ)) {
598*7f2fe78bSCy Schubert return krb5_kdc_verify_ticket(context, enc_tkt, server->princ,
599*7f2fe78bSCy Schubert server_key, NULL, pac_out);
600*7f2fe78bSCy Schubert }
601*7f2fe78bSCy Schubert
602*7f2fe78bSCy Schubert ret = try_verify_pac(context, enc_tkt, server, server_key, tgt_key,
603*7f2fe78bSCy Schubert pac_out);
604*7f2fe78bSCy Schubert if (ret != KRB5KRB_AP_ERR_MODIFIED && ret != KRB5_BAD_ENCTYPE)
605*7f2fe78bSCy Schubert return ret;
606*7f2fe78bSCy Schubert
607*7f2fe78bSCy Schubert /* There is no kvno in PAC signatures, so try two previous versions. */
608*7f2fe78bSCy Schubert kvno = tgt->key_data[0].key_data_kvno - 1;
609*7f2fe78bSCy Schubert for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
610*7f2fe78bSCy Schubert ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
611*7f2fe78bSCy Schubert if (ret)
612*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_MODIFIED;
613*7f2fe78bSCy Schubert ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
614*7f2fe78bSCy Schubert if (ret)
615*7f2fe78bSCy Schubert return ret;
616*7f2fe78bSCy Schubert ret = try_verify_pac(context, enc_tkt, server, server_key, &old_key,
617*7f2fe78bSCy Schubert pac_out);
618*7f2fe78bSCy Schubert krb5_free_keyblock_contents(context, &old_key);
619*7f2fe78bSCy Schubert if (!ret)
620*7f2fe78bSCy Schubert return 0;
621*7f2fe78bSCy Schubert }
622*7f2fe78bSCy Schubert
623*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_MODIFIED;
624*7f2fe78bSCy Schubert }
625*7f2fe78bSCy Schubert
626*7f2fe78bSCy Schubert /*
627*7f2fe78bSCy Schubert * Fetch the client info from pac and parse it into a principal name, expecting
628*7f2fe78bSCy Schubert * a realm in the string. Set *authtime_out to the client info authtime if it
629*7f2fe78bSCy Schubert * is not null.
630*7f2fe78bSCy Schubert */
631*7f2fe78bSCy Schubert krb5_error_code
get_pac_princ_with_realm(krb5_context context,krb5_pac pac,krb5_principal * princ_out,krb5_timestamp * authtime_out)632*7f2fe78bSCy Schubert get_pac_princ_with_realm(krb5_context context, krb5_pac pac,
633*7f2fe78bSCy Schubert krb5_principal *princ_out,
634*7f2fe78bSCy Schubert krb5_timestamp *authtime_out)
635*7f2fe78bSCy Schubert {
636*7f2fe78bSCy Schubert krb5_error_code ret;
637*7f2fe78bSCy Schubert int n_atsigns, flags = KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
638*7f2fe78bSCy Schubert char *client_str = NULL;
639*7f2fe78bSCy Schubert const char *p;
640*7f2fe78bSCy Schubert
641*7f2fe78bSCy Schubert *princ_out = NULL;
642*7f2fe78bSCy Schubert
643*7f2fe78bSCy Schubert ret = krb5_pac_get_client_info(context, pac, authtime_out, &client_str);
644*7f2fe78bSCy Schubert if (ret)
645*7f2fe78bSCy Schubert return ret;
646*7f2fe78bSCy Schubert
647*7f2fe78bSCy Schubert n_atsigns = 0;
648*7f2fe78bSCy Schubert for (p = client_str; *p != '\0'; p++) {
649*7f2fe78bSCy Schubert if (*p == '@')
650*7f2fe78bSCy Schubert n_atsigns++;
651*7f2fe78bSCy Schubert }
652*7f2fe78bSCy Schubert
653*7f2fe78bSCy Schubert if (n_atsigns == 2) {
654*7f2fe78bSCy Schubert flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
655*7f2fe78bSCy Schubert } else if (n_atsigns != 1) {
656*7f2fe78bSCy Schubert ret = KRB5_PARSE_MALFORMED;
657*7f2fe78bSCy Schubert goto cleanup;
658*7f2fe78bSCy Schubert }
659*7f2fe78bSCy Schubert
660*7f2fe78bSCy Schubert ret = krb5_parse_name_flags(context, client_str, flags, princ_out);
661*7f2fe78bSCy Schubert if (ret)
662*7f2fe78bSCy Schubert return ret;
663*7f2fe78bSCy Schubert
664*7f2fe78bSCy Schubert (*princ_out)->type = KRB5_NT_MS_PRINCIPAL;
665*7f2fe78bSCy Schubert
666*7f2fe78bSCy Schubert cleanup:
667*7f2fe78bSCy Schubert free(client_str);
668*7f2fe78bSCy Schubert return 0;
669*7f2fe78bSCy Schubert }
670*7f2fe78bSCy Schubert
671*7f2fe78bSCy Schubert /* This probably wants to be updated if you support last_req stuff */
672*7f2fe78bSCy Schubert
673*7f2fe78bSCy Schubert static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
674*7f2fe78bSCy Schubert static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
675*7f2fe78bSCy Schubert
676*7f2fe78bSCy Schubert krb5_error_code
fetch_last_req_info(krb5_db_entry * dbentry,krb5_last_req_entry *** lrentry)677*7f2fe78bSCy Schubert fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
678*7f2fe78bSCy Schubert {
679*7f2fe78bSCy Schubert *lrentry = nolrarray;
680*7f2fe78bSCy Schubert return 0;
681*7f2fe78bSCy Schubert }
682*7f2fe78bSCy Schubert
683*7f2fe78bSCy Schubert
684*7f2fe78bSCy Schubert /* Convert an API error code to a protocol error code. */
685*7f2fe78bSCy Schubert int
errcode_to_protocol(krb5_error_code code)686*7f2fe78bSCy Schubert errcode_to_protocol(krb5_error_code code)
687*7f2fe78bSCy Schubert {
688*7f2fe78bSCy Schubert int protcode;
689*7f2fe78bSCy Schubert
690*7f2fe78bSCy Schubert protcode = code - ERROR_TABLE_BASE_krb5;
691*7f2fe78bSCy Schubert return (protcode >= 0 && protcode <= 128) ? protcode : KRB_ERR_GENERIC;
692*7f2fe78bSCy Schubert }
693*7f2fe78bSCy Schubert
694*7f2fe78bSCy Schubert /* Return -1 if the AS or TGS request is disallowed due to KDC policy on
695*7f2fe78bSCy Schubert * anonymous tickets. */
696*7f2fe78bSCy Schubert int
check_anon(kdc_realm_t * realm,krb5_principal client,krb5_principal server)697*7f2fe78bSCy Schubert check_anon(kdc_realm_t *realm, krb5_principal client, krb5_principal server)
698*7f2fe78bSCy Schubert {
699*7f2fe78bSCy Schubert /* If restrict_anon is set, reject requests from anonymous clients to
700*7f2fe78bSCy Schubert * server principals other than local TGTs. */
701*7f2fe78bSCy Schubert if (realm->realm_restrict_anon &&
702*7f2fe78bSCy Schubert krb5_principal_compare_any_realm(realm->realm_context, client,
703*7f2fe78bSCy Schubert krb5_anonymous_principal()) &&
704*7f2fe78bSCy Schubert !is_local_tgs_principal(server))
705*7f2fe78bSCy Schubert return -1;
706*7f2fe78bSCy Schubert return 0;
707*7f2fe78bSCy Schubert }
708*7f2fe78bSCy Schubert
709*7f2fe78bSCy Schubert krb5_error_code
validate_as_request(kdc_realm_t * realm,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp kdc_time,const char ** status,krb5_pa_data *** e_data)710*7f2fe78bSCy Schubert validate_as_request(kdc_realm_t *realm, krb5_kdc_req *request,
711*7f2fe78bSCy Schubert krb5_db_entry *client, krb5_db_entry *server,
712*7f2fe78bSCy Schubert krb5_timestamp kdc_time, const char **status,
713*7f2fe78bSCy Schubert krb5_pa_data ***e_data)
714*7f2fe78bSCy Schubert {
715*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
716*7f2fe78bSCy Schubert krb5_error_code ret;
717*7f2fe78bSCy Schubert
718*7f2fe78bSCy Schubert /*
719*7f2fe78bSCy Schubert * If an option is set that is only allowed in TGS requests, complain.
720*7f2fe78bSCy Schubert */
721*7f2fe78bSCy Schubert if (request->kdc_options & AS_INVALID_OPTIONS) {
722*7f2fe78bSCy Schubert *status = "INVALID AS OPTIONS";
723*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
724*7f2fe78bSCy Schubert }
725*7f2fe78bSCy Schubert
726*7f2fe78bSCy Schubert /* The client must not be expired */
727*7f2fe78bSCy Schubert if (client->expiration && ts_after(kdc_time, client->expiration)) {
728*7f2fe78bSCy Schubert *status = "CLIENT EXPIRED";
729*7f2fe78bSCy Schubert if (vague_errors)
730*7f2fe78bSCy Schubert return KRB5KRB_ERR_GENERIC;
731*7f2fe78bSCy Schubert else
732*7f2fe78bSCy Schubert return KRB5KDC_ERR_NAME_EXP;
733*7f2fe78bSCy Schubert }
734*7f2fe78bSCy Schubert
735*7f2fe78bSCy Schubert /* The client's password must not be expired, unless the server is
736*7f2fe78bSCy Schubert a KRB5_KDC_PWCHANGE_SERVICE. */
737*7f2fe78bSCy Schubert if (client->pw_expiration && ts_after(kdc_time, client->pw_expiration) &&
738*7f2fe78bSCy Schubert !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
739*7f2fe78bSCy Schubert *status = "CLIENT KEY EXPIRED";
740*7f2fe78bSCy Schubert if (vague_errors)
741*7f2fe78bSCy Schubert return KRB5KRB_ERR_GENERIC;
742*7f2fe78bSCy Schubert else
743*7f2fe78bSCy Schubert return KRB5KDC_ERR_KEY_EXP;
744*7f2fe78bSCy Schubert }
745*7f2fe78bSCy Schubert
746*7f2fe78bSCy Schubert /* The server must not be expired */
747*7f2fe78bSCy Schubert if (server->expiration && ts_after(kdc_time, server->expiration)) {
748*7f2fe78bSCy Schubert *status = "SERVICE EXPIRED";
749*7f2fe78bSCy Schubert return KRB5KDC_ERR_SERVICE_EXP;
750*7f2fe78bSCy Schubert }
751*7f2fe78bSCy Schubert
752*7f2fe78bSCy Schubert /*
753*7f2fe78bSCy Schubert * If the client requires password changing, then only allow the
754*7f2fe78bSCy Schubert * pwchange service.
755*7f2fe78bSCy Schubert */
756*7f2fe78bSCy Schubert if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
757*7f2fe78bSCy Schubert !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
758*7f2fe78bSCy Schubert *status = "REQUIRED PWCHANGE";
759*7f2fe78bSCy Schubert return KRB5KDC_ERR_KEY_EXP;
760*7f2fe78bSCy Schubert }
761*7f2fe78bSCy Schubert
762*7f2fe78bSCy Schubert /* Client and server must allow postdating tickets */
763*7f2fe78bSCy Schubert if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
764*7f2fe78bSCy Schubert isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
765*7f2fe78bSCy Schubert (isflagset(client->attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
766*7f2fe78bSCy Schubert isflagset(server->attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
767*7f2fe78bSCy Schubert *status = "POSTDATE NOT ALLOWED";
768*7f2fe78bSCy Schubert return KRB5KDC_ERR_CANNOT_POSTDATE;
769*7f2fe78bSCy Schubert }
770*7f2fe78bSCy Schubert
771*7f2fe78bSCy Schubert /* Check to see if client is locked out */
772*7f2fe78bSCy Schubert if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
773*7f2fe78bSCy Schubert *status = "CLIENT LOCKED OUT";
774*7f2fe78bSCy Schubert return KRB5KDC_ERR_CLIENT_REVOKED;
775*7f2fe78bSCy Schubert }
776*7f2fe78bSCy Schubert
777*7f2fe78bSCy Schubert /* Check to see if server is locked out */
778*7f2fe78bSCy Schubert if (isflagset(server->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
779*7f2fe78bSCy Schubert *status = "SERVICE LOCKED OUT";
780*7f2fe78bSCy Schubert return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
781*7f2fe78bSCy Schubert }
782*7f2fe78bSCy Schubert
783*7f2fe78bSCy Schubert /* Check to see if server is allowed to be a service */
784*7f2fe78bSCy Schubert if (isflagset(server->attributes, KRB5_KDB_DISALLOW_SVR)) {
785*7f2fe78bSCy Schubert *status = "SERVICE NOT ALLOWED";
786*7f2fe78bSCy Schubert return KRB5KDC_ERR_MUST_USE_USER2USER;
787*7f2fe78bSCy Schubert }
788*7f2fe78bSCy Schubert
789*7f2fe78bSCy Schubert if (check_anon(realm, client->princ, request->server) != 0) {
790*7f2fe78bSCy Schubert *status = "ANONYMOUS NOT ALLOWED";
791*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
792*7f2fe78bSCy Schubert }
793*7f2fe78bSCy Schubert
794*7f2fe78bSCy Schubert /* Perform KDB module policy checks. */
795*7f2fe78bSCy Schubert ret = krb5_db_check_policy_as(context, request, client, server, kdc_time,
796*7f2fe78bSCy Schubert status, e_data);
797*7f2fe78bSCy Schubert return (ret == KRB5_PLUGIN_OP_NOTSUPP) ? 0 : ret;
798*7f2fe78bSCy Schubert }
799*7f2fe78bSCy Schubert
800*7f2fe78bSCy Schubert /*
801*7f2fe78bSCy Schubert * Compute ticket flags based on the request, the client and server DB entry
802*7f2fe78bSCy Schubert * (which may prohibit forwardable or proxiable tickets), and the header
803*7f2fe78bSCy Schubert * ticket. client may be NULL for a TGS request (although it may be set, such
804*7f2fe78bSCy Schubert * as for an S4U2Self request). header_enc may be NULL for an AS request.
805*7f2fe78bSCy Schubert */
806*7f2fe78bSCy Schubert krb5_flags
get_ticket_flags(krb5_flags reqflags,krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * header_enc)807*7f2fe78bSCy Schubert get_ticket_flags(krb5_flags reqflags, krb5_db_entry *client,
808*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_enc_tkt_part *header_enc)
809*7f2fe78bSCy Schubert {
810*7f2fe78bSCy Schubert krb5_flags flags;
811*7f2fe78bSCy Schubert
812*7f2fe78bSCy Schubert /* Validation and renewal TGS requests preserve the header ticket flags. */
813*7f2fe78bSCy Schubert if ((reqflags & (KDC_OPT_VALIDATE | KDC_OPT_RENEW)) && header_enc != NULL)
814*7f2fe78bSCy Schubert return header_enc->flags & ~TKT_FLG_INVALID;
815*7f2fe78bSCy Schubert
816*7f2fe78bSCy Schubert /* Indicate support for encrypted padata (RFC 6806), and set flags based on
817*7f2fe78bSCy Schubert * request options and the header ticket. */
818*7f2fe78bSCy Schubert flags = OPTS2FLAGS(reqflags) | TKT_FLG_ENC_PA_REP;
819*7f2fe78bSCy Schubert if (reqflags & KDC_OPT_POSTDATED)
820*7f2fe78bSCy Schubert flags |= TKT_FLG_INVALID;
821*7f2fe78bSCy Schubert if (header_enc != NULL)
822*7f2fe78bSCy Schubert flags |= COPY_TKT_FLAGS(header_enc->flags);
823*7f2fe78bSCy Schubert if (header_enc == NULL)
824*7f2fe78bSCy Schubert flags |= TKT_FLG_INITIAL;
825*7f2fe78bSCy Schubert
826*7f2fe78bSCy Schubert /* For TGS requests, indicate if the service is marked ok-as-delegate. */
827*7f2fe78bSCy Schubert if (header_enc != NULL && (server->attributes & KRB5_KDB_OK_AS_DELEGATE))
828*7f2fe78bSCy Schubert flags |= TKT_FLG_OK_AS_DELEGATE;
829*7f2fe78bSCy Schubert
830*7f2fe78bSCy Schubert /* Unset PROXIABLE if it is disallowed. */
831*7f2fe78bSCy Schubert if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_PROXIABLE))
832*7f2fe78bSCy Schubert flags &= ~TKT_FLG_PROXIABLE;
833*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_PROXIABLE)
834*7f2fe78bSCy Schubert flags &= ~TKT_FLG_PROXIABLE;
835*7f2fe78bSCy Schubert if (header_enc != NULL && !(header_enc->flags & TKT_FLG_PROXIABLE))
836*7f2fe78bSCy Schubert flags &= ~TKT_FLG_PROXIABLE;
837*7f2fe78bSCy Schubert
838*7f2fe78bSCy Schubert /* Unset FORWARDABLE if it is disallowed. */
839*7f2fe78bSCy Schubert if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE))
840*7f2fe78bSCy Schubert flags &= ~TKT_FLG_FORWARDABLE;
841*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_FORWARDABLE)
842*7f2fe78bSCy Schubert flags &= ~TKT_FLG_FORWARDABLE;
843*7f2fe78bSCy Schubert if (header_enc != NULL && !(header_enc->flags & TKT_FLG_FORWARDABLE))
844*7f2fe78bSCy Schubert flags &= ~TKT_FLG_FORWARDABLE;
845*7f2fe78bSCy Schubert
846*7f2fe78bSCy Schubert /* We don't currently handle issuing anonymous tickets based on
847*7f2fe78bSCy Schubert * non-anonymous ones. */
848*7f2fe78bSCy Schubert if (header_enc != NULL && !(header_enc->flags & TKT_FLG_ANONYMOUS))
849*7f2fe78bSCy Schubert flags &= ~TKT_FLG_ANONYMOUS;
850*7f2fe78bSCy Schubert
851*7f2fe78bSCy Schubert return flags;
852*7f2fe78bSCy Schubert }
853*7f2fe78bSCy Schubert
854*7f2fe78bSCy Schubert /* Return KRB5KDC_ERR_POLICY if indicators does not contain the required auth
855*7f2fe78bSCy Schubert * indicators for server, ENOMEM on allocation error, 0 otherwise. */
856*7f2fe78bSCy Schubert krb5_error_code
check_indicators(krb5_context context,krb5_db_entry * server,krb5_data * const * indicators)857*7f2fe78bSCy Schubert check_indicators(krb5_context context, krb5_db_entry *server,
858*7f2fe78bSCy Schubert krb5_data *const *indicators)
859*7f2fe78bSCy Schubert {
860*7f2fe78bSCy Schubert krb5_error_code ret;
861*7f2fe78bSCy Schubert char *str = NULL, *copy = NULL, *save, *ind;
862*7f2fe78bSCy Schubert
863*7f2fe78bSCy Schubert ret = krb5_dbe_get_string(context, server, KRB5_KDB_SK_REQUIRE_AUTH, &str);
864*7f2fe78bSCy Schubert if (ret || str == NULL)
865*7f2fe78bSCy Schubert goto cleanup;
866*7f2fe78bSCy Schubert copy = strdup(str);
867*7f2fe78bSCy Schubert if (copy == NULL) {
868*7f2fe78bSCy Schubert ret = ENOMEM;
869*7f2fe78bSCy Schubert goto cleanup;
870*7f2fe78bSCy Schubert }
871*7f2fe78bSCy Schubert
872*7f2fe78bSCy Schubert /* Look for any of the space-separated strings in indicators. */
873*7f2fe78bSCy Schubert ind = strtok_r(copy, " ", &save);
874*7f2fe78bSCy Schubert while (ind != NULL) {
875*7f2fe78bSCy Schubert if (authind_contains(indicators, ind))
876*7f2fe78bSCy Schubert goto cleanup;
877*7f2fe78bSCy Schubert ind = strtok_r(NULL, " ", &save);
878*7f2fe78bSCy Schubert }
879*7f2fe78bSCy Schubert
880*7f2fe78bSCy Schubert ret = KRB5KDC_ERR_POLICY;
881*7f2fe78bSCy Schubert k5_setmsg(context, ret,
882*7f2fe78bSCy Schubert _("Required auth indicators not present in ticket: %s"), str);
883*7f2fe78bSCy Schubert
884*7f2fe78bSCy Schubert cleanup:
885*7f2fe78bSCy Schubert krb5_dbe_free_string(context, str);
886*7f2fe78bSCy Schubert free(copy);
887*7f2fe78bSCy Schubert return ret;
888*7f2fe78bSCy Schubert }
889*7f2fe78bSCy Schubert
890*7f2fe78bSCy Schubert #define ASN1_ID_CLASS (0xc0)
891*7f2fe78bSCy Schubert #define ASN1_ID_TYPE (0x20)
892*7f2fe78bSCy Schubert #define ASN1_ID_TAG (0x1f)
893*7f2fe78bSCy Schubert #define ASN1_CLASS_UNIV (0)
894*7f2fe78bSCy Schubert #define ASN1_CLASS_APP (1)
895*7f2fe78bSCy Schubert #define ASN1_CLASS_CTX (2)
896*7f2fe78bSCy Schubert #define ASN1_CLASS_PRIV (3)
897*7f2fe78bSCy Schubert #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
898*7f2fe78bSCy Schubert #define asn1_id_primitive(x) (!asn1_id_constructed(x))
899*7f2fe78bSCy Schubert #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
900*7f2fe78bSCy Schubert #define asn1_id_tag(x) (x & ASN1_ID_TAG)
901*7f2fe78bSCy Schubert
902*7f2fe78bSCy Schubert /*
903*7f2fe78bSCy Schubert * asn1length - return encoded length of value.
904*7f2fe78bSCy Schubert *
905*7f2fe78bSCy Schubert * passed a pointer into the asn.1 stream, which is updated
906*7f2fe78bSCy Schubert * to point right after the length bits.
907*7f2fe78bSCy Schubert *
908*7f2fe78bSCy Schubert * returns -1 on failure.
909*7f2fe78bSCy Schubert */
910*7f2fe78bSCy Schubert static int
asn1length(unsigned char ** astream)911*7f2fe78bSCy Schubert asn1length(unsigned char **astream)
912*7f2fe78bSCy Schubert {
913*7f2fe78bSCy Schubert int length; /* resulting length */
914*7f2fe78bSCy Schubert int sublen; /* sublengths */
915*7f2fe78bSCy Schubert int blen; /* bytes of length */
916*7f2fe78bSCy Schubert unsigned char *p; /* substring searching */
917*7f2fe78bSCy Schubert
918*7f2fe78bSCy Schubert if (**astream & 0x80) {
919*7f2fe78bSCy Schubert blen = **astream & 0x7f;
920*7f2fe78bSCy Schubert if (blen > 3) {
921*7f2fe78bSCy Schubert return(-1);
922*7f2fe78bSCy Schubert }
923*7f2fe78bSCy Schubert for (++*astream, length = 0; blen; ++*astream, blen--) {
924*7f2fe78bSCy Schubert length = (length << 8) | **astream;
925*7f2fe78bSCy Schubert }
926*7f2fe78bSCy Schubert if (length == 0) {
927*7f2fe78bSCy Schubert /* indefinite length, figure out by hand */
928*7f2fe78bSCy Schubert p = *astream;
929*7f2fe78bSCy Schubert p++;
930*7f2fe78bSCy Schubert while (1) {
931*7f2fe78bSCy Schubert /* compute value length. */
932*7f2fe78bSCy Schubert if ((sublen = asn1length(&p)) < 0) {
933*7f2fe78bSCy Schubert return(-1);
934*7f2fe78bSCy Schubert }
935*7f2fe78bSCy Schubert p += sublen;
936*7f2fe78bSCy Schubert /* check for termination */
937*7f2fe78bSCy Schubert if ((!*p++) && (!*p)) {
938*7f2fe78bSCy Schubert p++;
939*7f2fe78bSCy Schubert break;
940*7f2fe78bSCy Schubert }
941*7f2fe78bSCy Schubert }
942*7f2fe78bSCy Schubert length = p - *astream;
943*7f2fe78bSCy Schubert }
944*7f2fe78bSCy Schubert } else {
945*7f2fe78bSCy Schubert length = **astream;
946*7f2fe78bSCy Schubert ++*astream;
947*7f2fe78bSCy Schubert }
948*7f2fe78bSCy Schubert return(length);
949*7f2fe78bSCy Schubert }
950*7f2fe78bSCy Schubert
951*7f2fe78bSCy Schubert /*
952*7f2fe78bSCy Schubert * fetch_asn1_field - return raw asn.1 stream of subfield.
953*7f2fe78bSCy Schubert *
954*7f2fe78bSCy Schubert * this routine is passed a context-dependent tag number and "level" and returns
955*7f2fe78bSCy Schubert * the size and length of the corresponding level subfield.
956*7f2fe78bSCy Schubert *
957*7f2fe78bSCy Schubert * levels and are numbered starting from 1.
958*7f2fe78bSCy Schubert *
959*7f2fe78bSCy Schubert * returns 0 on success, -1 otherwise.
960*7f2fe78bSCy Schubert */
961*7f2fe78bSCy Schubert int
fetch_asn1_field(unsigned char * astream,unsigned int level,unsigned int field,krb5_data * data)962*7f2fe78bSCy Schubert fetch_asn1_field(unsigned char *astream, unsigned int level,
963*7f2fe78bSCy Schubert unsigned int field, krb5_data *data)
964*7f2fe78bSCy Schubert {
965*7f2fe78bSCy Schubert unsigned char *estream; /* end of stream */
966*7f2fe78bSCy Schubert int classes; /* # classes seen so far this level */
967*7f2fe78bSCy Schubert unsigned int levels = 0; /* levels seen so far */
968*7f2fe78bSCy Schubert int lastlevel = 1000; /* last level seen */
969*7f2fe78bSCy Schubert int length; /* various lengths */
970*7f2fe78bSCy Schubert int tag; /* tag number */
971*7f2fe78bSCy Schubert unsigned char savelen; /* saved length of our field */
972*7f2fe78bSCy Schubert
973*7f2fe78bSCy Schubert classes = -1;
974*7f2fe78bSCy Schubert /* we assume that the first identifier/length will tell us
975*7f2fe78bSCy Schubert how long the entire stream is. */
976*7f2fe78bSCy Schubert astream++;
977*7f2fe78bSCy Schubert estream = astream;
978*7f2fe78bSCy Schubert if ((length = asn1length(&astream)) < 0) {
979*7f2fe78bSCy Schubert return(-1);
980*7f2fe78bSCy Schubert }
981*7f2fe78bSCy Schubert estream += length;
982*7f2fe78bSCy Schubert /* search down the stream, checking identifiers. we process identifiers
983*7f2fe78bSCy Schubert until we hit the "level" we want, and then process that level for our
984*7f2fe78bSCy Schubert subfield, always making sure we don't go off the end of the stream. */
985*7f2fe78bSCy Schubert while (astream < estream) {
986*7f2fe78bSCy Schubert if (!asn1_id_constructed(*astream)) {
987*7f2fe78bSCy Schubert return(-1);
988*7f2fe78bSCy Schubert }
989*7f2fe78bSCy Schubert if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
990*7f2fe78bSCy Schubert if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
991*7f2fe78bSCy Schubert levels++;
992*7f2fe78bSCy Schubert classes = -1;
993*7f2fe78bSCy Schubert }
994*7f2fe78bSCy Schubert lastlevel = tag;
995*7f2fe78bSCy Schubert if (levels == level) {
996*7f2fe78bSCy Schubert /* in our context-dependent class, is this the one we're looking for ? */
997*7f2fe78bSCy Schubert if (tag == (int)field) {
998*7f2fe78bSCy Schubert /* return length and data */
999*7f2fe78bSCy Schubert astream++;
1000*7f2fe78bSCy Schubert savelen = *astream;
1001*7f2fe78bSCy Schubert if ((length = asn1length(&astream)) < 0) {
1002*7f2fe78bSCy Schubert return(-1);
1003*7f2fe78bSCy Schubert }
1004*7f2fe78bSCy Schubert data->length = length;
1005*7f2fe78bSCy Schubert /* if the field length is indefinite, we will have to subtract two
1006*7f2fe78bSCy Schubert (terminating octets) from the length returned since we don't want
1007*7f2fe78bSCy Schubert to pass any info from the "wrapper" back. asn1length will always return
1008*7f2fe78bSCy Schubert the *total* length of the field, not just what's contained in it */
1009*7f2fe78bSCy Schubert if ((savelen & 0xff) == 0x80) {
1010*7f2fe78bSCy Schubert data->length -=2 ;
1011*7f2fe78bSCy Schubert }
1012*7f2fe78bSCy Schubert data->data = (char *)astream;
1013*7f2fe78bSCy Schubert return(0);
1014*7f2fe78bSCy Schubert } else if (tag <= classes) {
1015*7f2fe78bSCy Schubert /* we've seen this class before, something must be wrong */
1016*7f2fe78bSCy Schubert return(-1);
1017*7f2fe78bSCy Schubert } else {
1018*7f2fe78bSCy Schubert classes = tag;
1019*7f2fe78bSCy Schubert }
1020*7f2fe78bSCy Schubert }
1021*7f2fe78bSCy Schubert }
1022*7f2fe78bSCy Schubert /* if we're not on our level yet, process this value. otherwise skip over it */
1023*7f2fe78bSCy Schubert astream++;
1024*7f2fe78bSCy Schubert if ((length = asn1length(&astream)) < 0) {
1025*7f2fe78bSCy Schubert return(-1);
1026*7f2fe78bSCy Schubert }
1027*7f2fe78bSCy Schubert if (levels == level) {
1028*7f2fe78bSCy Schubert astream += length;
1029*7f2fe78bSCy Schubert }
1030*7f2fe78bSCy Schubert }
1031*7f2fe78bSCy Schubert return(-1);
1032*7f2fe78bSCy Schubert }
1033*7f2fe78bSCy Schubert
1034*7f2fe78bSCy Schubert /* Return true if we believe server can support enctype as a session key. */
1035*7f2fe78bSCy Schubert static krb5_boolean
dbentry_supports_enctype(krb5_context context,krb5_db_entry * server,krb5_enctype enctype)1036*7f2fe78bSCy Schubert dbentry_supports_enctype(krb5_context context, krb5_db_entry *server,
1037*7f2fe78bSCy Schubert krb5_enctype enctype)
1038*7f2fe78bSCy Schubert {
1039*7f2fe78bSCy Schubert krb5_error_code retval;
1040*7f2fe78bSCy Schubert krb5_key_data *datap;
1041*7f2fe78bSCy Schubert char *etypes_str = NULL;
1042*7f2fe78bSCy Schubert krb5_enctype default_enctypes[1] = { 0 };
1043*7f2fe78bSCy Schubert krb5_enctype *etypes = NULL;
1044*7f2fe78bSCy Schubert krb5_boolean in_list;
1045*7f2fe78bSCy Schubert
1046*7f2fe78bSCy Schubert /* Look up the supported session key enctypes list in the KDB. */
1047*7f2fe78bSCy Schubert retval = krb5_dbe_get_string(context, server, KRB5_KDB_SK_SESSION_ENCTYPES,
1048*7f2fe78bSCy Schubert &etypes_str);
1049*7f2fe78bSCy Schubert if (retval == 0 && etypes_str != NULL && *etypes_str != '\0') {
1050*7f2fe78bSCy Schubert /* Pass a fake profile key for tracing of unrecognized tokens. */
1051*7f2fe78bSCy Schubert retval = krb5int_parse_enctype_list(context, "KDB-session_etypes",
1052*7f2fe78bSCy Schubert etypes_str, default_enctypes,
1053*7f2fe78bSCy Schubert &etypes);
1054*7f2fe78bSCy Schubert if (retval == 0 && etypes != NULL && etypes[0]) {
1055*7f2fe78bSCy Schubert in_list = k5_etypes_contains(etypes, enctype);
1056*7f2fe78bSCy Schubert free(etypes_str);
1057*7f2fe78bSCy Schubert free(etypes);
1058*7f2fe78bSCy Schubert return in_list;
1059*7f2fe78bSCy Schubert }
1060*7f2fe78bSCy Schubert /* Fall through on error or empty list */
1061*7f2fe78bSCy Schubert }
1062*7f2fe78bSCy Schubert free(etypes_str);
1063*7f2fe78bSCy Schubert free(etypes);
1064*7f2fe78bSCy Schubert
1065*7f2fe78bSCy Schubert /* Assume every server without a session_enctypes attribute supports
1066*7f2fe78bSCy Schubert * aes256-cts-hmac-sha1-96. */
1067*7f2fe78bSCy Schubert if (enctype == ENCTYPE_AES256_CTS_HMAC_SHA1_96)
1068*7f2fe78bSCy Schubert return TRUE;
1069*7f2fe78bSCy Schubert /* Assume the server supports any enctype it has a long-term key for. */
1070*7f2fe78bSCy Schubert return !krb5_dbe_find_enctype(context, server, enctype, -1, 0, &datap);
1071*7f2fe78bSCy Schubert }
1072*7f2fe78bSCy Schubert
1073*7f2fe78bSCy Schubert /*
1074*7f2fe78bSCy Schubert * This function returns the keytype which should be selected for the
1075*7f2fe78bSCy Schubert * session key. It is based on the ordered list which the user
1076*7f2fe78bSCy Schubert * requested, and what the KDC and the application server can support.
1077*7f2fe78bSCy Schubert */
1078*7f2fe78bSCy Schubert krb5_enctype
select_session_keytype(krb5_context context,krb5_db_entry * server,int nktypes,krb5_enctype * ktype)1079*7f2fe78bSCy Schubert select_session_keytype(krb5_context context, krb5_db_entry *server,
1080*7f2fe78bSCy Schubert int nktypes, krb5_enctype *ktype)
1081*7f2fe78bSCy Schubert {
1082*7f2fe78bSCy Schubert int i;
1083*7f2fe78bSCy Schubert
1084*7f2fe78bSCy Schubert for (i = 0; i < nktypes; i++) {
1085*7f2fe78bSCy Schubert if (!krb5_c_valid_enctype(ktype[i]))
1086*7f2fe78bSCy Schubert continue;
1087*7f2fe78bSCy Schubert
1088*7f2fe78bSCy Schubert if (!krb5_is_permitted_enctype(context, ktype[i]))
1089*7f2fe78bSCy Schubert continue;
1090*7f2fe78bSCy Schubert
1091*7f2fe78bSCy Schubert /*
1092*7f2fe78bSCy Schubert * Prevent these deprecated enctypes from being used as session keys
1093*7f2fe78bSCy Schubert * unless they are explicitly allowed. In the future they will be more
1094*7f2fe78bSCy Schubert * comprehensively disabled and eventually removed.
1095*7f2fe78bSCy Schubert */
1096*7f2fe78bSCy Schubert if (ktype[i] == ENCTYPE_DES3_CBC_SHA1 && !context->allow_des3)
1097*7f2fe78bSCy Schubert continue;
1098*7f2fe78bSCy Schubert if (ktype[i] == ENCTYPE_ARCFOUR_HMAC && !context->allow_rc4)
1099*7f2fe78bSCy Schubert continue;
1100*7f2fe78bSCy Schubert
1101*7f2fe78bSCy Schubert if (dbentry_supports_enctype(context, server, ktype[i]))
1102*7f2fe78bSCy Schubert return ktype[i];
1103*7f2fe78bSCy Schubert }
1104*7f2fe78bSCy Schubert
1105*7f2fe78bSCy Schubert return 0;
1106*7f2fe78bSCy Schubert }
1107*7f2fe78bSCy Schubert
1108*7f2fe78bSCy Schubert /*
1109*7f2fe78bSCy Schubert * Limit strings to a "reasonable" length to prevent crowding out of
1110*7f2fe78bSCy Schubert * other useful information in the log entry
1111*7f2fe78bSCy Schubert */
1112*7f2fe78bSCy Schubert #define NAME_LENGTH_LIMIT 128
1113*7f2fe78bSCy Schubert
limit_string(char * name)1114*7f2fe78bSCy Schubert void limit_string(char *name)
1115*7f2fe78bSCy Schubert {
1116*7f2fe78bSCy Schubert int i;
1117*7f2fe78bSCy Schubert
1118*7f2fe78bSCy Schubert if (!name)
1119*7f2fe78bSCy Schubert return;
1120*7f2fe78bSCy Schubert
1121*7f2fe78bSCy Schubert if (strlen(name) < NAME_LENGTH_LIMIT)
1122*7f2fe78bSCy Schubert return;
1123*7f2fe78bSCy Schubert
1124*7f2fe78bSCy Schubert i = NAME_LENGTH_LIMIT-4;
1125*7f2fe78bSCy Schubert name[i++] = '.';
1126*7f2fe78bSCy Schubert name[i++] = '.';
1127*7f2fe78bSCy Schubert name[i++] = '.';
1128*7f2fe78bSCy Schubert name[i] = '\0';
1129*7f2fe78bSCy Schubert return;
1130*7f2fe78bSCy Schubert }
1131*7f2fe78bSCy Schubert
1132*7f2fe78bSCy Schubert /* Wrapper of krb5_enctype_to_name() to include the PKINIT types. */
1133*7f2fe78bSCy Schubert static krb5_error_code
enctype_name(krb5_enctype ktype,char * buf,size_t buflen)1134*7f2fe78bSCy Schubert enctype_name(krb5_enctype ktype, char *buf, size_t buflen)
1135*7f2fe78bSCy Schubert {
1136*7f2fe78bSCy Schubert const char *name, *prefix = "";
1137*7f2fe78bSCy Schubert size_t len;
1138*7f2fe78bSCy Schubert
1139*7f2fe78bSCy Schubert if (buflen == 0)
1140*7f2fe78bSCy Schubert return EINVAL;
1141*7f2fe78bSCy Schubert *buf = '\0'; /* ensure these are always valid C-strings */
1142*7f2fe78bSCy Schubert
1143*7f2fe78bSCy Schubert if (!krb5_c_valid_enctype(ktype))
1144*7f2fe78bSCy Schubert prefix = "UNSUPPORTED:";
1145*7f2fe78bSCy Schubert else if (krb5int_c_deprecated_enctype(ktype))
1146*7f2fe78bSCy Schubert prefix = "DEPRECATED:";
1147*7f2fe78bSCy Schubert len = strlcpy(buf, prefix, buflen);
1148*7f2fe78bSCy Schubert if (len >= buflen)
1149*7f2fe78bSCy Schubert return ENOMEM;
1150*7f2fe78bSCy Schubert buflen -= len;
1151*7f2fe78bSCy Schubert buf += len;
1152*7f2fe78bSCy Schubert
1153*7f2fe78bSCy Schubert /* rfc4556 recommends that clients wishing to indicate support for these
1154*7f2fe78bSCy Schubert * pkinit algorithms include them in the etype field of the AS-REQ. */
1155*7f2fe78bSCy Schubert if (ktype == ENCTYPE_DSA_SHA1_CMS)
1156*7f2fe78bSCy Schubert name = "id-dsa-with-sha1-CmsOID";
1157*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_MD5_RSA_CMS)
1158*7f2fe78bSCy Schubert name = "md5WithRSAEncryption-CmsOID";
1159*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_SHA1_RSA_CMS)
1160*7f2fe78bSCy Schubert name = "sha-1WithRSAEncryption-CmsOID";
1161*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_RC2_CBC_ENV)
1162*7f2fe78bSCy Schubert name = "rc2-cbc-EnvOID";
1163*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_RSA_ENV)
1164*7f2fe78bSCy Schubert name = "rsaEncryption-EnvOID";
1165*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_RSA_ES_OAEP_ENV)
1166*7f2fe78bSCy Schubert name = "id-RSAES-OAEP-EnvOID";
1167*7f2fe78bSCy Schubert else if (ktype == ENCTYPE_DES3_CBC_ENV)
1168*7f2fe78bSCy Schubert name = "des-ede3-cbc-EnvOID";
1169*7f2fe78bSCy Schubert else
1170*7f2fe78bSCy Schubert return krb5_enctype_to_name(ktype, FALSE, buf, buflen);
1171*7f2fe78bSCy Schubert
1172*7f2fe78bSCy Schubert if (strlcpy(buf, name, buflen) >= buflen)
1173*7f2fe78bSCy Schubert return ENOMEM;
1174*7f2fe78bSCy Schubert return 0;
1175*7f2fe78bSCy Schubert }
1176*7f2fe78bSCy Schubert
1177*7f2fe78bSCy Schubert char *
ktypes2str(krb5_enctype * ktype,int nktypes)1178*7f2fe78bSCy Schubert ktypes2str(krb5_enctype *ktype, int nktypes)
1179*7f2fe78bSCy Schubert {
1180*7f2fe78bSCy Schubert struct k5buf buf;
1181*7f2fe78bSCy Schubert int i;
1182*7f2fe78bSCy Schubert char name[64];
1183*7f2fe78bSCy Schubert
1184*7f2fe78bSCy Schubert if (nktypes < 0)
1185*7f2fe78bSCy Schubert return NULL;
1186*7f2fe78bSCy Schubert
1187*7f2fe78bSCy Schubert k5_buf_init_dynamic(&buf);
1188*7f2fe78bSCy Schubert k5_buf_add_fmt(&buf, "%d etypes {", nktypes);
1189*7f2fe78bSCy Schubert for (i = 0; i < nktypes; i++) {
1190*7f2fe78bSCy Schubert enctype_name(ktype[i], name, sizeof(name));
1191*7f2fe78bSCy Schubert k5_buf_add_fmt(&buf, "%s%s(%ld)", i ? ", " : "", name, (long)ktype[i]);
1192*7f2fe78bSCy Schubert }
1193*7f2fe78bSCy Schubert k5_buf_add(&buf, "}");
1194*7f2fe78bSCy Schubert return k5_buf_cstring(&buf);
1195*7f2fe78bSCy Schubert }
1196*7f2fe78bSCy Schubert
1197*7f2fe78bSCy Schubert char *
rep_etypes2str(krb5_kdc_rep * rep)1198*7f2fe78bSCy Schubert rep_etypes2str(krb5_kdc_rep *rep)
1199*7f2fe78bSCy Schubert {
1200*7f2fe78bSCy Schubert struct k5buf buf;
1201*7f2fe78bSCy Schubert char name[64];
1202*7f2fe78bSCy Schubert krb5_enctype etype;
1203*7f2fe78bSCy Schubert
1204*7f2fe78bSCy Schubert k5_buf_init_dynamic(&buf);
1205*7f2fe78bSCy Schubert k5_buf_add(&buf, "etypes {rep=");
1206*7f2fe78bSCy Schubert enctype_name(rep->enc_part.enctype, name, sizeof(name));
1207*7f2fe78bSCy Schubert k5_buf_add_fmt(&buf, "%s(%ld)", name, (long)rep->enc_part.enctype);
1208*7f2fe78bSCy Schubert
1209*7f2fe78bSCy Schubert if (rep->ticket != NULL) {
1210*7f2fe78bSCy Schubert etype = rep->ticket->enc_part.enctype;
1211*7f2fe78bSCy Schubert enctype_name(etype, name, sizeof(name));
1212*7f2fe78bSCy Schubert k5_buf_add_fmt(&buf, ", tkt=%s(%ld)", name, (long)etype);
1213*7f2fe78bSCy Schubert }
1214*7f2fe78bSCy Schubert
1215*7f2fe78bSCy Schubert if (rep->ticket != NULL && rep->ticket->enc_part2 != NULL &&
1216*7f2fe78bSCy Schubert rep->ticket->enc_part2->session != NULL) {
1217*7f2fe78bSCy Schubert etype = rep->ticket->enc_part2->session->enctype;
1218*7f2fe78bSCy Schubert enctype_name(etype, name, sizeof(name));
1219*7f2fe78bSCy Schubert k5_buf_add_fmt(&buf, ", ses=%s(%ld)", name, (long)etype);
1220*7f2fe78bSCy Schubert }
1221*7f2fe78bSCy Schubert
1222*7f2fe78bSCy Schubert k5_buf_add(&buf, "}");
1223*7f2fe78bSCy Schubert return k5_buf_cstring(&buf);
1224*7f2fe78bSCy Schubert }
1225*7f2fe78bSCy Schubert
1226*7f2fe78bSCy Schubert static krb5_error_code
verify_for_user_checksum(krb5_context context,krb5_keyblock * key,krb5_pa_for_user * req)1227*7f2fe78bSCy Schubert verify_for_user_checksum(krb5_context context,
1228*7f2fe78bSCy Schubert krb5_keyblock *key,
1229*7f2fe78bSCy Schubert krb5_pa_for_user *req)
1230*7f2fe78bSCy Schubert {
1231*7f2fe78bSCy Schubert krb5_error_code code;
1232*7f2fe78bSCy Schubert int i;
1233*7f2fe78bSCy Schubert krb5_int32 name_type;
1234*7f2fe78bSCy Schubert char *p;
1235*7f2fe78bSCy Schubert krb5_data data;
1236*7f2fe78bSCy Schubert krb5_boolean valid = FALSE;
1237*7f2fe78bSCy Schubert
1238*7f2fe78bSCy Schubert if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
1239*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_INAPP_CKSUM;
1240*7f2fe78bSCy Schubert }
1241*7f2fe78bSCy Schubert
1242*7f2fe78bSCy Schubert /*
1243*7f2fe78bSCy Schubert * Checksum is over name type and string components of
1244*7f2fe78bSCy Schubert * client principal name and auth_package.
1245*7f2fe78bSCy Schubert */
1246*7f2fe78bSCy Schubert data.length = 4;
1247*7f2fe78bSCy Schubert for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1248*7f2fe78bSCy Schubert data.length += krb5_princ_component(context, req->user, i)->length;
1249*7f2fe78bSCy Schubert }
1250*7f2fe78bSCy Schubert data.length += krb5_princ_realm(context, req->user)->length;
1251*7f2fe78bSCy Schubert data.length += req->auth_package.length;
1252*7f2fe78bSCy Schubert
1253*7f2fe78bSCy Schubert p = data.data = malloc(data.length);
1254*7f2fe78bSCy Schubert if (data.data == NULL) {
1255*7f2fe78bSCy Schubert return ENOMEM;
1256*7f2fe78bSCy Schubert }
1257*7f2fe78bSCy Schubert
1258*7f2fe78bSCy Schubert name_type = krb5_princ_type(context, req->user);
1259*7f2fe78bSCy Schubert p[0] = (name_type >> 0 ) & 0xFF;
1260*7f2fe78bSCy Schubert p[1] = (name_type >> 8 ) & 0xFF;
1261*7f2fe78bSCy Schubert p[2] = (name_type >> 16) & 0xFF;
1262*7f2fe78bSCy Schubert p[3] = (name_type >> 24) & 0xFF;
1263*7f2fe78bSCy Schubert p += 4;
1264*7f2fe78bSCy Schubert
1265*7f2fe78bSCy Schubert for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1266*7f2fe78bSCy Schubert if (krb5_princ_component(context, req->user, i)->length > 0) {
1267*7f2fe78bSCy Schubert memcpy(p, krb5_princ_component(context, req->user, i)->data,
1268*7f2fe78bSCy Schubert krb5_princ_component(context, req->user, i)->length);
1269*7f2fe78bSCy Schubert }
1270*7f2fe78bSCy Schubert p += krb5_princ_component(context, req->user, i)->length;
1271*7f2fe78bSCy Schubert }
1272*7f2fe78bSCy Schubert
1273*7f2fe78bSCy Schubert if (krb5_princ_realm(context, req->user)->length > 0) {
1274*7f2fe78bSCy Schubert memcpy(p, krb5_princ_realm(context, req->user)->data,
1275*7f2fe78bSCy Schubert krb5_princ_realm(context, req->user)->length);
1276*7f2fe78bSCy Schubert }
1277*7f2fe78bSCy Schubert p += krb5_princ_realm(context, req->user)->length;
1278*7f2fe78bSCy Schubert
1279*7f2fe78bSCy Schubert if (req->auth_package.length > 0)
1280*7f2fe78bSCy Schubert memcpy(p, req->auth_package.data, req->auth_package.length);
1281*7f2fe78bSCy Schubert p += req->auth_package.length;
1282*7f2fe78bSCy Schubert
1283*7f2fe78bSCy Schubert code = krb5_c_verify_checksum(context,
1284*7f2fe78bSCy Schubert key,
1285*7f2fe78bSCy Schubert KRB5_KEYUSAGE_APP_DATA_CKSUM,
1286*7f2fe78bSCy Schubert &data,
1287*7f2fe78bSCy Schubert &req->cksum,
1288*7f2fe78bSCy Schubert &valid);
1289*7f2fe78bSCy Schubert
1290*7f2fe78bSCy Schubert if (code == 0 && valid == FALSE)
1291*7f2fe78bSCy Schubert code = KRB5KRB_AP_ERR_MODIFIED;
1292*7f2fe78bSCy Schubert
1293*7f2fe78bSCy Schubert free(data.data);
1294*7f2fe78bSCy Schubert
1295*7f2fe78bSCy Schubert return code;
1296*7f2fe78bSCy Schubert }
1297*7f2fe78bSCy Schubert
1298*7f2fe78bSCy Schubert /*
1299*7f2fe78bSCy Schubert * Legacy protocol transition (Windows 2003 and above)
1300*7f2fe78bSCy Schubert */
1301*7f2fe78bSCy Schubert static krb5_error_code
kdc_process_for_user(krb5_context context,krb5_pa_data * pa_data,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,const char ** status)1302*7f2fe78bSCy Schubert kdc_process_for_user(krb5_context context, krb5_pa_data *pa_data,
1303*7f2fe78bSCy Schubert krb5_keyblock *tgs_session,
1304*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user **s4u_x509_user,
1305*7f2fe78bSCy Schubert const char **status)
1306*7f2fe78bSCy Schubert {
1307*7f2fe78bSCy Schubert krb5_error_code code;
1308*7f2fe78bSCy Schubert krb5_pa_for_user *for_user;
1309*7f2fe78bSCy Schubert krb5_data req_data;
1310*7f2fe78bSCy Schubert
1311*7f2fe78bSCy Schubert req_data.length = pa_data->length;
1312*7f2fe78bSCy Schubert req_data.data = (char *)pa_data->contents;
1313*7f2fe78bSCy Schubert
1314*7f2fe78bSCy Schubert code = decode_krb5_pa_for_user(&req_data, &for_user);
1315*7f2fe78bSCy Schubert if (code) {
1316*7f2fe78bSCy Schubert *status = "DECODE_PA_FOR_USER";
1317*7f2fe78bSCy Schubert return code;
1318*7f2fe78bSCy Schubert }
1319*7f2fe78bSCy Schubert
1320*7f2fe78bSCy Schubert code = verify_for_user_checksum(context, tgs_session, for_user);
1321*7f2fe78bSCy Schubert if (code) {
1322*7f2fe78bSCy Schubert *status = "INVALID_S4U2SELF_CHECKSUM";
1323*7f2fe78bSCy Schubert krb5_free_pa_for_user(context, for_user);
1324*7f2fe78bSCy Schubert return code;
1325*7f2fe78bSCy Schubert }
1326*7f2fe78bSCy Schubert
1327*7f2fe78bSCy Schubert *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user));
1328*7f2fe78bSCy Schubert if (*s4u_x509_user == NULL) {
1329*7f2fe78bSCy Schubert krb5_free_pa_for_user(context, for_user);
1330*7f2fe78bSCy Schubert return ENOMEM;
1331*7f2fe78bSCy Schubert }
1332*7f2fe78bSCy Schubert
1333*7f2fe78bSCy Schubert (*s4u_x509_user)->user_id.user = for_user->user;
1334*7f2fe78bSCy Schubert for_user->user = NULL;
1335*7f2fe78bSCy Schubert krb5_free_pa_for_user(context, for_user);
1336*7f2fe78bSCy Schubert
1337*7f2fe78bSCy Schubert return 0;
1338*7f2fe78bSCy Schubert }
1339*7f2fe78bSCy Schubert
1340*7f2fe78bSCy Schubert static krb5_error_code
verify_s4u_x509_user_checksum(krb5_context context,krb5_keyblock * key,krb5_data * req_data,krb5_int32 kdc_req_nonce,krb5_pa_s4u_x509_user * req)1341*7f2fe78bSCy Schubert verify_s4u_x509_user_checksum(krb5_context context,
1342*7f2fe78bSCy Schubert krb5_keyblock *key,
1343*7f2fe78bSCy Schubert krb5_data *req_data,
1344*7f2fe78bSCy Schubert krb5_int32 kdc_req_nonce,
1345*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user *req)
1346*7f2fe78bSCy Schubert {
1347*7f2fe78bSCy Schubert krb5_error_code code;
1348*7f2fe78bSCy Schubert krb5_data scratch;
1349*7f2fe78bSCy Schubert krb5_boolean valid = FALSE;
1350*7f2fe78bSCy Schubert
1351*7f2fe78bSCy Schubert if (enctype_requires_etype_info_2(key->enctype) &&
1352*7f2fe78bSCy Schubert !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
1353*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_INAPP_CKSUM;
1354*7f2fe78bSCy Schubert
1355*7f2fe78bSCy Schubert if (req->user_id.nonce != kdc_req_nonce)
1356*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_MODIFIED;
1357*7f2fe78bSCy Schubert
1358*7f2fe78bSCy Schubert /*
1359*7f2fe78bSCy Schubert * Verify checksum over the encoded userid. If that fails,
1360*7f2fe78bSCy Schubert * re-encode, and verify that. This is similar to the
1361*7f2fe78bSCy Schubert * behaviour in kdc_process_tgs_req().
1362*7f2fe78bSCy Schubert */
1363*7f2fe78bSCy Schubert if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
1364*7f2fe78bSCy Schubert return ASN1_PARSE_ERROR;
1365*7f2fe78bSCy Schubert
1366*7f2fe78bSCy Schubert code = krb5_c_verify_checksum(context,
1367*7f2fe78bSCy Schubert key,
1368*7f2fe78bSCy Schubert KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1369*7f2fe78bSCy Schubert &scratch,
1370*7f2fe78bSCy Schubert &req->cksum,
1371*7f2fe78bSCy Schubert &valid);
1372*7f2fe78bSCy Schubert if (code != 0)
1373*7f2fe78bSCy Schubert return code;
1374*7f2fe78bSCy Schubert
1375*7f2fe78bSCy Schubert if (valid == FALSE) {
1376*7f2fe78bSCy Schubert krb5_data *data;
1377*7f2fe78bSCy Schubert
1378*7f2fe78bSCy Schubert code = encode_krb5_s4u_userid(&req->user_id, &data);
1379*7f2fe78bSCy Schubert if (code != 0)
1380*7f2fe78bSCy Schubert return code;
1381*7f2fe78bSCy Schubert
1382*7f2fe78bSCy Schubert code = krb5_c_verify_checksum(context,
1383*7f2fe78bSCy Schubert key,
1384*7f2fe78bSCy Schubert KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1385*7f2fe78bSCy Schubert data,
1386*7f2fe78bSCy Schubert &req->cksum,
1387*7f2fe78bSCy Schubert &valid);
1388*7f2fe78bSCy Schubert
1389*7f2fe78bSCy Schubert krb5_free_data(context, data);
1390*7f2fe78bSCy Schubert
1391*7f2fe78bSCy Schubert if (code != 0)
1392*7f2fe78bSCy Schubert return code;
1393*7f2fe78bSCy Schubert }
1394*7f2fe78bSCy Schubert
1395*7f2fe78bSCy Schubert return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
1396*7f2fe78bSCy Schubert }
1397*7f2fe78bSCy Schubert
1398*7f2fe78bSCy Schubert /*
1399*7f2fe78bSCy Schubert * New protocol transition request (Windows 2008 and above)
1400*7f2fe78bSCy Schubert */
1401*7f2fe78bSCy Schubert static krb5_error_code
kdc_process_s4u_x509_user(krb5_context context,krb5_kdc_req * request,krb5_pa_data * pa_data,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,const char ** status)1402*7f2fe78bSCy Schubert kdc_process_s4u_x509_user(krb5_context context,
1403*7f2fe78bSCy Schubert krb5_kdc_req *request,
1404*7f2fe78bSCy Schubert krb5_pa_data *pa_data,
1405*7f2fe78bSCy Schubert krb5_keyblock *tgs_subkey,
1406*7f2fe78bSCy Schubert krb5_keyblock *tgs_session,
1407*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user **s4u_x509_user,
1408*7f2fe78bSCy Schubert const char **status)
1409*7f2fe78bSCy Schubert {
1410*7f2fe78bSCy Schubert krb5_error_code code;
1411*7f2fe78bSCy Schubert krb5_data req_data;
1412*7f2fe78bSCy Schubert
1413*7f2fe78bSCy Schubert req_data.length = pa_data->length;
1414*7f2fe78bSCy Schubert req_data.data = (char *)pa_data->contents;
1415*7f2fe78bSCy Schubert
1416*7f2fe78bSCy Schubert code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
1417*7f2fe78bSCy Schubert if (code) {
1418*7f2fe78bSCy Schubert *status = "DECODE_PA_S4U_X509_USER";
1419*7f2fe78bSCy Schubert return code;
1420*7f2fe78bSCy Schubert }
1421*7f2fe78bSCy Schubert
1422*7f2fe78bSCy Schubert code = verify_s4u_x509_user_checksum(context,
1423*7f2fe78bSCy Schubert tgs_subkey ? tgs_subkey :
1424*7f2fe78bSCy Schubert tgs_session,
1425*7f2fe78bSCy Schubert &req_data,
1426*7f2fe78bSCy Schubert request->nonce, *s4u_x509_user);
1427*7f2fe78bSCy Schubert
1428*7f2fe78bSCy Schubert if (code) {
1429*7f2fe78bSCy Schubert *status = "INVALID_S4U2SELF_CHECKSUM";
1430*7f2fe78bSCy Schubert krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1431*7f2fe78bSCy Schubert *s4u_x509_user = NULL;
1432*7f2fe78bSCy Schubert return code;
1433*7f2fe78bSCy Schubert }
1434*7f2fe78bSCy Schubert
1435*7f2fe78bSCy Schubert if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 &&
1436*7f2fe78bSCy Schubert (*s4u_x509_user)->user_id.subject_cert.length == 0) {
1437*7f2fe78bSCy Schubert *status = "INVALID_S4U2SELF_REQUEST";
1438*7f2fe78bSCy Schubert krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1439*7f2fe78bSCy Schubert *s4u_x509_user = NULL;
1440*7f2fe78bSCy Schubert return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1441*7f2fe78bSCy Schubert }
1442*7f2fe78bSCy Schubert
1443*7f2fe78bSCy Schubert return 0;
1444*7f2fe78bSCy Schubert }
1445*7f2fe78bSCy Schubert
1446*7f2fe78bSCy Schubert krb5_error_code
kdc_make_s4u2self_rep(krb5_context context,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user * req_s4u_user,krb5_kdc_rep * reply,krb5_enc_kdc_rep_part * reply_encpart)1447*7f2fe78bSCy Schubert kdc_make_s4u2self_rep(krb5_context context,
1448*7f2fe78bSCy Schubert krb5_keyblock *tgs_subkey,
1449*7f2fe78bSCy Schubert krb5_keyblock *tgs_session,
1450*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user *req_s4u_user,
1451*7f2fe78bSCy Schubert krb5_kdc_rep *reply,
1452*7f2fe78bSCy Schubert krb5_enc_kdc_rep_part *reply_encpart)
1453*7f2fe78bSCy Schubert {
1454*7f2fe78bSCy Schubert krb5_error_code code;
1455*7f2fe78bSCy Schubert krb5_data *der_user_id = NULL, *der_s4u_x509_user = NULL;
1456*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user rep_s4u_user;
1457*7f2fe78bSCy Schubert krb5_pa_data *pa = NULL;
1458*7f2fe78bSCy Schubert krb5_enctype enctype;
1459*7f2fe78bSCy Schubert krb5_keyusage usage;
1460*7f2fe78bSCy Schubert
1461*7f2fe78bSCy Schubert memset(&rep_s4u_user, 0, sizeof(rep_s4u_user));
1462*7f2fe78bSCy Schubert
1463*7f2fe78bSCy Schubert rep_s4u_user.user_id.nonce = req_s4u_user->user_id.nonce;
1464*7f2fe78bSCy Schubert rep_s4u_user.user_id.user = req_s4u_user->user_id.user;
1465*7f2fe78bSCy Schubert rep_s4u_user.user_id.options =
1466*7f2fe78bSCy Schubert req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
1467*7f2fe78bSCy Schubert
1468*7f2fe78bSCy Schubert code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &der_user_id);
1469*7f2fe78bSCy Schubert if (code != 0)
1470*7f2fe78bSCy Schubert goto cleanup;
1471*7f2fe78bSCy Schubert
1472*7f2fe78bSCy Schubert if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
1473*7f2fe78bSCy Schubert usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
1474*7f2fe78bSCy Schubert else
1475*7f2fe78bSCy Schubert usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
1476*7f2fe78bSCy Schubert
1477*7f2fe78bSCy Schubert code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
1478*7f2fe78bSCy Schubert tgs_subkey != NULL ? tgs_subkey : tgs_session,
1479*7f2fe78bSCy Schubert usage, der_user_id, &rep_s4u_user.cksum);
1480*7f2fe78bSCy Schubert if (code != 0)
1481*7f2fe78bSCy Schubert goto cleanup;
1482*7f2fe78bSCy Schubert
1483*7f2fe78bSCy Schubert code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &der_s4u_x509_user);
1484*7f2fe78bSCy Schubert if (code != 0)
1485*7f2fe78bSCy Schubert goto cleanup;
1486*7f2fe78bSCy Schubert
1487*7f2fe78bSCy Schubert code = k5_add_pa_data_from_data(&reply->padata, KRB5_PADATA_S4U_X509_USER,
1488*7f2fe78bSCy Schubert der_s4u_x509_user);
1489*7f2fe78bSCy Schubert if (code != 0)
1490*7f2fe78bSCy Schubert goto cleanup;
1491*7f2fe78bSCy Schubert
1492*7f2fe78bSCy Schubert if (tgs_subkey != NULL)
1493*7f2fe78bSCy Schubert enctype = tgs_subkey->enctype;
1494*7f2fe78bSCy Schubert else
1495*7f2fe78bSCy Schubert enctype = tgs_session->enctype;
1496*7f2fe78bSCy Schubert
1497*7f2fe78bSCy Schubert /*
1498*7f2fe78bSCy Schubert * Owing to a bug in Windows, unkeyed checksums were used for older
1499*7f2fe78bSCy Schubert * enctypes, including rc4-hmac. A forthcoming workaround for this
1500*7f2fe78bSCy Schubert * includes the checksum bytes in the encrypted padata.
1501*7f2fe78bSCy Schubert */
1502*7f2fe78bSCy Schubert if (enctype_requires_etype_info_2(enctype) == FALSE) {
1503*7f2fe78bSCy Schubert code = k5_alloc_pa_data(KRB5_PADATA_S4U_X509_USER,
1504*7f2fe78bSCy Schubert req_s4u_user->cksum.length +
1505*7f2fe78bSCy Schubert rep_s4u_user.cksum.length, &pa);
1506*7f2fe78bSCy Schubert if (code != 0)
1507*7f2fe78bSCy Schubert goto cleanup;
1508*7f2fe78bSCy Schubert memcpy(pa->contents,
1509*7f2fe78bSCy Schubert req_s4u_user->cksum.contents, req_s4u_user->cksum.length);
1510*7f2fe78bSCy Schubert memcpy(&pa->contents[req_s4u_user->cksum.length],
1511*7f2fe78bSCy Schubert rep_s4u_user.cksum.contents, rep_s4u_user.cksum.length);
1512*7f2fe78bSCy Schubert
1513*7f2fe78bSCy Schubert code = k5_add_pa_data_element(&reply_encpart->enc_padata, &pa);
1514*7f2fe78bSCy Schubert if (code != 0)
1515*7f2fe78bSCy Schubert goto cleanup;
1516*7f2fe78bSCy Schubert }
1517*7f2fe78bSCy Schubert
1518*7f2fe78bSCy Schubert cleanup:
1519*7f2fe78bSCy Schubert if (rep_s4u_user.cksum.contents != NULL)
1520*7f2fe78bSCy Schubert krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
1521*7f2fe78bSCy Schubert krb5_free_data(context, der_user_id);
1522*7f2fe78bSCy Schubert krb5_free_data(context, der_s4u_x509_user);
1523*7f2fe78bSCy Schubert k5_free_pa_data_element(pa);
1524*7f2fe78bSCy Schubert return code;
1525*7f2fe78bSCy Schubert }
1526*7f2fe78bSCy Schubert
1527*7f2fe78bSCy Schubert /* Return true if princ canonicalizes to the same principal as entry's. */
1528*7f2fe78bSCy Schubert krb5_boolean
is_client_db_alias(krb5_context context,const krb5_db_entry * entry,krb5_const_principal princ)1529*7f2fe78bSCy Schubert is_client_db_alias(krb5_context context, const krb5_db_entry *entry,
1530*7f2fe78bSCy Schubert krb5_const_principal princ)
1531*7f2fe78bSCy Schubert {
1532*7f2fe78bSCy Schubert krb5_error_code ret;
1533*7f2fe78bSCy Schubert krb5_db_entry *self;
1534*7f2fe78bSCy Schubert krb5_boolean is_self = FALSE;
1535*7f2fe78bSCy Schubert
1536*7f2fe78bSCy Schubert ret = krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_CLIENT, &self);
1537*7f2fe78bSCy Schubert if (!ret) {
1538*7f2fe78bSCy Schubert is_self = krb5_principal_compare(context, entry->princ, self->princ);
1539*7f2fe78bSCy Schubert krb5_db_free_principal(context, self);
1540*7f2fe78bSCy Schubert }
1541*7f2fe78bSCy Schubert
1542*7f2fe78bSCy Schubert return is_self;
1543*7f2fe78bSCy Schubert }
1544*7f2fe78bSCy Schubert
1545*7f2fe78bSCy Schubert /*
1546*7f2fe78bSCy Schubert * If S4U2Self padata is present in request, verify the checksum and set
1547*7f2fe78bSCy Schubert * *s4u_x509_user to the S4U2Self request. If the requested client realm is
1548*7f2fe78bSCy Schubert * local, look up the client and set *princ_ptr to its DB entry.
1549*7f2fe78bSCy Schubert */
1550*7f2fe78bSCy Schubert krb5_error_code
kdc_process_s4u2self_req(krb5_context context,krb5_kdc_req * request,const krb5_db_entry * server,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,krb5_db_entry ** princ_ptr,const char ** status)1551*7f2fe78bSCy Schubert kdc_process_s4u2self_req(krb5_context context, krb5_kdc_req *request,
1552*7f2fe78bSCy Schubert const krb5_db_entry *server,
1553*7f2fe78bSCy Schubert krb5_keyblock *tgs_subkey, krb5_keyblock *tgs_session,
1554*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user **s4u_x509_user,
1555*7f2fe78bSCy Schubert krb5_db_entry **princ_ptr, const char **status)
1556*7f2fe78bSCy Schubert {
1557*7f2fe78bSCy Schubert krb5_error_code code;
1558*7f2fe78bSCy Schubert krb5_pa_data *pa_data;
1559*7f2fe78bSCy Schubert krb5_db_entry *princ;
1560*7f2fe78bSCy Schubert krb5_s4u_userid *id;
1561*7f2fe78bSCy Schubert
1562*7f2fe78bSCy Schubert *princ_ptr = NULL;
1563*7f2fe78bSCy Schubert
1564*7f2fe78bSCy Schubert pa_data = krb5int_find_pa_data(context, request->padata,
1565*7f2fe78bSCy Schubert KRB5_PADATA_S4U_X509_USER);
1566*7f2fe78bSCy Schubert if (pa_data != NULL) {
1567*7f2fe78bSCy Schubert code = kdc_process_s4u_x509_user(context, request, pa_data, tgs_subkey,
1568*7f2fe78bSCy Schubert tgs_session, s4u_x509_user, status);
1569*7f2fe78bSCy Schubert if (code != 0)
1570*7f2fe78bSCy Schubert return code;
1571*7f2fe78bSCy Schubert } else {
1572*7f2fe78bSCy Schubert pa_data = krb5int_find_pa_data(context, request->padata,
1573*7f2fe78bSCy Schubert KRB5_PADATA_FOR_USER);
1574*7f2fe78bSCy Schubert if (pa_data != NULL) {
1575*7f2fe78bSCy Schubert code = kdc_process_for_user(context, pa_data, tgs_session,
1576*7f2fe78bSCy Schubert s4u_x509_user, status);
1577*7f2fe78bSCy Schubert if (code != 0)
1578*7f2fe78bSCy Schubert return code;
1579*7f2fe78bSCy Schubert } else
1580*7f2fe78bSCy Schubert return 0;
1581*7f2fe78bSCy Schubert }
1582*7f2fe78bSCy Schubert id = &(*s4u_x509_user)->user_id;
1583*7f2fe78bSCy Schubert
1584*7f2fe78bSCy Schubert if (data_eq(server->princ->realm, id->user->realm)) {
1585*7f2fe78bSCy Schubert if (id->subject_cert.length != 0) {
1586*7f2fe78bSCy Schubert code = krb5_db_get_s4u_x509_principal(context,
1587*7f2fe78bSCy Schubert &id->subject_cert, id->user,
1588*7f2fe78bSCy Schubert KRB5_KDB_FLAG_CLIENT,
1589*7f2fe78bSCy Schubert &princ);
1590*7f2fe78bSCy Schubert if (code == 0 && id->user->length == 0) {
1591*7f2fe78bSCy Schubert krb5_free_principal(context, id->user);
1592*7f2fe78bSCy Schubert code = krb5_copy_principal(context, princ->princ, &id->user);
1593*7f2fe78bSCy Schubert }
1594*7f2fe78bSCy Schubert } else {
1595*7f2fe78bSCy Schubert code = krb5_db_get_principal(context, id->user,
1596*7f2fe78bSCy Schubert KRB5_KDB_FLAG_CLIENT, &princ);
1597*7f2fe78bSCy Schubert }
1598*7f2fe78bSCy Schubert if (code == KRB5_KDB_NOENTRY) {
1599*7f2fe78bSCy Schubert *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
1600*7f2fe78bSCy Schubert return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1601*7f2fe78bSCy Schubert } else if (code) {
1602*7f2fe78bSCy Schubert *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
1603*7f2fe78bSCy Schubert return code; /* caller can free for_user */
1604*7f2fe78bSCy Schubert }
1605*7f2fe78bSCy Schubert
1606*7f2fe78bSCy Schubert /* Ignore password expiration and needchange attributes (as Windows
1607*7f2fe78bSCy Schubert * does), since S4U2Self is not password authentication. */
1608*7f2fe78bSCy Schubert princ->pw_expiration = 0;
1609*7f2fe78bSCy Schubert clear(princ->attributes, KRB5_KDB_REQUIRES_PWCHANGE);
1610*7f2fe78bSCy Schubert
1611*7f2fe78bSCy Schubert *princ_ptr = princ;
1612*7f2fe78bSCy Schubert }
1613*7f2fe78bSCy Schubert
1614*7f2fe78bSCy Schubert return 0;
1615*7f2fe78bSCy Schubert }
1616*7f2fe78bSCy Schubert
1617*7f2fe78bSCy Schubert /* Clear the forwardable flag in tkt if server cannot obtain forwardable
1618*7f2fe78bSCy Schubert * S4U2Self tickets according to [MS-SFU] 3.2.5.1.2. */
1619*7f2fe78bSCy Schubert krb5_error_code
s4u2self_forwardable(krb5_context context,krb5_db_entry * server,krb5_flags * tktflags)1620*7f2fe78bSCy Schubert s4u2self_forwardable(krb5_context context, krb5_db_entry *server,
1621*7f2fe78bSCy Schubert krb5_flags *tktflags)
1622*7f2fe78bSCy Schubert {
1623*7f2fe78bSCy Schubert krb5_error_code ret;
1624*7f2fe78bSCy Schubert
1625*7f2fe78bSCy Schubert /* Allow the forwardable flag if server has ok-to-auth-as-delegate set. */
1626*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)
1627*7f2fe78bSCy Schubert return 0;
1628*7f2fe78bSCy Schubert
1629*7f2fe78bSCy Schubert /* Deny the forwardable flag if server has any authorized delegation
1630*7f2fe78bSCy Schubert * targets for traditional S4U2Proxy. */
1631*7f2fe78bSCy Schubert ret = krb5_db_check_allowed_to_delegate(context, NULL, server, NULL);
1632*7f2fe78bSCy Schubert if (!ret)
1633*7f2fe78bSCy Schubert *tktflags &= ~TKT_FLG_FORWARDABLE;
1634*7f2fe78bSCy Schubert
1635*7f2fe78bSCy Schubert if (ret == KRB5KDC_ERR_BADOPTION || ret == KRB5_PLUGIN_OP_NOTSUPP)
1636*7f2fe78bSCy Schubert return 0;
1637*7f2fe78bSCy Schubert return ret;
1638*7f2fe78bSCy Schubert }
1639*7f2fe78bSCy Schubert
1640*7f2fe78bSCy Schubert krb5_error_code
kdc_check_transited_list(krb5_context context,const krb5_data * trans,const krb5_data * realm1,const krb5_data * realm2)1641*7f2fe78bSCy Schubert kdc_check_transited_list(krb5_context context, const krb5_data *trans,
1642*7f2fe78bSCy Schubert const krb5_data *realm1, const krb5_data *realm2)
1643*7f2fe78bSCy Schubert {
1644*7f2fe78bSCy Schubert krb5_error_code code;
1645*7f2fe78bSCy Schubert
1646*7f2fe78bSCy Schubert /* Check against the KDB module. Treat this answer as authoritative if the
1647*7f2fe78bSCy Schubert * method is supported and doesn't explicitly pass control. */
1648*7f2fe78bSCy Schubert code = krb5_db_check_transited_realms(context, trans, realm1, realm2);
1649*7f2fe78bSCy Schubert if (code != KRB5_PLUGIN_OP_NOTSUPP && code != KRB5_PLUGIN_NO_HANDLE)
1650*7f2fe78bSCy Schubert return code;
1651*7f2fe78bSCy Schubert
1652*7f2fe78bSCy Schubert /* Check using krb5.conf [capaths] or hierarchical relationships. */
1653*7f2fe78bSCy Schubert return krb5_check_transited_list(context, trans, realm1, realm2);
1654*7f2fe78bSCy Schubert }
1655*7f2fe78bSCy Schubert
1656*7f2fe78bSCy Schubert krb5_boolean
enctype_requires_etype_info_2(krb5_enctype enctype)1657*7f2fe78bSCy Schubert enctype_requires_etype_info_2(krb5_enctype enctype)
1658*7f2fe78bSCy Schubert {
1659*7f2fe78bSCy Schubert switch(enctype) {
1660*7f2fe78bSCy Schubert case ENCTYPE_DES3_CBC_SHA1:
1661*7f2fe78bSCy Schubert case ENCTYPE_DES3_CBC_RAW:
1662*7f2fe78bSCy Schubert case ENCTYPE_ARCFOUR_HMAC:
1663*7f2fe78bSCy Schubert case ENCTYPE_ARCFOUR_HMAC_EXP :
1664*7f2fe78bSCy Schubert return 0;
1665*7f2fe78bSCy Schubert default:
1666*7f2fe78bSCy Schubert return krb5_c_valid_enctype(enctype);
1667*7f2fe78bSCy Schubert }
1668*7f2fe78bSCy Schubert }
1669*7f2fe78bSCy Schubert
1670*7f2fe78bSCy Schubert void
kdc_get_ticket_endtime(kdc_realm_t * realm,krb5_timestamp starttime,krb5_timestamp endtime,krb5_timestamp till,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp * out_endtime)1671*7f2fe78bSCy Schubert kdc_get_ticket_endtime(kdc_realm_t *realm, krb5_timestamp starttime,
1672*7f2fe78bSCy Schubert krb5_timestamp endtime, krb5_timestamp till,
1673*7f2fe78bSCy Schubert krb5_db_entry *client, krb5_db_entry *server,
1674*7f2fe78bSCy Schubert krb5_timestamp *out_endtime)
1675*7f2fe78bSCy Schubert {
1676*7f2fe78bSCy Schubert krb5_timestamp until;
1677*7f2fe78bSCy Schubert krb5_deltat life;
1678*7f2fe78bSCy Schubert
1679*7f2fe78bSCy Schubert if (till == 0)
1680*7f2fe78bSCy Schubert till = kdc_infinity;
1681*7f2fe78bSCy Schubert
1682*7f2fe78bSCy Schubert until = ts_min(till, endtime);
1683*7f2fe78bSCy Schubert
1684*7f2fe78bSCy Schubert /* Determine the requested lifetime, capped at the maximum valid time
1685*7f2fe78bSCy Schubert * interval. */
1686*7f2fe78bSCy Schubert life = ts_delta(until, starttime);
1687*7f2fe78bSCy Schubert if (ts_after(until, starttime) && life < 0)
1688*7f2fe78bSCy Schubert life = INT32_MAX;
1689*7f2fe78bSCy Schubert
1690*7f2fe78bSCy Schubert if (client != NULL && client->max_life != 0)
1691*7f2fe78bSCy Schubert life = min(life, client->max_life);
1692*7f2fe78bSCy Schubert if (server->max_life != 0)
1693*7f2fe78bSCy Schubert life = min(life, server->max_life);
1694*7f2fe78bSCy Schubert if (realm->realm_maxlife != 0)
1695*7f2fe78bSCy Schubert life = min(life, realm->realm_maxlife);
1696*7f2fe78bSCy Schubert
1697*7f2fe78bSCy Schubert *out_endtime = ts_incr(starttime, life);
1698*7f2fe78bSCy Schubert }
1699*7f2fe78bSCy Schubert
1700*7f2fe78bSCy Schubert /*
1701*7f2fe78bSCy Schubert * Set times->renew_till to the requested renewable lifetime as modified by
1702*7f2fe78bSCy Schubert * policy. Set the TKT_FLG_RENEWABLE bit in *tktflags if we set a nonzero
1703*7f2fe78bSCy Schubert * renew_till. *times must be filled in except for renew_till. client and tgt
1704*7f2fe78bSCy Schubert * may be NULL.
1705*7f2fe78bSCy Schubert */
1706*7f2fe78bSCy Schubert void
kdc_get_ticket_renewtime(kdc_realm_t * realm,krb5_kdc_req * request,krb5_enc_tkt_part * tgt,krb5_db_entry * client,krb5_db_entry * server,krb5_flags * tktflags,krb5_ticket_times * times)1707*7f2fe78bSCy Schubert kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
1708*7f2fe78bSCy Schubert krb5_enc_tkt_part *tgt, krb5_db_entry *client,
1709*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_flags *tktflags,
1710*7f2fe78bSCy Schubert krb5_ticket_times *times)
1711*7f2fe78bSCy Schubert {
1712*7f2fe78bSCy Schubert krb5_timestamp rtime, max_rlife;
1713*7f2fe78bSCy Schubert
1714*7f2fe78bSCy Schubert *tktflags &= ~TKT_FLG_RENEWABLE;
1715*7f2fe78bSCy Schubert times->renew_till = 0;
1716*7f2fe78bSCy Schubert
1717*7f2fe78bSCy Schubert /* Don't issue renewable tickets if the client or server don't allow it,
1718*7f2fe78bSCy Schubert * or if this is a TGS request and the TGT isn't renewable. */
1719*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_RENEWABLE)
1720*7f2fe78bSCy Schubert return;
1721*7f2fe78bSCy Schubert if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_RENEWABLE))
1722*7f2fe78bSCy Schubert return;
1723*7f2fe78bSCy Schubert if (tgt != NULL && !(tgt->flags & TKT_FLG_RENEWABLE))
1724*7f2fe78bSCy Schubert return;
1725*7f2fe78bSCy Schubert
1726*7f2fe78bSCy Schubert /* Determine the requested renewable time. */
1727*7f2fe78bSCy Schubert if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE))
1728*7f2fe78bSCy Schubert rtime = request->rtime ? request->rtime : kdc_infinity;
1729*7f2fe78bSCy Schubert else if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
1730*7f2fe78bSCy Schubert ts_after(request->till, times->endtime))
1731*7f2fe78bSCy Schubert rtime = request->till;
1732*7f2fe78bSCy Schubert else
1733*7f2fe78bSCy Schubert return;
1734*7f2fe78bSCy Schubert
1735*7f2fe78bSCy Schubert /* Truncate it to the allowable renewable time. */
1736*7f2fe78bSCy Schubert if (tgt != NULL)
1737*7f2fe78bSCy Schubert rtime = ts_min(rtime, tgt->times.renew_till);
1738*7f2fe78bSCy Schubert max_rlife = min(server->max_renewable_life, realm->realm_maxrlife);
1739*7f2fe78bSCy Schubert if (client != NULL)
1740*7f2fe78bSCy Schubert max_rlife = min(max_rlife, client->max_renewable_life);
1741*7f2fe78bSCy Schubert rtime = ts_min(rtime, ts_incr(times->starttime, max_rlife));
1742*7f2fe78bSCy Schubert
1743*7f2fe78bSCy Schubert /* If the client only specified renewable-ok, don't issue a renewable
1744*7f2fe78bSCy Schubert * ticket unless the truncated renew time exceeds the ticket end time. */
1745*7f2fe78bSCy Schubert if (!isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1746*7f2fe78bSCy Schubert !ts_after(rtime, times->endtime))
1747*7f2fe78bSCy Schubert return;
1748*7f2fe78bSCy Schubert
1749*7f2fe78bSCy Schubert *tktflags |= TKT_FLG_RENEWABLE;
1750*7f2fe78bSCy Schubert times->renew_till = rtime;
1751*7f2fe78bSCy Schubert }
1752*7f2fe78bSCy Schubert
1753*7f2fe78bSCy Schubert /**
1754*7f2fe78bSCy Schubert * Handle protected negotiation of FAST using enc_padata
1755*7f2fe78bSCy Schubert * - If ENCPADATA_REQ_ENC_PA_REP is present, then:
1756*7f2fe78bSCy Schubert * - Return ENCPADATA_REQ_ENC_PA_REP with checksum of AS-REQ from client
1757*7f2fe78bSCy Schubert * - Include PADATA_FX_FAST in the enc_padata to indicate FAST
1758*7f2fe78bSCy Schubert * @pre @c out_enc_padata has space for at least two more padata
1759*7f2fe78bSCy Schubert * @param index in/out index into @c out_enc_padata for next item
1760*7f2fe78bSCy Schubert */
1761*7f2fe78bSCy Schubert krb5_error_code
kdc_handle_protected_negotiation(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,const krb5_keyblock * reply_key,krb5_pa_data *** out_enc_padata)1762*7f2fe78bSCy Schubert kdc_handle_protected_negotiation(krb5_context context,
1763*7f2fe78bSCy Schubert krb5_data *req_pkt, krb5_kdc_req *request,
1764*7f2fe78bSCy Schubert const krb5_keyblock *reply_key,
1765*7f2fe78bSCy Schubert krb5_pa_data ***out_enc_padata)
1766*7f2fe78bSCy Schubert {
1767*7f2fe78bSCy Schubert krb5_error_code retval = 0;
1768*7f2fe78bSCy Schubert krb5_checksum checksum;
1769*7f2fe78bSCy Schubert krb5_data *der_cksum = NULL;
1770*7f2fe78bSCy Schubert krb5_pa_data *pa_in;
1771*7f2fe78bSCy Schubert
1772*7f2fe78bSCy Schubert memset(&checksum, 0, sizeof(checksum));
1773*7f2fe78bSCy Schubert
1774*7f2fe78bSCy Schubert pa_in = krb5int_find_pa_data(context, request->padata,
1775*7f2fe78bSCy Schubert KRB5_ENCPADATA_REQ_ENC_PA_REP);
1776*7f2fe78bSCy Schubert if (pa_in == NULL)
1777*7f2fe78bSCy Schubert return 0;
1778*7f2fe78bSCy Schubert
1779*7f2fe78bSCy Schubert /* Compute and encode a checksum over the AS-REQ. */
1780*7f2fe78bSCy Schubert retval = krb5_c_make_checksum(context, 0, reply_key, KRB5_KEYUSAGE_AS_REQ,
1781*7f2fe78bSCy Schubert req_pkt, &checksum);
1782*7f2fe78bSCy Schubert if (retval != 0)
1783*7f2fe78bSCy Schubert goto cleanup;
1784*7f2fe78bSCy Schubert retval = encode_krb5_checksum(&checksum, &der_cksum);
1785*7f2fe78bSCy Schubert if (retval != 0)
1786*7f2fe78bSCy Schubert goto cleanup;
1787*7f2fe78bSCy Schubert
1788*7f2fe78bSCy Schubert retval = k5_add_pa_data_from_data(out_enc_padata,
1789*7f2fe78bSCy Schubert KRB5_ENCPADATA_REQ_ENC_PA_REP,
1790*7f2fe78bSCy Schubert der_cksum);
1791*7f2fe78bSCy Schubert if (retval)
1792*7f2fe78bSCy Schubert goto cleanup;
1793*7f2fe78bSCy Schubert
1794*7f2fe78bSCy Schubert /* Add a zero-length PA-FX-FAST element to the list. */
1795*7f2fe78bSCy Schubert retval = k5_add_empty_pa_data(out_enc_padata, KRB5_PADATA_FX_FAST);
1796*7f2fe78bSCy Schubert
1797*7f2fe78bSCy Schubert cleanup:
1798*7f2fe78bSCy Schubert krb5_free_checksum_contents(context, &checksum);
1799*7f2fe78bSCy Schubert krb5_free_data(context, der_cksum);
1800*7f2fe78bSCy Schubert return retval;
1801*7f2fe78bSCy Schubert }
1802*7f2fe78bSCy Schubert
1803*7f2fe78bSCy Schubert krb5_error_code
kdc_get_pa_pac_options(krb5_context context,krb5_pa_data ** in_padata,krb5_pa_pac_options ** pac_options_out)1804*7f2fe78bSCy Schubert kdc_get_pa_pac_options(krb5_context context, krb5_pa_data **in_padata,
1805*7f2fe78bSCy Schubert krb5_pa_pac_options **pac_options_out)
1806*7f2fe78bSCy Schubert {
1807*7f2fe78bSCy Schubert krb5_pa_data *pa;
1808*7f2fe78bSCy Schubert krb5_data der_pac_options;
1809*7f2fe78bSCy Schubert
1810*7f2fe78bSCy Schubert *pac_options_out = NULL;
1811*7f2fe78bSCy Schubert
1812*7f2fe78bSCy Schubert pa = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_PAC_OPTIONS);
1813*7f2fe78bSCy Schubert if (pa == NULL)
1814*7f2fe78bSCy Schubert return 0;
1815*7f2fe78bSCy Schubert
1816*7f2fe78bSCy Schubert der_pac_options = make_data(pa->contents, pa->length);
1817*7f2fe78bSCy Schubert return decode_krb5_pa_pac_options(&der_pac_options, pac_options_out);
1818*7f2fe78bSCy Schubert }
1819*7f2fe78bSCy Schubert
1820*7f2fe78bSCy Schubert krb5_error_code
kdc_add_pa_pac_options(krb5_context context,krb5_kdc_req * request,krb5_pa_data *** out_enc_padata)1821*7f2fe78bSCy Schubert kdc_add_pa_pac_options(krb5_context context, krb5_kdc_req *request,
1822*7f2fe78bSCy Schubert krb5_pa_data ***out_enc_padata)
1823*7f2fe78bSCy Schubert {
1824*7f2fe78bSCy Schubert krb5_error_code ret;
1825*7f2fe78bSCy Schubert krb5_pa_pac_options *pac_options = NULL;
1826*7f2fe78bSCy Schubert krb5_data *der_pac_options;
1827*7f2fe78bSCy Schubert
1828*7f2fe78bSCy Schubert ret = kdc_get_pa_pac_options(context, request->padata, &pac_options);
1829*7f2fe78bSCy Schubert if (ret || pac_options == NULL)
1830*7f2fe78bSCy Schubert return ret;
1831*7f2fe78bSCy Schubert
1832*7f2fe78bSCy Schubert /* Only return supported PAC options (currently only resource-based
1833*7f2fe78bSCy Schubert * constrained delegation support). */
1834*7f2fe78bSCy Schubert pac_options->options &= KRB5_PA_PAC_OPTIONS_RBCD;
1835*7f2fe78bSCy Schubert if (pac_options->options == 0) {
1836*7f2fe78bSCy Schubert free(pac_options);
1837*7f2fe78bSCy Schubert return 0;
1838*7f2fe78bSCy Schubert }
1839*7f2fe78bSCy Schubert
1840*7f2fe78bSCy Schubert ret = encode_krb5_pa_pac_options(pac_options, &der_pac_options);
1841*7f2fe78bSCy Schubert free(pac_options);
1842*7f2fe78bSCy Schubert if (ret)
1843*7f2fe78bSCy Schubert return ret;
1844*7f2fe78bSCy Schubert
1845*7f2fe78bSCy Schubert ret = k5_add_pa_data_from_data(out_enc_padata, KRB5_PADATA_PAC_OPTIONS,
1846*7f2fe78bSCy Schubert der_pac_options);
1847*7f2fe78bSCy Schubert krb5_free_data(context, der_pac_options);
1848*7f2fe78bSCy Schubert return ret;
1849*7f2fe78bSCy Schubert }
1850*7f2fe78bSCy Schubert
1851*7f2fe78bSCy Schubert krb5_error_code
kdc_get_pa_pac_rbcd(krb5_context context,krb5_pa_data ** in_padata,krb5_boolean * supported)1852*7f2fe78bSCy Schubert kdc_get_pa_pac_rbcd(krb5_context context, krb5_pa_data **in_padata,
1853*7f2fe78bSCy Schubert krb5_boolean *supported)
1854*7f2fe78bSCy Schubert {
1855*7f2fe78bSCy Schubert krb5_error_code retval;
1856*7f2fe78bSCy Schubert krb5_pa_pac_options *pac_options = NULL;
1857*7f2fe78bSCy Schubert
1858*7f2fe78bSCy Schubert *supported = FALSE;
1859*7f2fe78bSCy Schubert
1860*7f2fe78bSCy Schubert retval = kdc_get_pa_pac_options(context, in_padata, &pac_options);
1861*7f2fe78bSCy Schubert if (retval || !pac_options)
1862*7f2fe78bSCy Schubert return retval;
1863*7f2fe78bSCy Schubert
1864*7f2fe78bSCy Schubert if (pac_options->options & KRB5_PA_PAC_OPTIONS_RBCD)
1865*7f2fe78bSCy Schubert *supported = TRUE;
1866*7f2fe78bSCy Schubert
1867*7f2fe78bSCy Schubert free(pac_options);
1868*7f2fe78bSCy Schubert return 0;
1869*7f2fe78bSCy Schubert }
1870*7f2fe78bSCy Schubert
1871*7f2fe78bSCy Schubert /*
1872*7f2fe78bSCy Schubert * Although the KDC doesn't call this function directly,
1873*7f2fe78bSCy Schubert * process_tcp_connection_read() in net-server.c does call it.
1874*7f2fe78bSCy Schubert */
1875*7f2fe78bSCy Schubert krb5_error_code
make_toolong_error(void * handle,krb5_data ** out)1876*7f2fe78bSCy Schubert make_toolong_error (void *handle, krb5_data **out)
1877*7f2fe78bSCy Schubert {
1878*7f2fe78bSCy Schubert krb5_error errpkt;
1879*7f2fe78bSCy Schubert krb5_error_code retval;
1880*7f2fe78bSCy Schubert krb5_data *scratch;
1881*7f2fe78bSCy Schubert struct server_handle *h = handle;
1882*7f2fe78bSCy Schubert
1883*7f2fe78bSCy Schubert retval = krb5_us_timeofday(h->kdc_err_context,
1884*7f2fe78bSCy Schubert &errpkt.stime, &errpkt.susec);
1885*7f2fe78bSCy Schubert if (retval)
1886*7f2fe78bSCy Schubert return retval;
1887*7f2fe78bSCy Schubert errpkt.error = KRB_ERR_FIELD_TOOLONG;
1888*7f2fe78bSCy Schubert errpkt.server = h->kdc_realmlist[0]->realm_tgsprinc;
1889*7f2fe78bSCy Schubert errpkt.client = NULL;
1890*7f2fe78bSCy Schubert errpkt.cusec = 0;
1891*7f2fe78bSCy Schubert errpkt.ctime = 0;
1892*7f2fe78bSCy Schubert errpkt.text.length = 0;
1893*7f2fe78bSCy Schubert errpkt.text.data = 0;
1894*7f2fe78bSCy Schubert errpkt.e_data.length = 0;
1895*7f2fe78bSCy Schubert errpkt.e_data.data = 0;
1896*7f2fe78bSCy Schubert scratch = malloc(sizeof(*scratch));
1897*7f2fe78bSCy Schubert if (scratch == NULL)
1898*7f2fe78bSCy Schubert return ENOMEM;
1899*7f2fe78bSCy Schubert retval = krb5_mk_error(h->kdc_err_context, &errpkt, scratch);
1900*7f2fe78bSCy Schubert if (retval) {
1901*7f2fe78bSCy Schubert free(scratch);
1902*7f2fe78bSCy Schubert return retval;
1903*7f2fe78bSCy Schubert }
1904*7f2fe78bSCy Schubert
1905*7f2fe78bSCy Schubert *out = scratch;
1906*7f2fe78bSCy Schubert return 0;
1907*7f2fe78bSCy Schubert }
1908*7f2fe78bSCy Schubert
reset_for_hangup(void * ctx)1909*7f2fe78bSCy Schubert void reset_for_hangup(void *ctx)
1910*7f2fe78bSCy Schubert {
1911*7f2fe78bSCy Schubert int k;
1912*7f2fe78bSCy Schubert struct server_handle *h = ctx;
1913*7f2fe78bSCy Schubert
1914*7f2fe78bSCy Schubert for (k = 0; k < h->kdc_numrealms; k++)
1915*7f2fe78bSCy Schubert krb5_db_refresh_config(h->kdc_realmlist[k]->realm_context);
1916*7f2fe78bSCy Schubert }
1917