155682Smarkm/*
2233294Sstas * Copyright (c) 1997-2007 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kdc_locl.h"
3555682Smarkm
3655682Smarkm#define MAX_TIME ((time_t)((1U << 31) - 1))
3755682Smarkm
38178825Sdfrvoid
39178825Sdfr_kdc_fix_time(time_t **t)
4055682Smarkm{
4155682Smarkm    if(*t == NULL){
4255682Smarkm	ALLOC(*t);
4355682Smarkm	**t = MAX_TIME;
4455682Smarkm    }
4555682Smarkm    if(**t == 0) **t = MAX_TIME; /* fix for old clients */
4655682Smarkm}
4755682Smarkm
48178825Sdfrstatic int
49178825Sdfrrealloc_method_data(METHOD_DATA *md)
50178825Sdfr{
51178825Sdfr    PA_DATA *pa;
52178825Sdfr    pa = realloc(md->val, (md->len + 1) * sizeof(*md->val));
53178825Sdfr    if(pa == NULL)
54178825Sdfr	return ENOMEM;
55178825Sdfr    md->val = pa;
56178825Sdfr    md->len++;
57178825Sdfr    return 0;
58178825Sdfr}
59178825Sdfr
6055682Smarkmstatic void
61233294Sstasset_salt_padata(METHOD_DATA *md, Salt *salt)
6255682Smarkm{
6355682Smarkm    if (salt) {
64233294Sstas       realloc_method_data(md);
65233294Sstas       md->val[md->len - 1].padata_type = salt->type;
66233294Sstas       der_copy_octet_string(&salt->salt,
67233294Sstas                             &md->val[md->len - 1].padata_value);
6855682Smarkm    }
6955682Smarkm}
7055682Smarkm
71178825Sdfrconst PA_DATA*
72178825Sdfr_kdc_find_padata(const KDC_REQ *req, int *start, int type)
7355682Smarkm{
74178825Sdfr    if (req->padata == NULL)
75178825Sdfr	return NULL;
76178825Sdfr
77233294Sstas    while((size_t)*start < req->padata->len){
7855682Smarkm	(*start)++;
79233294Sstas	if(req->padata->val[*start - 1].padata_type == (unsigned)type)
8055682Smarkm	    return &req->padata->val[*start - 1];
8155682Smarkm    }
8255682Smarkm    return NULL;
8355682Smarkm}
8455682Smarkm
8572445Sassar/*
86233294Sstas * This is a hack to allow predefined weak services, like afs to
87233294Sstas * still use weak types
88233294Sstas */
89233294Sstas
90233294Sstaskrb5_boolean
91233294Sstas_kdc_is_weak_exception(krb5_principal principal, krb5_enctype etype)
92233294Sstas{
93233294Sstas    if (principal->name.name_string.len > 0 &&
94233294Sstas	strcmp(principal->name.name_string.val[0], "afs") == 0 &&
95233294Sstas	(etype == ETYPE_DES_CBC_CRC
96233294Sstas	 || etype == ETYPE_DES_CBC_MD4
97233294Sstas	 || etype == ETYPE_DES_CBC_MD5))
98233294Sstas	return TRUE;
99233294Sstas    return FALSE;
100233294Sstas}
101233294Sstas
102233294Sstas
103233294Sstas/*
104178825Sdfr * Detect if `key' is the using the the precomputed `default_salt'.
105178825Sdfr */
106178825Sdfr
107178825Sdfrstatic krb5_boolean
108178825Sdfris_default_salt_p(const krb5_salt *default_salt, const Key *key)
109178825Sdfr{
110178825Sdfr    if (key->salt == NULL)
111178825Sdfr	return TRUE;
112178825Sdfr    if (default_salt->salttype != key->salt->type)
113178825Sdfr	return FALSE;
114178825Sdfr    if (krb5_data_cmp(&default_salt->saltvalue, &key->salt->salt))
115178825Sdfr	return FALSE;
116178825Sdfr    return TRUE;
117178825Sdfr}
118178825Sdfr
119178825Sdfr/*
12072445Sassar * return the first appropriate key of `princ' in `ret_key'.  Look for
12172445Sassar * all the etypes in (`etypes', `len'), stopping as soon as we find
12272445Sassar * one, but preferring one that has default salt
12372445Sassar */
12455682Smarkm
125178825Sdfrkrb5_error_code
126233294Sstas_kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
127233294Sstas		krb5_boolean is_preauth, hdb_entry_ex *princ,
128233294Sstas		krb5_enctype *etypes, unsigned len,
129233294Sstas		krb5_enctype *ret_enctype, Key **ret_key)
13055682Smarkm{
131233294Sstas    krb5_error_code ret;
132233294Sstas    krb5_salt def_salt;
133233294Sstas    krb5_enctype enctype = ETYPE_NULL;
134233294Sstas    Key *key;
13555682Smarkm    int i;
13655682Smarkm
137233294Sstas    /* We'll want to avoid keys with v4 salted keys in the pre-auth case... */
138233294Sstas    ret = krb5_get_pw_salt(context, princ->entry.principal, &def_salt);
139233294Sstas    if (ret)
140233294Sstas	return ret;
141178825Sdfr
142233294Sstas    ret = KRB5KDC_ERR_ETYPE_NOSUPP;
14355682Smarkm
144233294Sstas    if (use_strongest_session_key) {
145233294Sstas	const krb5_enctype *p;
146233294Sstas	krb5_enctype clientbest = ETYPE_NULL;
147233294Sstas	int j;
148178825Sdfr
149233294Sstas	/*
150233294Sstas	 * Pick the strongest key that the KDC, target service, and
151233294Sstas	 * client all support, using the local cryptosystem enctype
152233294Sstas	 * list in strongest-to-weakest order to drive the search.
153233294Sstas	 *
154233294Sstas	 * This is not what RFC4120 says to do, but it encourages
155233294Sstas	 * adoption of stronger enctypes.  This doesn't play well with
156233294Sstas	 * clients that have multiple Kerberos client implementations
157233294Sstas	 * available with different supported enctype lists.
158233294Sstas	 */
159233294Sstas
160233294Sstas	/* drive the search with local supported enctypes list */
161233294Sstas	p = krb5_kerberos_enctypes(context);
162233294Sstas	for (i = 0; p[i] != ETYPE_NULL && enctype == ETYPE_NULL; i++) {
163233294Sstas	    if (krb5_enctype_valid(context, p[i]) != 0)
16472445Sassar		continue;
165233294Sstas
166233294Sstas	    /* check that the client supports it too */
167233294Sstas	    for (j = 0; j < len && enctype == ETYPE_NULL; j++) {
168233294Sstas		if (p[i] != etypes[j])
169233294Sstas		    continue;
170233294Sstas		/* save best of union of { client, crypto system } */
171233294Sstas		if (clientbest == ETYPE_NULL)
172233294Sstas		    clientbest = p[i];
173233294Sstas		/* check target princ support */
174233294Sstas		ret = hdb_enctype2key(context, &princ->entry, p[i], &key);
175233294Sstas		if (ret)
176233294Sstas		    continue;
177233294Sstas		if (is_preauth && !is_default_salt_p(&def_salt, key))
178233294Sstas		    continue;
179233294Sstas		enctype = p[i];
18055682Smarkm	    }
181233294Sstas	}
182233294Sstas	if (clientbest != ETYPE_NULL && enctype == ETYPE_NULL)
183233294Sstas	    enctype = clientbest;
184233294Sstas	else if (enctype == ETYPE_NULL)
185233294Sstas	    ret = KRB5KDC_ERR_ETYPE_NOSUPP;
186233294Sstas	if (ret == 0 && ret_enctype != NULL)
187233294Sstas	    *ret_enctype = enctype;
188233294Sstas	if (ret == 0 && ret_key != NULL)
189233294Sstas	    *ret_key = key;
190233294Sstas    } else {
191233294Sstas	/*
192233294Sstas	 * Pick the first key from the client's enctype list that is
193233294Sstas	 * supported by the cryptosystem and by the given principal.
194233294Sstas	 *
195233294Sstas	 * RFC4120 says we SHOULD pick the first _strong_ key from the
196233294Sstas	 * client's list... not the first key...  If the admin disallows
197233294Sstas	 * weak enctypes in krb5.conf and selects this key selection
198233294Sstas	 * algorithm, then we get exactly what RFC4120 says.
199233294Sstas	 */
200233294Sstas	for(key = NULL, i = 0; ret != 0 && i < len; i++, key = NULL) {
201233294Sstas
202233294Sstas	    if (krb5_enctype_valid(context, etypes[i]) != 0 &&
203233294Sstas		!_kdc_is_weak_exception(princ->entry.principal, etypes[i]))
204233294Sstas		continue;
205233294Sstas
206233294Sstas	    while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) {
207233294Sstas		if (key->key.keyvalue.length == 0) {
208233294Sstas		    ret = KRB5KDC_ERR_NULL_KEY;
209233294Sstas		    continue;
210233294Sstas		}
211233294Sstas		if (ret_key != NULL)
212233294Sstas		    *ret_key = key;
213233294Sstas		if (ret_enctype != NULL)
214233294Sstas		    *ret_enctype = etypes[i];
215233294Sstas		ret = 0;
216233294Sstas		if (is_preauth && is_default_salt_p(&def_salt, key))
217233294Sstas		    goto out;
218178825Sdfr	    }
21955682Smarkm	}
22055682Smarkm    }
221233294Sstas
222233294Sstasout:
223178825Sdfr    krb5_free_salt (context, def_salt);
22455682Smarkm    return ret;
22555682Smarkm}
22655682Smarkm
227178825Sdfrkrb5_error_code
228178825Sdfr_kdc_make_anonymous_principalname (PrincipalName *pn)
22955682Smarkm{
23072445Sassar    pn->name_type = KRB5_NT_PRINCIPAL;
23172445Sassar    pn->name_string.len = 1;
23272445Sassar    pn->name_string.val = malloc(sizeof(*pn->name_string.val));
23372445Sassar    if (pn->name_string.val == NULL)
23472445Sassar	return ENOMEM;
23572445Sassar    pn->name_string.val[0] = strdup("anonymous");
23672445Sassar    if (pn->name_string.val[0] == NULL) {
23772445Sassar	free(pn->name_string.val);
23872445Sassar	pn->name_string.val = NULL;
23972445Sassar	return ENOMEM;
24072445Sassar    }
24172445Sassar    return 0;
24272445Sassar}
24372445Sassar
244178825Sdfrvoid
245233294Sstas_kdc_log_timestamp(krb5_context context,
246178825Sdfr		   krb5_kdc_configuration *config,
247178825Sdfr		   const char *type,
248233294Sstas		   KerberosTime authtime, KerberosTime *starttime,
249178825Sdfr		   KerberosTime endtime, KerberosTime *renew_till)
25055682Smarkm{
251233294Sstas    char authtime_str[100], starttime_str[100],
252178825Sdfr	endtime_str[100], renewtime_str[100];
253233294Sstas
254233294Sstas    krb5_format_time(context, authtime,
255233294Sstas		     authtime_str, sizeof(authtime_str), TRUE);
256178825Sdfr    if (starttime)
257233294Sstas	krb5_format_time(context, *starttime,
258233294Sstas			 starttime_str, sizeof(starttime_str), TRUE);
259178825Sdfr    else
260178825Sdfr	strlcpy(starttime_str, "unset", sizeof(starttime_str));
261233294Sstas    krb5_format_time(context, endtime,
262233294Sstas		     endtime_str, sizeof(endtime_str), TRUE);
263178825Sdfr    if (renew_till)
264233294Sstas	krb5_format_time(context, *renew_till,
265233294Sstas			 renewtime_str, sizeof(renewtime_str), TRUE);
266178825Sdfr    else
267178825Sdfr	strlcpy(renewtime_str, "unset", sizeof(renewtime_str));
268233294Sstas
269178825Sdfr    kdc_log(context, config, 5,
270178825Sdfr	    "%s authtime: %s starttime: %s endtime: %s renew till: %s",
271178825Sdfr	    type, authtime_str, starttime_str, endtime_str, renewtime_str);
272178825Sdfr}
273178825Sdfr
274178825Sdfrstatic void
275233294Sstaslog_patypes(krb5_context context,
276178825Sdfr	    krb5_kdc_configuration *config,
277178825Sdfr	    METHOD_DATA *padata)
278178825Sdfr{
279178825Sdfr    struct rk_strpool *p = NULL;
280178825Sdfr    char *str;
281233294Sstas    size_t i;
282233294Sstas
283178825Sdfr    for (i = 0; i < padata->len; i++) {
284178825Sdfr	switch(padata->val[i].padata_type) {
285178825Sdfr	case KRB5_PADATA_PK_AS_REQ:
286178825Sdfr	    p = rk_strpoolprintf(p, "PK-INIT(ietf)");
287178825Sdfr	    break;
288178825Sdfr	case KRB5_PADATA_PK_AS_REQ_WIN:
289178825Sdfr	    p = rk_strpoolprintf(p, "PK-INIT(win2k)");
290178825Sdfr	    break;
291178825Sdfr	case KRB5_PADATA_PA_PK_OCSP_RESPONSE:
292178825Sdfr	    p = rk_strpoolprintf(p, "OCSP");
293178825Sdfr	    break;
294178825Sdfr	case KRB5_PADATA_ENC_TIMESTAMP:
295178825Sdfr	    p = rk_strpoolprintf(p, "encrypted-timestamp");
296178825Sdfr	    break;
297178825Sdfr	default:
298178825Sdfr	    p = rk_strpoolprintf(p, "%d", padata->val[i].padata_type);
299178825Sdfr	    break;
300178825Sdfr	}
301178825Sdfr	if (p && i + 1 < padata->len)
302178825Sdfr	    p = rk_strpoolprintf(p, ", ");
303178825Sdfr	if (p == NULL) {
304178825Sdfr	    kdc_log(context, config, 0, "out of memory");
305178825Sdfr	    return;
306178825Sdfr	}
307178825Sdfr    }
308178825Sdfr    if (p == NULL)
309178825Sdfr	p = rk_strpoolprintf(p, "none");
310233294Sstas
311178825Sdfr    str = rk_strpoolcollect(p);
312178825Sdfr    kdc_log(context, config, 0, "Client sent patypes: %s", str);
313178825Sdfr    free(str);
314178825Sdfr}
315178825Sdfr
316178825Sdfr/*
317178825Sdfr *
318178825Sdfr */
319178825Sdfr
320178825Sdfr
321178825Sdfrkrb5_error_code
322178825Sdfr_kdc_encode_reply(krb5_context context,
323178825Sdfr		  krb5_kdc_configuration *config,
324233294Sstas		  KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek,
325233294Sstas		  krb5_enctype etype,
326178825Sdfr		  int skvno, const EncryptionKey *skey,
327233294Sstas		  int ckvno, const EncryptionKey *reply_key,
328233294Sstas		  int rk_is_subkey,
329178825Sdfr		  const char **e_text,
330178825Sdfr		  krb5_data *reply)
331178825Sdfr{
332103423Snectar    unsigned char *buf;
333103423Snectar    size_t buf_size;
334233294Sstas    size_t len = 0;
33555682Smarkm    krb5_error_code ret;
33655682Smarkm    krb5_crypto crypto;
33755682Smarkm
338103423Snectar    ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret);
33955682Smarkm    if(ret) {
340233294Sstas	const char *msg = krb5_get_error_message(context, ret);
341233294Sstas	kdc_log(context, config, 0, "Failed to encode ticket: %s", msg);
342233294Sstas	krb5_free_error_message(context, msg);
34355682Smarkm	return ret;
34455682Smarkm    }
345103423Snectar    if(buf_size != len) {
346103423Snectar	free(buf);
347178825Sdfr	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
348103423Snectar	*e_text = "KDC internal error";
349103423Snectar	return KRB5KRB_ERR_GENERIC;
350103423Snectar    }
35155682Smarkm
35272445Sassar    ret = krb5_crypto_init(context, skey, etype, &crypto);
35372445Sassar    if (ret) {
354233294Sstas        const char *msg;
355103423Snectar	free(buf);
356233294Sstas	msg = krb5_get_error_message(context, ret);
357233294Sstas	kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
358233294Sstas	krb5_free_error_message(context, msg);
35972445Sassar	return ret;
36072445Sassar    }
36155682Smarkm
362233294Sstas    ret = krb5_encrypt_EncryptedData(context,
363103423Snectar				     crypto,
364103423Snectar				     KRB5_KU_TICKET,
365103423Snectar				     buf,
366103423Snectar				     len,
367103423Snectar				     skvno,
368103423Snectar				     &rep->ticket.enc_part);
369103423Snectar    free(buf);
37055682Smarkm    krb5_crypto_destroy(context, crypto);
371103423Snectar    if(ret) {
372233294Sstas	const char *msg = krb5_get_error_message(context, ret);
373233294Sstas	kdc_log(context, config, 0, "Failed to encrypt data: %s", msg);
374233294Sstas	krb5_free_error_message(context, msg);
375103423Snectar	return ret;
376103423Snectar    }
377233294Sstas
378178825Sdfr    if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep)
379103423Snectar	ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret);
38055682Smarkm    else
381103423Snectar	ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret);
38255682Smarkm    if(ret) {
383233294Sstas	const char *msg = krb5_get_error_message(context, ret);
384233294Sstas	kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg);
385233294Sstas	krb5_free_error_message(context, msg);
38655682Smarkm	return ret;
38755682Smarkm    }
388103423Snectar    if(buf_size != len) {
389103423Snectar	free(buf);
390178825Sdfr	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
391103423Snectar	*e_text = "KDC internal error";
392103423Snectar	return KRB5KRB_ERR_GENERIC;
393103423Snectar    }
394233294Sstas    ret = krb5_crypto_init(context, reply_key, 0, &crypto);
39572445Sassar    if (ret) {
396233294Sstas	const char *msg = krb5_get_error_message(context, ret);
397103423Snectar	free(buf);
398233294Sstas	kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
399233294Sstas	krb5_free_error_message(context, msg);
40072445Sassar	return ret;
40172445Sassar    }
40255682Smarkm    if(rep->msg_type == krb_as_rep) {
40355682Smarkm	krb5_encrypt_EncryptedData(context,
40455682Smarkm				   crypto,
40555682Smarkm				   KRB5_KU_AS_REP_ENC_PART,
406103423Snectar				   buf,
40755682Smarkm				   len,
40855682Smarkm				   ckvno,
40955682Smarkm				   &rep->enc_part);
410103423Snectar	free(buf);
411103423Snectar	ASN1_MALLOC_ENCODE(AS_REP, buf, buf_size, rep, &len, ret);
41255682Smarkm    } else {
41355682Smarkm	krb5_encrypt_EncryptedData(context,
41455682Smarkm				   crypto,
415233294Sstas				   rk_is_subkey ? KRB5_KU_TGS_REP_ENC_PART_SUB_KEY : KRB5_KU_TGS_REP_ENC_PART_SESSION,
416103423Snectar				   buf,
41755682Smarkm				   len,
41855682Smarkm				   ckvno,
41955682Smarkm				   &rep->enc_part);
420103423Snectar	free(buf);
421103423Snectar	ASN1_MALLOC_ENCODE(TGS_REP, buf, buf_size, rep, &len, ret);
42255682Smarkm    }
42355682Smarkm    krb5_crypto_destroy(context, crypto);
42455682Smarkm    if(ret) {
425233294Sstas	const char *msg = krb5_get_error_message(context, ret);
426233294Sstas	kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", msg);
427233294Sstas	krb5_free_error_message(context, msg);
42855682Smarkm	return ret;
42955682Smarkm    }
430103423Snectar    if(buf_size != len) {
431103423Snectar	free(buf);
432178825Sdfr	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
433103423Snectar	*e_text = "KDC internal error";
434103423Snectar	return KRB5KRB_ERR_GENERIC;
435103423Snectar    }
436103423Snectar    reply->data = buf;
437103423Snectar    reply->length = buf_size;
43855682Smarkm    return 0;
43955682Smarkm}
44055682Smarkm
441178825Sdfr/*
442178825Sdfr * Return 1 if the client have only older enctypes, this is for
443178825Sdfr * determining if the server should send ETYPE_INFO2 or not.
444178825Sdfr */
445178825Sdfr
44655682Smarkmstatic int
447178825Sdfrolder_enctype(krb5_enctype enctype)
44855682Smarkm{
449178825Sdfr    switch (enctype) {
450178825Sdfr    case ETYPE_DES_CBC_CRC:
451178825Sdfr    case ETYPE_DES_CBC_MD4:
452178825Sdfr    case ETYPE_DES_CBC_MD5:
453178825Sdfr    case ETYPE_DES3_CBC_SHA1:
454178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5:
455178825Sdfr    case ETYPE_ARCFOUR_HMAC_MD5_56:
456233294Sstas    /*
457178825Sdfr     * The following three is "old" windows enctypes and is needed for
458178825Sdfr     * windows 2000 hosts.
459178825Sdfr     */
460178825Sdfr    case ETYPE_ARCFOUR_MD4:
461178825Sdfr    case ETYPE_ARCFOUR_HMAC_OLD:
462178825Sdfr    case ETYPE_ARCFOUR_HMAC_OLD_EXP:
463178825Sdfr	return 1;
464178825Sdfr    default:
465178825Sdfr	return 0;
466178825Sdfr    }
46755682Smarkm}
46855682Smarkm
469178825Sdfr/*
470178825Sdfr *
471178825Sdfr */
472178825Sdfr
47355682Smarkmstatic krb5_error_code
474178825Sdfrmake_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
47555682Smarkm{
476102644Snectar    ent->etype = key->key.keytype;
477102644Snectar    if(key->salt){
478178825Sdfr#if 0
479102644Snectar	ALLOC(ent->salttype);
480178825Sdfr
481102644Snectar	if(key->salt->type == hdb_pw_salt)
482102644Snectar	    *ent->salttype = 0; /* or 1? or NULL? */
483102644Snectar	else if(key->salt->type == hdb_afs3_salt)
484102644Snectar	    *ent->salttype = 2;
485102644Snectar	else {
486233294Sstas	    kdc_log(context, config, 0, "unknown salt-type: %d",
487102644Snectar		    key->salt->type);
488102644Snectar	    return KRB5KRB_ERR_GENERIC;
489102644Snectar	}
490102644Snectar	/* according to `the specs', we can't send a salt if
491102644Snectar	   we have AFS3 salted key, but that requires that you
492102644Snectar	   *know* what cell you are using (e.g by assuming
493102644Snectar	   that the cell is the same as the realm in lower
494102644Snectar	   case) */
495178825Sdfr#elif 0
496178825Sdfr	ALLOC(ent->salttype);
497178825Sdfr	*ent->salttype = key->salt->type;
498102644Snectar#else
499233294Sstas	/*
500178825Sdfr	 * We shouldn't sent salttype since it is incompatible with the
501178825Sdfr	 * specification and it breaks windows clients.  The afs
502178825Sdfr	 * salting problem is solved by using KRB5-PADATA-AFS3-SALT
503178825Sdfr	 * implemented in Heimdal 0.7 and later.
504178825Sdfr	 */
505178825Sdfr	ent->salttype = NULL;
506102644Snectar#endif
507102644Snectar	krb5_copy_data(context, &key->salt->salt,
508102644Snectar		       &ent->salt);
509102644Snectar    } else {
510102644Snectar	/* we return no salt type at all, as that should indicate
511102644Snectar	 * the default salt type and make everybody happy.  some
512102644Snectar	 * systems (like w2k) dislike being told the salt type
513102644Snectar	 * here. */
514102644Snectar
515102644Snectar	ent->salttype = NULL;
516102644Snectar	ent->salt = NULL;
517102644Snectar    }
518102644Snectar    return 0;
519102644Snectar}
520102644Snectar
521102644Snectarstatic krb5_error_code
522233294Sstasget_pa_etype_info(krb5_context context,
523178825Sdfr		  krb5_kdc_configuration *config,
524233294Sstas		  METHOD_DATA *md, Key *ckey)
525102644Snectar{
52655682Smarkm    krb5_error_code ret = 0;
52755682Smarkm    ETYPE_INFO pa;
52855682Smarkm    unsigned char *buf;
52955682Smarkm    size_t len;
53055682Smarkm
531233294Sstas
532233294Sstas    pa.len = 1;
533233294Sstas    pa.val = calloc(1, sizeof(pa.val[0]));
53455682Smarkm    if(pa.val == NULL)
53555682Smarkm	return ENOMEM;
53672445Sassar
537233294Sstas    ret = make_etype_info_entry(context, &pa.val[0], ckey);
538233294Sstas    if (ret) {
539233294Sstas	free_ETYPE_INFO(&pa);
540233294Sstas	return ret;
54155682Smarkm    }
542102644Snectar
543103423Snectar    ASN1_MALLOC_ENCODE(ETYPE_INFO, buf, len, &pa, &len, ret);
54455682Smarkm    free_ETYPE_INFO(&pa);
545103423Snectar    if(ret)
54655682Smarkm	return ret;
54755682Smarkm    ret = realloc_method_data(md);
54855682Smarkm    if(ret) {
54955682Smarkm	free(buf);
55055682Smarkm	return ret;
55155682Smarkm    }
55272445Sassar    md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO;
55355682Smarkm    md->val[md->len - 1].padata_value.length = len;
55455682Smarkm    md->val[md->len - 1].padata_value.data = buf;
55555682Smarkm    return 0;
55655682Smarkm}
55755682Smarkm
55872445Sassar/*
559178825Sdfr *
560178825Sdfr */
561178825Sdfr
562178825Sdfrextern int _krb5_AES_string_to_default_iterator;
563178825Sdfr
564178825Sdfrstatic krb5_error_code
565178825Sdfrmake_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
566178825Sdfr{
567178825Sdfr    ent->etype = key->key.keytype;
568178825Sdfr    if(key->salt) {
569178825Sdfr	ALLOC(ent->salt);
570178825Sdfr	if (ent->salt == NULL)
571178825Sdfr	    return ENOMEM;
572178825Sdfr	*ent->salt = malloc(key->salt->salt.length + 1);
573178825Sdfr	if (*ent->salt == NULL) {
574178825Sdfr	    free(ent->salt);
575178825Sdfr	    ent->salt = NULL;
576178825Sdfr	    return ENOMEM;
577178825Sdfr	}
578178825Sdfr	memcpy(*ent->salt, key->salt->salt.data, key->salt->salt.length);
579178825Sdfr	(*ent->salt)[key->salt->salt.length] = '\0';
580178825Sdfr    } else
581178825Sdfr	ent->salt = NULL;
582178825Sdfr
583178825Sdfr    ent->s2kparams = NULL;
584178825Sdfr
585178825Sdfr    switch (key->key.keytype) {
586178825Sdfr    case ETYPE_AES128_CTS_HMAC_SHA1_96:
587178825Sdfr    case ETYPE_AES256_CTS_HMAC_SHA1_96:
588178825Sdfr	ALLOC(ent->s2kparams);
589178825Sdfr	if (ent->s2kparams == NULL)
590178825Sdfr	    return ENOMEM;
591178825Sdfr	ent->s2kparams->length = 4;
592178825Sdfr	ent->s2kparams->data = malloc(ent->s2kparams->length);
593178825Sdfr	if (ent->s2kparams->data == NULL) {
594178825Sdfr	    free(ent->s2kparams);
595178825Sdfr	    ent->s2kparams = NULL;
596178825Sdfr	    return ENOMEM;
597178825Sdfr	}
598233294Sstas	_krb5_put_int(ent->s2kparams->data,
599233294Sstas		      _krb5_AES_string_to_default_iterator,
600178825Sdfr		      ent->s2kparams->length);
601178825Sdfr	break;
602178825Sdfr    case ETYPE_DES_CBC_CRC:
603178825Sdfr    case ETYPE_DES_CBC_MD4:
604178825Sdfr    case ETYPE_DES_CBC_MD5:
605178825Sdfr	/* Check if this was a AFS3 salted key */
606178825Sdfr	if(key->salt && key->salt->type == hdb_afs3_salt){
607178825Sdfr	    ALLOC(ent->s2kparams);
608178825Sdfr	    if (ent->s2kparams == NULL)
609178825Sdfr		return ENOMEM;
610178825Sdfr	    ent->s2kparams->length = 1;
611178825Sdfr	    ent->s2kparams->data = malloc(ent->s2kparams->length);
612178825Sdfr	    if (ent->s2kparams->data == NULL) {
613178825Sdfr		free(ent->s2kparams);
614178825Sdfr		ent->s2kparams = NULL;
615178825Sdfr		return ENOMEM;
616178825Sdfr	    }
617233294Sstas	    _krb5_put_int(ent->s2kparams->data,
618178825Sdfr			  1,
619178825Sdfr			  ent->s2kparams->length);
620178825Sdfr	}
621178825Sdfr	break;
622178825Sdfr    default:
623178825Sdfr	break;
624178825Sdfr    }
625178825Sdfr    return 0;
626178825Sdfr}
627178825Sdfr
628178825Sdfr/*
629178825Sdfr * Return an ETYPE-INFO2. Enctypes are storted the same way as in the
630178825Sdfr * database (client supported enctypes first, then the unsupported
631178825Sdfr * enctypes).
632178825Sdfr */
633178825Sdfr
634178825Sdfrstatic krb5_error_code
635233294Sstasget_pa_etype_info2(krb5_context context,
636178825Sdfr		   krb5_kdc_configuration *config,
637233294Sstas		   METHOD_DATA *md, Key *ckey)
638178825Sdfr{
639178825Sdfr    krb5_error_code ret = 0;
640178825Sdfr    ETYPE_INFO2 pa;
641178825Sdfr    unsigned char *buf;
642178825Sdfr    size_t len;
643178825Sdfr
644233294Sstas    pa.len = 1;
645233294Sstas    pa.val = calloc(1, sizeof(pa.val[0]));
646178825Sdfr    if(pa.val == NULL)
647178825Sdfr	return ENOMEM;
648178825Sdfr
649233294Sstas    ret = make_etype_info2_entry(&pa.val[0], ckey);
650233294Sstas    if (ret) {
651233294Sstas	free_ETYPE_INFO2(&pa);
652233294Sstas	return ret;
653178825Sdfr    }
654178825Sdfr
655178825Sdfr    ASN1_MALLOC_ENCODE(ETYPE_INFO2, buf, len, &pa, &len, ret);
656178825Sdfr    free_ETYPE_INFO2(&pa);
657178825Sdfr    if(ret)
658178825Sdfr	return ret;
659178825Sdfr    ret = realloc_method_data(md);
660178825Sdfr    if(ret) {
661178825Sdfr	free(buf);
662178825Sdfr	return ret;
663178825Sdfr    }
664178825Sdfr    md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO2;
665178825Sdfr    md->val[md->len - 1].padata_value.length = len;
666178825Sdfr    md->val[md->len - 1].padata_value.data = buf;
667178825Sdfr    return 0;
668178825Sdfr}
669178825Sdfr
670178825Sdfr/*
671178825Sdfr *
672178825Sdfr */
673178825Sdfr
674178825Sdfrstatic void
675178825Sdfrlog_as_req(krb5_context context,
676178825Sdfr	   krb5_kdc_configuration *config,
677178825Sdfr	   krb5_enctype cetype,
678178825Sdfr	   krb5_enctype setype,
679178825Sdfr	   const KDC_REQ_BODY *b)
680178825Sdfr{
681178825Sdfr    krb5_error_code ret;
682233294Sstas    struct rk_strpool *p;
683178825Sdfr    char *str;
684233294Sstas    size_t i;
685233294Sstas
686233294Sstas    p = rk_strpoolprintf(NULL, "%s", "Client supported enctypes: ");
687233294Sstas
688178825Sdfr    for (i = 0; i < b->etype.len; i++) {
689178825Sdfr	ret = krb5_enctype_to_string(context, b->etype.val[i], &str);
690178825Sdfr	if (ret == 0) {
691178825Sdfr	    p = rk_strpoolprintf(p, "%s", str);
692178825Sdfr	    free(str);
693178825Sdfr	} else
694178825Sdfr	    p = rk_strpoolprintf(p, "%d", b->etype.val[i]);
695178825Sdfr	if (p && i + 1 < b->etype.len)
696178825Sdfr	    p = rk_strpoolprintf(p, ", ");
697178825Sdfr	if (p == NULL) {
698178825Sdfr	    kdc_log(context, config, 0, "out of memory");
699178825Sdfr	    return;
700178825Sdfr	}
701178825Sdfr    }
702178825Sdfr    if (p == NULL)
703178825Sdfr	p = rk_strpoolprintf(p, "no encryption types");
704178825Sdfr
705178825Sdfr    {
706178825Sdfr	char *cet;
707178825Sdfr	char *set;
708178825Sdfr
709178825Sdfr	ret = krb5_enctype_to_string(context, cetype, &cet);
710178825Sdfr	if(ret == 0) {
711178825Sdfr	    ret = krb5_enctype_to_string(context, setype, &set);
712178825Sdfr	    if (ret == 0) {
713233294Sstas		p = rk_strpoolprintf(p, ", using %s/%s", cet, set);
714178825Sdfr		free(set);
715178825Sdfr	    }
716178825Sdfr	    free(cet);
717178825Sdfr	}
718178825Sdfr	if (ret != 0)
719233294Sstas	    p = rk_strpoolprintf(p, ", using enctypes %d/%d",
720233294Sstas				 cetype, setype);
721178825Sdfr    }
722233294Sstas
723233294Sstas    str = rk_strpoolcollect(p);
724233294Sstas    kdc_log(context, config, 0, "%s", str);
725233294Sstas    free(str);
726233294Sstas
727178825Sdfr    {
728178825Sdfr	char fixedstr[128];
729233294Sstas	unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(),
730178825Sdfr		      fixedstr, sizeof(fixedstr));
731178825Sdfr	if(*fixedstr)
732233294Sstas	    kdc_log(context, config, 0, "Requested flags: %s", fixedstr);
733178825Sdfr    }
734178825Sdfr}
735178825Sdfr
736178825Sdfr/*
73772445Sassar * verify the flags on `client' and `server', returning 0
73872445Sassar * if they are OK and generating an error messages and returning
73972445Sassar * and error code otherwise.
74072445Sassar */
74172445Sassar
74272445Sassarkrb5_error_code
743233294Sstaskdc_check_flags(krb5_context context,
744233294Sstas		krb5_kdc_configuration *config,
745233294Sstas		hdb_entry_ex *client_ex, const char *client_name,
746233294Sstas		hdb_entry_ex *server_ex, const char *server_name,
747233294Sstas		krb5_boolean is_as_req)
74855682Smarkm{
749178825Sdfr    if(client_ex != NULL) {
750178825Sdfr	hdb_entry *client = &client_ex->entry;
751178825Sdfr
75255682Smarkm	/* check client */
753233294Sstas	if (client->flags.locked_out) {
754233294Sstas	    kdc_log(context, config, 0,
755233294Sstas		    "Client (%s) is locked out", client_name);
756233294Sstas	    return KRB5KDC_ERR_POLICY;
757233294Sstas	}
758233294Sstas
75955682Smarkm	if (client->flags.invalid) {
760233294Sstas	    kdc_log(context, config, 0,
761178825Sdfr		    "Client (%s) has invalid bit set", client_name);
76255682Smarkm	    return KRB5KDC_ERR_POLICY;
76355682Smarkm	}
764233294Sstas
76555682Smarkm	if(!client->flags.client){
766178825Sdfr	    kdc_log(context, config, 0,
767178825Sdfr		    "Principal may not act as client -- %s", client_name);
76855682Smarkm	    return KRB5KDC_ERR_POLICY;
76955682Smarkm	}
770233294Sstas
77155682Smarkm	if (client->valid_start && *client->valid_start > kdc_time) {
772178825Sdfr	    char starttime_str[100];
773233294Sstas	    krb5_format_time(context, *client->valid_start,
774233294Sstas			     starttime_str, sizeof(starttime_str), TRUE);
775178825Sdfr	    kdc_log(context, config, 0,
776233294Sstas		    "Client not yet valid until %s -- %s",
777178825Sdfr		    starttime_str, client_name);
77855682Smarkm	    return KRB5KDC_ERR_CLIENT_NOTYET;
77955682Smarkm	}
780233294Sstas
78155682Smarkm	if (client->valid_end && *client->valid_end < kdc_time) {
782178825Sdfr	    char endtime_str[100];
783233294Sstas	    krb5_format_time(context, *client->valid_end,
784233294Sstas			     endtime_str, sizeof(endtime_str), TRUE);
785178825Sdfr	    kdc_log(context, config, 0,
786178825Sdfr		    "Client expired at %s -- %s",
787178825Sdfr		    endtime_str, client_name);
78855682Smarkm	    return KRB5KDC_ERR_NAME_EXP;
78955682Smarkm	}
790233294Sstas
791233294Sstas	if (client->pw_end && *client->pw_end < kdc_time
792178825Sdfr	    && (server_ex == NULL || !server_ex->entry.flags.change_pw)) {
793178825Sdfr	    char pwend_str[100];
794233294Sstas	    krb5_format_time(context, *client->pw_end,
795233294Sstas			     pwend_str, sizeof(pwend_str), TRUE);
796178825Sdfr	    kdc_log(context, config, 0,
797233294Sstas		    "Client's key has expired at %s -- %s",
798178825Sdfr		    pwend_str, client_name);
79955682Smarkm	    return KRB5KDC_ERR_KEY_EXPIRED;
80055682Smarkm	}
80155682Smarkm    }
80255682Smarkm
80355682Smarkm    /* check server */
804233294Sstas
805178825Sdfr    if (server_ex != NULL) {
806178825Sdfr	hdb_entry *server = &server_ex->entry;
807178825Sdfr
808233294Sstas	if (server->flags.locked_out) {
809233294Sstas	    kdc_log(context, config, 0,
810233294Sstas		    "Client server locked out -- %s", server_name);
811233294Sstas	    return KRB5KDC_ERR_POLICY;
812233294Sstas	}
81355682Smarkm	if (server->flags.invalid) {
814178825Sdfr	    kdc_log(context, config, 0,
815178825Sdfr		    "Server has invalid flag set -- %s", server_name);
81655682Smarkm	    return KRB5KDC_ERR_POLICY;
81755682Smarkm	}
81855682Smarkm
81955682Smarkm	if(!server->flags.server){
820178825Sdfr	    kdc_log(context, config, 0,
821178825Sdfr		    "Principal may not act as server -- %s", server_name);
82255682Smarkm	    return KRB5KDC_ERR_POLICY;
82355682Smarkm	}
82455682Smarkm
82555682Smarkm	if(!is_as_req && server->flags.initial) {
826178825Sdfr	    kdc_log(context, config, 0,
827178825Sdfr		    "AS-REQ is required for server -- %s", server_name);
82855682Smarkm	    return KRB5KDC_ERR_POLICY;
82955682Smarkm	}
83055682Smarkm
83155682Smarkm	if (server->valid_start && *server->valid_start > kdc_time) {
832178825Sdfr	    char starttime_str[100];
833233294Sstas	    krb5_format_time(context, *server->valid_start,
834233294Sstas			     starttime_str, sizeof(starttime_str), TRUE);
835178825Sdfr	    kdc_log(context, config, 0,
836178825Sdfr		    "Server not yet valid until %s -- %s",
837178825Sdfr		    starttime_str, server_name);
83855682Smarkm	    return KRB5KDC_ERR_SERVICE_NOTYET;
83955682Smarkm	}
84055682Smarkm
84155682Smarkm	if (server->valid_end && *server->valid_end < kdc_time) {
842178825Sdfr	    char endtime_str[100];
843233294Sstas	    krb5_format_time(context, *server->valid_end,
844233294Sstas			     endtime_str, sizeof(endtime_str), TRUE);
845178825Sdfr	    kdc_log(context, config, 0,
846233294Sstas		    "Server expired at %s -- %s",
847178825Sdfr		    endtime_str, server_name);
84855682Smarkm	    return KRB5KDC_ERR_SERVICE_EXP;
84955682Smarkm	}
85055682Smarkm
85155682Smarkm	if (server->pw_end && *server->pw_end < kdc_time) {
852178825Sdfr	    char pwend_str[100];
853233294Sstas	    krb5_format_time(context, *server->pw_end,
854233294Sstas			     pwend_str, sizeof(pwend_str), TRUE);
855178825Sdfr	    kdc_log(context, config, 0,
856233294Sstas		    "Server's key has expired at -- %s",
857178825Sdfr		    pwend_str, server_name);
85855682Smarkm	    return KRB5KDC_ERR_KEY_EXPIRED;
85955682Smarkm	}
86055682Smarkm    }
86155682Smarkm    return 0;
86255682Smarkm}
86355682Smarkm
86472445Sassar/*
86572445Sassar * Return TRUE if `from' is part of `addresses' taking into consideration
86672445Sassar * the configuration variables that tells us how strict we should be about
86772445Sassar * these checks
86872445Sassar */
86972445Sassar
870178825Sdfrkrb5_boolean
871233294Sstas_kdc_check_addresses(krb5_context context,
872178825Sdfr		     krb5_kdc_configuration *config,
873178825Sdfr		     HostAddresses *addresses, const struct sockaddr *from)
87455682Smarkm{
87555682Smarkm    krb5_error_code ret;
87655682Smarkm    krb5_address addr;
87772445Sassar    krb5_boolean result;
878178825Sdfr    krb5_boolean only_netbios = TRUE;
879233294Sstas    size_t i;
880233294Sstas
881178825Sdfr    if(config->check_ticket_addresses == 0)
88255682Smarkm	return TRUE;
88355682Smarkm
88455682Smarkm    if(addresses == NULL)
885178825Sdfr	return config->allow_null_ticket_addresses;
886233294Sstas
887178825Sdfr    for (i = 0; i < addresses->len; ++i) {
888178825Sdfr	if (addresses->val[i].addr_type != KRB5_ADDRESS_NETBIOS) {
889178825Sdfr	    only_netbios = FALSE;
890178825Sdfr	}
891178825Sdfr    }
892178825Sdfr
893178825Sdfr    /* Windows sends it's netbios name, which I can only assume is
894178825Sdfr     * used for the 'allowed workstations' check.  This is painful,
895178825Sdfr     * but we still want to check IP addresses if they happen to be
896178825Sdfr     * present.
897178825Sdfr     */
898178825Sdfr
899178825Sdfr    if(only_netbios)
900178825Sdfr	return config->allow_null_ticket_addresses;
901178825Sdfr
90278527Sassar    ret = krb5_sockaddr2address (context, from, &addr);
90355682Smarkm    if(ret)
90455682Smarkm	return FALSE;
90555682Smarkm
90672445Sassar    result = krb5_address_search(context, &addr, addresses);
90772445Sassar    krb5_free_address (context, &addr);
90872445Sassar    return result;
90955682Smarkm}
91055682Smarkm
911178825Sdfr/*
912178825Sdfr *
913178825Sdfr */
914178825Sdfr
915178825Sdfrstatic krb5_boolean
916178825Sdfrsend_pac_p(krb5_context context, KDC_REQ *req)
917178825Sdfr{
918178825Sdfr    krb5_error_code ret;
919178825Sdfr    PA_PAC_REQUEST pacreq;
920178825Sdfr    const PA_DATA *pa;
921178825Sdfr    int i = 0;
922233294Sstas
923178825Sdfr    pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
924178825Sdfr    if (pa == NULL)
925178825Sdfr	return TRUE;
926178825Sdfr
927178825Sdfr    ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
928178825Sdfr				pa->padata_value.length,
929178825Sdfr				&pacreq,
930178825Sdfr				NULL);
931178825Sdfr    if (ret)
932178825Sdfr	return TRUE;
933178825Sdfr    i = pacreq.include_pac;
934178825Sdfr    free_PA_PAC_REQUEST(&pacreq);
935178825Sdfr    if (i == 0)
936178825Sdfr	return FALSE;
937178825Sdfr    return TRUE;
938178825Sdfr}
939178825Sdfr
940233294Sstaskrb5_boolean
941233294Sstas_kdc_is_anonymous(krb5_context context, krb5_principal principal)
942233294Sstas{
943233294Sstas    if (principal->name.name_type != KRB5_NT_WELLKNOWN ||
944233294Sstas	principal->name.name_string.len != 2 ||
945233294Sstas	strcmp(principal->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
946233294Sstas	strcmp(principal->name.name_string.val[1], KRB5_ANON_NAME) != 0)
947233294Sstas	return 0;
948233294Sstas    return 1;
949233294Sstas}
950233294Sstas
951178825Sdfr/*
952178825Sdfr *
953178825Sdfr */
954178825Sdfr
95555682Smarkmkrb5_error_code
956233294Sstas_kdc_as_rep(krb5_context context,
957178825Sdfr	    krb5_kdc_configuration *config,
958233294Sstas	    KDC_REQ *req,
959233294Sstas	    const krb5_data *req_buffer,
960178825Sdfr	    krb5_data *reply,
961178825Sdfr	    const char *from,
962178825Sdfr	    struct sockaddr *from_addr,
963178825Sdfr	    int datagram_reply)
96455682Smarkm{
96555682Smarkm    KDC_REQ_BODY *b = &req->req_body;
96655682Smarkm    AS_REP rep;
96755682Smarkm    KDCOptions f = b->kdc_options;
968178825Sdfr    hdb_entry_ex *client = NULL, *server = NULL;
969233294Sstas    HDB *clientdb;
970233294Sstas    krb5_enctype setype, sessionetype;
971178825Sdfr    krb5_data e_data;
97255682Smarkm    EncTicketPart et;
97355682Smarkm    EncKDCRepPart ek;
974127808Snectar    krb5_principal client_princ = NULL, server_princ = NULL;
975127808Snectar    char *client_name = NULL, *server_name = NULL;
97655682Smarkm    krb5_error_code ret = 0;
97755682Smarkm    const char *e_text = NULL;
97855682Smarkm    krb5_crypto crypto;
97955682Smarkm    Key *ckey, *skey;
980233294Sstas    EncryptionKey *reply_key = NULL, session_key;
981233294Sstas    int flags = HDB_F_FOR_AS_REQ;
982178825Sdfr#ifdef PKINIT
983178825Sdfr    pk_client_params *pkp = NULL;
984178825Sdfr#endif
98555682Smarkm
98672445Sassar    memset(&rep, 0, sizeof(rep));
987233294Sstas    memset(&session_key, 0, sizeof(session_key));
988178825Sdfr    krb5_data_zero(&e_data);
98972445Sassar
990233294Sstas    ALLOC(rep.padata);
991233294Sstas    rep.padata->len = 0;
992233294Sstas    rep.padata->val = NULL;
993233294Sstas
994178825Sdfr    if (f.canonicalize)
995178825Sdfr	flags |= HDB_F_CANON;
996178825Sdfr
99755682Smarkm    if(b->sname == NULL){
99855682Smarkm	ret = KRB5KRB_ERR_GENERIC;
99955682Smarkm	e_text = "No server in request";
100055682Smarkm    } else{
1001178825Sdfr	ret = _krb5_principalname2krb5_principal (context,
1002178825Sdfr						  &server_princ,
1003178825Sdfr						  *(b->sname),
1004178825Sdfr						  b->realm);
1005178825Sdfr	if (ret == 0)
1006178825Sdfr	    ret = krb5_unparse_name(context, server_princ, &server_name);
100755682Smarkm    }
1008127808Snectar    if (ret) {
1009233294Sstas	kdc_log(context, config, 0,
1010178825Sdfr		"AS-REQ malformed server name from %s", from);
1011127808Snectar	goto out;
1012127808Snectar    }
101355682Smarkm    if(b->cname == NULL){
101455682Smarkm	ret = KRB5KRB_ERR_GENERIC;
101555682Smarkm	e_text = "No client in request";
101655682Smarkm    } else {
1017233294Sstas	ret = _krb5_principalname2krb5_principal (context,
1018233294Sstas						  &client_princ,
1019233294Sstas						  *(b->cname),
1020233294Sstas						  b->realm);
1021233294Sstas	if (ret)
1022233294Sstas	    goto out;
1023178825Sdfr
1024178825Sdfr	ret = krb5_unparse_name(context, client_princ, &client_name);
102555682Smarkm    }
1026127808Snectar    if (ret) {
1027178825Sdfr	kdc_log(context, config, 0,
1028178825Sdfr		"AS-REQ malformed client name from %s", from);
102955682Smarkm	goto out;
1030127808Snectar    }
103155682Smarkm
1032233294Sstas    kdc_log(context, config, 0, "AS-REQ %s from %s for %s",
1033178825Sdfr	    client_name, from, server_name);
1034127808Snectar
1035233294Sstas    /*
1036233294Sstas     *
1037233294Sstas     */
1038233294Sstas
1039233294Sstas    if (_kdc_is_anonymous(context, client_princ)) {
1040233294Sstas	if (!b->kdc_options.request_anonymous) {
1041233294Sstas	    kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag");
1042233294Sstas	    ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1043233294Sstas	    goto out;
1044233294Sstas	}
1045233294Sstas    } else if (b->kdc_options.request_anonymous) {
1046233294Sstas	kdc_log(context, config, 0,
1047233294Sstas		"Request for a anonymous ticket with non "
1048233294Sstas		"anonymous client name: %s", client_name);
104955682Smarkm	ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
105055682Smarkm	goto out;
105155682Smarkm    }
105255682Smarkm
1053233294Sstas    /*
1054233294Sstas     *
1055233294Sstas     */
1056233294Sstas
1057233294Sstas    ret = _kdc_db_fetch(context, config, client_princ,
1058233294Sstas			HDB_F_GET_CLIENT | flags, NULL,
1059233294Sstas			&clientdb, &client);
1060233294Sstas    if(ret == HDB_ERR_NOT_FOUND_HERE) {
1061233294Sstas	kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name);
1062233294Sstas	goto out;
1063233294Sstas    } else if(ret){
1064233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1065233294Sstas	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg);
1066233294Sstas	krb5_free_error_message(context, msg);
1067233294Sstas	ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1068233294Sstas	goto out;
1069233294Sstas    }
1070178825Sdfr    ret = _kdc_db_fetch(context, config, server_princ,
1071233294Sstas			HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags,
1072233294Sstas			NULL, NULL, &server);
1073233294Sstas    if(ret == HDB_ERR_NOT_FOUND_HERE) {
1074233294Sstas	kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name);
1075233294Sstas	goto out;
1076233294Sstas    } else if(ret){
1077233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1078233294Sstas	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg);
1079233294Sstas	krb5_free_error_message(context, msg);
108055682Smarkm	ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
108155682Smarkm	goto out;
108255682Smarkm    }
108355682Smarkm
1084233294Sstas    memset(&et, 0, sizeof(et));
1085233294Sstas    memset(&ek, 0, sizeof(ek));
108655682Smarkm
1087233294Sstas    /*
1088233294Sstas     * Select a session enctype from the list of the crypto system
1089233294Sstas     * supported enctypes that is supported by the client and is one of
1090233294Sstas     * the enctype of the enctype of the service (likely krbtgt).
1091233294Sstas     *
1092233294Sstas     * The latter is used as a hint of what enctypes all KDC support,
1093233294Sstas     * to make sure a newer version of KDC won't generate a session
1094233294Sstas     * enctype that an older version of a KDC in the same realm can't
1095233294Sstas     * decrypt.
1096233294Sstas     */
1097234027Sstas    ret = _kdc_find_etype(context,
1098234027Sstas			  krb5_principal_is_krbtgt(context, server_princ) ?
1099234027Sstas			  config->tgt_use_strongest_session_key :
1100234027Sstas			  config->svc_use_strongest_session_key, FALSE,
1101233294Sstas			  client, b->etype.val, b->etype.len, &sessionetype,
1102233294Sstas			  NULL);
1103233294Sstas    if (ret) {
1104233294Sstas	kdc_log(context, config, 0,
1105233294Sstas		"Client (%s) from %s has no common enctypes with KDC "
1106233294Sstas		"to use for the session key",
1107233294Sstas		client_name, from);
1108178825Sdfr	goto out;
1109233294Sstas    }
1110233294Sstas    /*
1111233294Sstas     * But if the KDC admin is paranoid and doesn't want to have "not
1112233294Sstas     * the best" enctypes on the krbtgt, lets save the best pick from
1113233294Sstas     * the client list and hope that that will work for any other
1114233294Sstas     * KDCs.
1115233294Sstas     */
1116178825Sdfr
1117233294Sstas    /*
1118233294Sstas     * Pre-auth processing
1119233294Sstas     */
112055682Smarkm
112155682Smarkm    if(req->padata){
1122178825Sdfr	int i;
1123178825Sdfr	const PA_DATA *pa;
112455682Smarkm	int found_pa = 0;
1125178825Sdfr
1126178825Sdfr	log_patypes(context, config, req->padata);
1127178825Sdfr
1128178825Sdfr#ifdef PKINIT
1129233294Sstas	kdc_log(context, config, 5,
1130178825Sdfr		"Looking for PKINIT pa-data -- %s", client_name);
1131178825Sdfr
1132178825Sdfr	e_text = "No PKINIT PA found";
1133178825Sdfr
1134178825Sdfr	i = 0;
1135233294Sstas	pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ);
1136178825Sdfr	if (pa == NULL) {
1137178825Sdfr	    i = 0;
1138233294Sstas	    pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN);
1139178825Sdfr	}
1140178825Sdfr	if (pa) {
1141178825Sdfr	    char *client_cert = NULL;
1142178825Sdfr
1143233294Sstas	    ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp);
1144178825Sdfr	    if (ret) {
1145178825Sdfr		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1146233294Sstas		kdc_log(context, config, 5,
1147233294Sstas			"Failed to decode PKINIT PA-DATA -- %s",
1148178825Sdfr			client_name);
1149178825Sdfr		goto ts_enc;
1150178825Sdfr	    }
1151178825Sdfr	    if (ret == 0 && pkp == NULL)
1152178825Sdfr		goto ts_enc;
1153178825Sdfr
1154178825Sdfr	    ret = _kdc_pk_check_client(context,
1155178825Sdfr				       config,
1156233294Sstas				       clientdb,
1157178825Sdfr				       client,
1158178825Sdfr				       pkp,
1159178825Sdfr				       &client_cert);
1160178825Sdfr	    if (ret) {
1161178825Sdfr		e_text = "PKINIT certificate not allowed to "
1162178825Sdfr		    "impersonate principal";
1163178825Sdfr		_kdc_pk_free_client_param(context, pkp);
1164178825Sdfr
1165178825Sdfr		kdc_log(context, config, 0, "%s", e_text);
1166178825Sdfr		pkp = NULL;
1167178825Sdfr		goto out;
1168178825Sdfr	    }
1169233294Sstas
1170178825Sdfr	    found_pa = 1;
1171178825Sdfr	    et.flags.pre_authent = 1;
1172178825Sdfr	    kdc_log(context, config, 0,
1173233294Sstas		    "PKINIT pre-authentication succeeded -- %s using %s",
1174178825Sdfr		    client_name, client_cert);
1175178825Sdfr	    free(client_cert);
1176178825Sdfr	    if (pkp)
1177178825Sdfr		goto preauth_done;
1178178825Sdfr	}
1179178825Sdfr    ts_enc:
1180178825Sdfr#endif
1181233294Sstas	kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s",
1182178825Sdfr		client_name);
1183178825Sdfr
1184178825Sdfr	i = 0;
1185178825Sdfr	e_text = "No ENC-TS found";
1186178825Sdfr	while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
118755682Smarkm	    krb5_data ts_data;
118855682Smarkm	    PA_ENC_TS_ENC p;
118955682Smarkm	    size_t len;
119055682Smarkm	    EncryptedData enc_data;
119155682Smarkm	    Key *pa_key;
1192178825Sdfr	    char *str;
1193233294Sstas
119455682Smarkm	    found_pa = 1;
1195233294Sstas
1196233294Sstas	    if (b->kdc_options.request_anonymous) {
1197233294Sstas		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1198233294Sstas		kdc_log(context, config, 0, "ENC-TS doesn't support anon");
1199233294Sstas		goto out;
1200233294Sstas	    }
1201233294Sstas
120255682Smarkm	    ret = decode_EncryptedData(pa->padata_value.data,
120355682Smarkm				       pa->padata_value.length,
120455682Smarkm				       &enc_data,
120555682Smarkm				       &len);
120655682Smarkm	    if (ret) {
120755682Smarkm		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1208233294Sstas		kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s",
120955682Smarkm			client_name);
121055682Smarkm		goto out;
121155682Smarkm	    }
1212233294Sstas
1213233294Sstas	    ret = hdb_enctype2key(context, &client->entry,
1214178825Sdfr				  enc_data.etype, &pa_key);
121555682Smarkm	    if(ret){
121655682Smarkm		char *estr;
121755682Smarkm		e_text = "No key matches pa-data";
1218178825Sdfr		ret = KRB5KDC_ERR_ETYPE_NOSUPP;
121955682Smarkm		if(krb5_enctype_to_string(context, enc_data.etype, &estr))
122055682Smarkm		    estr = NULL;
122155682Smarkm		if(estr == NULL)
1222233294Sstas		    kdc_log(context, config, 5,
1223233294Sstas			    "No client key matching pa-data (%d) -- %s",
122455682Smarkm			    enc_data.etype, client_name);
122555682Smarkm		else
1226178825Sdfr		    kdc_log(context, config, 5,
1227233294Sstas			    "No client key matching pa-data (%s) -- %s",
122855682Smarkm			    estr, client_name);
122955682Smarkm		free(estr);
123055682Smarkm		free_EncryptedData(&enc_data);
1231233294Sstas
123255682Smarkm		continue;
123355682Smarkm	    }
1234102644Snectar
1235178825Sdfr	try_next_key:
123672445Sassar	    ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto);
123772445Sassar	    if (ret) {
1238233294Sstas		const char *msg = krb5_get_error_message(context, ret);
1239233294Sstas		kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1240233294Sstas		krb5_free_error_message(context, msg);
124172445Sassar		free_EncryptedData(&enc_data);
124272445Sassar		continue;
124372445Sassar	    }
124472445Sassar
124555682Smarkm	    ret = krb5_decrypt_EncryptedData (context,
124655682Smarkm					      crypto,
124755682Smarkm					      KRB5_KU_PA_ENC_TIMESTAMP,
124855682Smarkm					      &enc_data,
124955682Smarkm					      &ts_data);
125055682Smarkm	    krb5_crypto_destroy(context, crypto);
1251233294Sstas	    /*
1252233294Sstas	     * Since the user might have several keys with the same
1253233294Sstas	     * enctype but with diffrent salting, we need to try all
1254233294Sstas	     * the keys with the same enctype.
1255233294Sstas	     */
125655682Smarkm	    if(ret){
1257178825Sdfr		krb5_error_code ret2;
1258233294Sstas		const char *msg = krb5_get_error_message(context, ret);
1259233294Sstas
1260233294Sstas		ret2 = krb5_enctype_to_string(context,
1261178825Sdfr					      pa_key->key.keytype, &str);
1262178825Sdfr		if (ret2)
1263178825Sdfr		    str = NULL;
1264233294Sstas		kdc_log(context, config, 5,
1265178825Sdfr			"Failed to decrypt PA-DATA -- %s "
1266178825Sdfr			"(enctype %s) error %s",
1267233294Sstas			client_name, str ? str : "unknown enctype", msg);
1268233294Sstas		krb5_free_error_message(context, msg);
1269178825Sdfr		free(str);
1270178825Sdfr
1271233294Sstas		if(hdb_next_enctype2key(context, &client->entry,
1272102644Snectar					enc_data.etype, &pa_key) == 0)
1273102644Snectar		    goto try_next_key;
1274178825Sdfr		e_text = "Failed to decrypt PA-DATA";
1275178825Sdfr
1276102644Snectar		free_EncryptedData(&enc_data);
1277233294Sstas
1278233294Sstas		if (clientdb->hdb_auth_status)
1279233294Sstas		    (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD);
1280233294Sstas
1281178825Sdfr		ret = KRB5KDC_ERR_PREAUTH_FAILED;
128255682Smarkm		continue;
128355682Smarkm	    }
1284102644Snectar	    free_EncryptedData(&enc_data);
128555682Smarkm	    ret = decode_PA_ENC_TS_ENC(ts_data.data,
128655682Smarkm				       ts_data.length,
128755682Smarkm				       &p,
128855682Smarkm				       &len);
128955682Smarkm	    krb5_data_free(&ts_data);
129055682Smarkm	    if(ret){
129155682Smarkm		e_text = "Failed to decode PA-ENC-TS-ENC";
1292178825Sdfr		ret = KRB5KDC_ERR_PREAUTH_FAILED;
1293233294Sstas		kdc_log(context, config,
1294178825Sdfr			5, "Failed to decode PA-ENC-TS_ENC -- %s",
1295178825Sdfr			client_name);
129655682Smarkm		continue;
129755682Smarkm	    }
129855682Smarkm	    free_PA_ENC_TS_ENC(&p);
129955682Smarkm	    if (abs(kdc_time - p.patimestamp) > context->max_skew) {
1300178825Sdfr		char client_time[100];
1301178825Sdfr
1302233294Sstas		krb5_format_time(context, p.patimestamp,
1303233294Sstas				 client_time, sizeof(client_time), TRUE);
1304233294Sstas
1305178825Sdfr 		ret = KRB5KRB_AP_ERR_SKEW;
1306178825Sdfr 		kdc_log(context, config, 0,
1307178825Sdfr			"Too large time skew, "
1308233294Sstas			"client time %s is out by %u > %u seconds -- %s",
1309233294Sstas			client_time,
1310233294Sstas			(unsigned)abs(kdc_time - p.patimestamp),
1311178825Sdfr			context->max_skew,
1312178825Sdfr			client_name);
1313233294Sstas
1314233294Sstas		/*
1315233294Sstas		 * The following is needed to make windows clients to
1316233294Sstas		 * retry using the timestamp in the error message, if
1317233294Sstas		 * there is a e_text, they become unhappy.
1318178825Sdfr		 */
1319178825Sdfr		e_text = NULL;
132055682Smarkm		goto out;
132155682Smarkm	    }
132255682Smarkm	    et.flags.pre_authent = 1;
1323178825Sdfr
1324233294Sstas	    set_salt_padata(rep.padata, pa_key->salt);
1325233294Sstas
1326233294Sstas	    reply_key = &pa_key->key;
1327233294Sstas
1328233294Sstas	    ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str);
1329178825Sdfr	    if (ret)
1330178825Sdfr		str = NULL;
1331178825Sdfr
1332178825Sdfr	    kdc_log(context, config, 2,
1333233294Sstas		    "ENC-TS Pre-authentication succeeded -- %s using %s",
1334178825Sdfr		    client_name, str ? str : "unknown enctype");
1335178825Sdfr	    free(str);
133655682Smarkm	    break;
133755682Smarkm	}
1338178825Sdfr#ifdef PKINIT
1339178825Sdfr    preauth_done:
1340178825Sdfr#endif
1341178825Sdfr	if(found_pa == 0 && config->require_preauth)
134255682Smarkm	    goto use_pa;
134355682Smarkm	/* We come here if we found a pa-enc-timestamp, but if there
134455682Smarkm           was some problem with it, other than too large skew */
134555682Smarkm	if(found_pa && et.flags.pre_authent == 0){
1346178825Sdfr	    kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
134755682Smarkm	    e_text = NULL;
134855682Smarkm	    goto out;
134955682Smarkm	}
1350178825Sdfr    }else if (config->require_preauth
1351233294Sstas	      || b->kdc_options.request_anonymous /* hack to force anon */
1352178825Sdfr	      || client->entry.flags.require_preauth
1353178825Sdfr	      || server->entry.flags.require_preauth) {
135455682Smarkm	METHOD_DATA method_data;
135555682Smarkm	PA_DATA *pa;
135655682Smarkm	unsigned char *buf;
135755682Smarkm	size_t len;
135855682Smarkm
1359233294Sstas    use_pa:
136055682Smarkm	method_data.len = 0;
136155682Smarkm	method_data.val = NULL;
136255682Smarkm
136355682Smarkm	ret = realloc_method_data(&method_data);
1364233294Sstas	if (ret) {
1365233294Sstas	    free_METHOD_DATA(&method_data);
1366233294Sstas	    goto out;
1367233294Sstas	}
136855682Smarkm	pa = &method_data.val[method_data.len-1];
136972445Sassar	pa->padata_type		= KRB5_PADATA_ENC_TIMESTAMP;
137055682Smarkm	pa->padata_value.length	= 0;
137155682Smarkm	pa->padata_value.data	= NULL;
137255682Smarkm
1373178825Sdfr#ifdef PKINIT
1374178825Sdfr	ret = realloc_method_data(&method_data);
1375233294Sstas	if (ret) {
1376233294Sstas	    free_METHOD_DATA(&method_data);
1377233294Sstas	    goto out;
1378233294Sstas	}
1379178825Sdfr	pa = &method_data.val[method_data.len-1];
1380178825Sdfr	pa->padata_type		= KRB5_PADATA_PK_AS_REQ;
1381178825Sdfr	pa->padata_value.length	= 0;
1382178825Sdfr	pa->padata_value.data	= NULL;
1383178825Sdfr
1384178825Sdfr	ret = realloc_method_data(&method_data);
1385233294Sstas	if (ret) {
1386233294Sstas	    free_METHOD_DATA(&method_data);
1387233294Sstas	    goto out;
1388233294Sstas	}
1389178825Sdfr	pa = &method_data.val[method_data.len-1];
1390178825Sdfr	pa->padata_type		= KRB5_PADATA_PK_AS_REQ_WIN;
1391178825Sdfr	pa->padata_value.length	= 0;
1392178825Sdfr	pa->padata_value.data	= NULL;
1393178825Sdfr#endif
1394178825Sdfr
1395233294Sstas	/*
1396233294Sstas	 * If there is a client key, send ETYPE_INFO{,2}
1397178825Sdfr	 */
1398233294Sstas	ret = _kdc_find_etype(context,
1399233294Sstas			      config->preauth_use_strongest_session_key, TRUE,
1400233294Sstas			      client, b->etype.val, b->etype.len, NULL, &ckey);
1401233294Sstas	if (ret == 0) {
1402178825Sdfr
1403233294Sstas	    /*
1404233294Sstas	     * RFC4120 requires:
1405233294Sstas	     * - If the client only knows about old enctypes, then send
1406233294Sstas	     *   both info replies (we send 'info' first in the list).
1407233294Sstas	     * - If the client is 'modern', because it knows about 'new'
1408233294Sstas	     *   enctype types, then only send the 'info2' reply.
1409233294Sstas	     *
1410233294Sstas	     * Before we send the full list of etype-info data, we pick
1411233294Sstas	     * the client key we would have used anyway below, just pick
1412233294Sstas	     * that instead.
1413233294Sstas	     */
1414178825Sdfr
1415233294Sstas	    if (older_enctype(ckey->key.keytype)) {
1416233294Sstas		ret = get_pa_etype_info(context, config,
1417233294Sstas					&method_data, ckey);
1418233294Sstas		if (ret) {
1419233294Sstas		    free_METHOD_DATA(&method_data);
1420233294Sstas		    goto out;
1421233294Sstas		}
1422233294Sstas	    }
1423233294Sstas	    ret = get_pa_etype_info2(context, config,
1424233294Sstas				     &method_data, ckey);
1425233294Sstas	    if (ret) {
1426233294Sstas		free_METHOD_DATA(&method_data);
1427233294Sstas		goto out;
1428233294Sstas	    }
1429233294Sstas	}
1430233294Sstas
1431103423Snectar	ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
143255682Smarkm	free_METHOD_DATA(&method_data);
1433178825Sdfr
1434178825Sdfr	e_data.data   = buf;
1435178825Sdfr	e_data.length = len;
1436178825Sdfr	e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",
1437178825Sdfr
143855682Smarkm	ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
1439178825Sdfr
1440178825Sdfr	kdc_log(context, config, 0,
1441178825Sdfr		"No preauth found, returning PREAUTH-REQUIRED -- %s",
1442178825Sdfr		client_name);
1443178825Sdfr	goto out;
144455682Smarkm    }
1445233294Sstas
1446233294Sstas    if (clientdb->hdb_auth_status)
1447233294Sstas	(clientdb->hdb_auth_status)(context, clientdb, client,
1448233294Sstas				    HDB_AUTH_SUCCESS);
1449233294Sstas
1450178825Sdfr    /*
1451233294Sstas     * Verify flags after the user been required to prove its identity
1452233294Sstas     * with in a preauth mech.
1453178825Sdfr     */
1454178825Sdfr
1455233294Sstas    ret = _kdc_check_access(context, config, client, client_name,
1456233294Sstas			    server, server_name,
1457233294Sstas			    req, &e_data);
1458233294Sstas    if(ret)
145955682Smarkm	goto out;
1460233294Sstas
1461233294Sstas    /*
1462233294Sstas     * Selelct the best encryption type for the KDC with out regard to
1463233294Sstas     * the client since the client never needs to read that data.
1464233294Sstas     */
1465233294Sstas
1466178825Sdfr    ret = _kdc_get_preferred_key(context, config,
1467178825Sdfr				 server, server_name,
1468178825Sdfr				 &setype, &skey);
1469178825Sdfr    if(ret)
1470178825Sdfr	goto out;
1471178825Sdfr
147272445Sassar    if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
1473178825Sdfr       || (f.request_anonymous && !config->allow_anonymous)) {
147455682Smarkm	ret = KRB5KDC_ERR_BADOPTION;
1475233294Sstas	e_text = "Bad KDC options";
1476178825Sdfr	kdc_log(context, config, 0, "Bad KDC options -- %s", client_name);
147755682Smarkm	goto out;
147855682Smarkm    }
1479233294Sstas
148072445Sassar    rep.pvno = 5;
148172445Sassar    rep.msg_type = krb_as_rep;
1482233294Sstas
1483233294Sstas    ret = copy_Realm(&client->entry.principal->realm, &rep.crealm);
1484233294Sstas    if (ret)
1485233294Sstas	goto out;
1486233294Sstas    ret = _krb5_principal2principalname(&rep.cname, client->entry.principal);
1487233294Sstas    if (ret)
1488233294Sstas	goto out;
1489233294Sstas
149072445Sassar    rep.ticket.tkt_vno = 5;
1491178825Sdfr    copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
1492233294Sstas    _krb5_principal2principalname(&rep.ticket.sname,
1493178825Sdfr				  server->entry.principal);
1494178825Sdfr    /* java 1.6 expects the name to be the same type, lets allow that
1495178825Sdfr     * uncomplicated name-types. */
1496178825Sdfr#define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
1497178825Sdfr    if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST))
1498178825Sdfr	rep.ticket.sname.name_type = b->sname->name_type;
1499178825Sdfr#undef CNT
150072445Sassar
150155682Smarkm    et.flags.initial = 1;
1502178825Sdfr    if(client->entry.flags.forwardable && server->entry.flags.forwardable)
150355682Smarkm	et.flags.forwardable = f.forwardable;
150455682Smarkm    else if (f.forwardable) {
1505233294Sstas	e_text = "Ticket may not be forwardable";
150655682Smarkm	ret = KRB5KDC_ERR_POLICY;
1507178825Sdfr	kdc_log(context, config, 0,
1508178825Sdfr		"Ticket may not be forwardable -- %s", client_name);
150955682Smarkm	goto out;
151055682Smarkm    }
1511178825Sdfr    if(client->entry.flags.proxiable && server->entry.flags.proxiable)
151255682Smarkm	et.flags.proxiable = f.proxiable;
151355682Smarkm    else if (f.proxiable) {
1514233294Sstas	e_text = "Ticket may not be proxiable";
151555682Smarkm	ret = KRB5KDC_ERR_POLICY;
1516233294Sstas	kdc_log(context, config, 0,
1517178825Sdfr		"Ticket may not be proxiable -- %s", client_name);
151855682Smarkm	goto out;
151955682Smarkm    }
1520178825Sdfr    if(client->entry.flags.postdate && server->entry.flags.postdate)
152155682Smarkm	et.flags.may_postdate = f.allow_postdate;
152255682Smarkm    else if (f.allow_postdate){
1523233294Sstas	e_text = "Ticket may not be postdate";
152455682Smarkm	ret = KRB5KDC_ERR_POLICY;
1525178825Sdfr	kdc_log(context, config, 0,
1526178825Sdfr		"Ticket may not be postdatable -- %s", client_name);
152755682Smarkm	goto out;
152855682Smarkm    }
152955682Smarkm
153055682Smarkm    /* check for valid set of addresses */
1531178825Sdfr    if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
1532233294Sstas	e_text = "Bad address list in requested";
153355682Smarkm	ret = KRB5KRB_AP_ERR_BADADDR;
1534178825Sdfr	kdc_log(context, config, 0,
1535178825Sdfr		"Bad address list requested -- %s", client_name);
153655682Smarkm	goto out;
153755682Smarkm    }
153855682Smarkm
1539233294Sstas    ret = copy_PrincipalName(&rep.cname, &et.cname);
1540178825Sdfr    if (ret)
1541178825Sdfr	goto out;
1542233294Sstas    ret = copy_Realm(&rep.crealm, &et.crealm);
1543233294Sstas    if (ret)
1544233294Sstas	goto out;
1545233294Sstas
154655682Smarkm    {
154755682Smarkm	time_t start;
154855682Smarkm	time_t t;
1549233294Sstas
155055682Smarkm	start = et.authtime = kdc_time;
1551233294Sstas
155255682Smarkm	if(f.postdated && req->req_body.from){
155355682Smarkm	    ALLOC(et.starttime);
155455682Smarkm	    start = *et.starttime = *req->req_body.from;
155555682Smarkm	    et.flags.invalid = 1;
155655682Smarkm	    et.flags.postdated = 1; /* XXX ??? */
155755682Smarkm	}
1558178825Sdfr	_kdc_fix_time(&b->till);
155955682Smarkm	t = *b->till;
156072445Sassar
156172445Sassar	/* be careful not overflowing */
156272445Sassar
1563178825Sdfr	if(client->entry.max_life)
1564178825Sdfr	    t = start + min(t - start, *client->entry.max_life);
1565178825Sdfr	if(server->entry.max_life)
1566178825Sdfr	    t = start + min(t - start, *server->entry.max_life);
156755682Smarkm#if 0
156855682Smarkm	t = min(t, start + realm->max_life);
156955682Smarkm#endif
157055682Smarkm	et.endtime = t;
157155682Smarkm	if(f.renewable_ok && et.endtime < *b->till){
157255682Smarkm	    f.renewable = 1;
157355682Smarkm	    if(b->rtime == NULL){
157455682Smarkm		ALLOC(b->rtime);
157555682Smarkm		*b->rtime = 0;
157655682Smarkm	    }
157755682Smarkm	    if(*b->rtime < *b->till)
157855682Smarkm		*b->rtime = *b->till;
157955682Smarkm	}
158055682Smarkm	if(f.renewable && b->rtime){
158155682Smarkm	    t = *b->rtime;
158255682Smarkm	    if(t == 0)
158355682Smarkm		t = MAX_TIME;
1584178825Sdfr	    if(client->entry.max_renew)
1585178825Sdfr		t = start + min(t - start, *client->entry.max_renew);
1586178825Sdfr	    if(server->entry.max_renew)
1587178825Sdfr		t = start + min(t - start, *server->entry.max_renew);
158855682Smarkm#if 0
158955682Smarkm	    t = min(t, start + realm->max_renew);
159055682Smarkm#endif
159155682Smarkm	    ALLOC(et.renew_till);
159255682Smarkm	    *et.renew_till = t;
159355682Smarkm	    et.flags.renewable = 1;
159455682Smarkm	}
159555682Smarkm    }
159672445Sassar
159772445Sassar    if (f.request_anonymous)
159872445Sassar	et.flags.anonymous = 1;
1599233294Sstas
160055682Smarkm    if(b->addresses){
160155682Smarkm	ALLOC(et.caddr);
160255682Smarkm	copy_HostAddresses(b->addresses, et.caddr);
160355682Smarkm    }
1604233294Sstas
1605127808Snectar    et.transited.tr_type = DOMAIN_X500_COMPRESS;
1606233294Sstas    krb5_data_zero(&et.transited.contents);
160755682Smarkm
160855682Smarkm    /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded
160955682Smarkm     * as 0 and as 0x80 (meaning indefinite length) apart, and is thus
161055682Smarkm     * incapable of correctly decoding SEQUENCE OF's of zero length.
161155682Smarkm     *
161255682Smarkm     * To fix this, always send at least one no-op last_req
161355682Smarkm     *
161455682Smarkm     * If there's a pw_end or valid_end we will use that,
161555682Smarkm     * otherwise just a dummy lr.
161655682Smarkm     */
161755682Smarkm    ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
1618178825Sdfr    if (ek.last_req.val == NULL) {
1619178825Sdfr	ret = ENOMEM;
1620178825Sdfr	goto out;
1621178825Sdfr    }
162255682Smarkm    ek.last_req.len = 0;
1623178825Sdfr    if (client->entry.pw_end
1624178825Sdfr	&& (config->kdc_warn_pwexpire == 0
1625178825Sdfr	    || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
162678527Sassar	ek.last_req.val[ek.last_req.len].lr_type  = LR_PW_EXPTIME;
1627178825Sdfr	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
162855682Smarkm	++ek.last_req.len;
162955682Smarkm    }
1630178825Sdfr    if (client->entry.valid_end) {
163178527Sassar	ek.last_req.val[ek.last_req.len].lr_type  = LR_ACCT_EXPTIME;
1632178825Sdfr	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
163355682Smarkm	++ek.last_req.len;
163455682Smarkm    }
163555682Smarkm    if (ek.last_req.len == 0) {
163678527Sassar	ek.last_req.val[ek.last_req.len].lr_type  = LR_NONE;
163755682Smarkm	ek.last_req.val[ek.last_req.len].lr_value = 0;
163855682Smarkm	++ek.last_req.len;
163955682Smarkm    }
164055682Smarkm    ek.nonce = b->nonce;
1641178825Sdfr    if (client->entry.valid_end || client->entry.pw_end) {
164255682Smarkm	ALLOC(ek.key_expiration);
1643178825Sdfr	if (client->entry.valid_end) {
1644178825Sdfr	    if (client->entry.pw_end)
1645233294Sstas		*ek.key_expiration = min(*client->entry.valid_end,
1646178825Sdfr					 *client->entry.pw_end);
164755682Smarkm	    else
1648178825Sdfr		*ek.key_expiration = *client->entry.valid_end;
164955682Smarkm	} else
1650178825Sdfr	    *ek.key_expiration = *client->entry.pw_end;
165155682Smarkm    } else
165255682Smarkm	ek.key_expiration = NULL;
165355682Smarkm    ek.flags = et.flags;
165455682Smarkm    ek.authtime = et.authtime;
165555682Smarkm    if (et.starttime) {
165655682Smarkm	ALLOC(ek.starttime);
165755682Smarkm	*ek.starttime = *et.starttime;
165855682Smarkm    }
165955682Smarkm    ek.endtime = et.endtime;
166055682Smarkm    if (et.renew_till) {
166155682Smarkm	ALLOC(ek.renew_till);
166255682Smarkm	*ek.renew_till = *et.renew_till;
166355682Smarkm    }
166455682Smarkm    copy_Realm(&rep.ticket.realm, &ek.srealm);
166555682Smarkm    copy_PrincipalName(&rep.ticket.sname, &ek.sname);
166655682Smarkm    if(et.caddr){
166755682Smarkm	ALLOC(ek.caddr);
166855682Smarkm	copy_HostAddresses(et.caddr, ek.caddr);
166955682Smarkm    }
167055682Smarkm
1671178825Sdfr#if PKINIT
1672178825Sdfr    if (pkp) {
1673233294Sstas        e_text = "Failed to build PK-INIT reply";
1674233294Sstas	ret = _kdc_pk_mk_pa_reply(context, config, pkp, client,
1675233294Sstas				  sessionetype, req, req_buffer,
1676233294Sstas				  &reply_key, &et.key, rep.padata);
1677178825Sdfr	if (ret)
1678178825Sdfr	    goto out;
1679178825Sdfr	ret = _kdc_add_inital_verified_cas(context,
1680178825Sdfr					   config,
1681178825Sdfr					   pkp,
1682178825Sdfr					   &et);
1683178825Sdfr	if (ret)
1684178825Sdfr	    goto out;
1685233294Sstas
1686233294Sstas    } else
1687233294Sstas#endif
1688233294Sstas    {
1689233294Sstas	ret = krb5_generate_random_keyblock(context, sessionetype, &et.key);
1690233294Sstas	if (ret)
1691233294Sstas	    goto out;
169255682Smarkm    }
169355682Smarkm
1694233294Sstas    if (reply_key == NULL) {
1695233294Sstas	e_text = "Client have no reply key";
1696233294Sstas	ret = KRB5KDC_ERR_CLIENT_NOTYET;
1697233294Sstas	goto out;
1698233294Sstas    }
169955682Smarkm
1700233294Sstas    ret = copy_EncryptionKey(&et.key, &ek.key);
1701233294Sstas    if (ret)
1702233294Sstas	goto out;
1703233294Sstas
1704178825Sdfr    /* Add signing of alias referral */
1705178825Sdfr    if (f.canonicalize) {
1706178825Sdfr	PA_ClientCanonicalized canon;
1707178825Sdfr	krb5_data data;
1708178825Sdfr	PA_DATA pa;
1709233294Sstas	krb5_crypto cryptox;
1710233294Sstas	size_t len = 0;
171155682Smarkm
1712178825Sdfr	memset(&canon, 0, sizeof(canon));
171355682Smarkm
1714178825Sdfr	canon.names.requested_name = *b->cname;
1715233294Sstas	canon.names.mapped_name = client->entry.principal->name;
171655682Smarkm
1717178825Sdfr	ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
1718178825Sdfr			   &canon.names, &len, ret);
1719233294Sstas	if (ret)
1720178825Sdfr	    goto out;
1721178825Sdfr	if (data.length != len)
1722178825Sdfr	    krb5_abortx(context, "internal asn.1 error");
1723178825Sdfr
1724178825Sdfr	/* sign using "returned session key" */
1725233294Sstas	ret = krb5_crypto_init(context, &et.key, 0, &cryptox);
1726178825Sdfr	if (ret) {
1727178825Sdfr	    free(data.data);
1728178825Sdfr	    goto out;
172955682Smarkm	}
173055682Smarkm
1731233294Sstas	ret = krb5_create_checksum(context, cryptox,
1732178825Sdfr				   KRB5_KU_CANONICALIZED_NAMES, 0,
1733178825Sdfr				   data.data, data.length,
1734178825Sdfr				   &canon.canon_checksum);
1735178825Sdfr	free(data.data);
1736233294Sstas	krb5_crypto_destroy(context, cryptox);
1737178825Sdfr	if (ret)
1738178825Sdfr	    goto out;
1739233294Sstas
1740178825Sdfr	ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length,
1741178825Sdfr			   &canon, &len, ret);
1742178825Sdfr	free_Checksum(&canon.canon_checksum);
1743233294Sstas	if (ret)
1744178825Sdfr	    goto out;
1745178825Sdfr	if (data.length != len)
1746178825Sdfr	    krb5_abortx(context, "internal asn.1 error");
1747127808Snectar
1748178825Sdfr	pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED;
1749178825Sdfr	pa.padata_value = data;
1750178825Sdfr	ret = add_METHOD_DATA(rep.padata, &pa);
1751178825Sdfr	free(data.data);
1752178825Sdfr	if (ret)
1753178825Sdfr	    goto out;
1754127808Snectar    }
1755127808Snectar
1756178825Sdfr    if (rep.padata->len == 0) {
1757178825Sdfr	free(rep.padata);
1758178825Sdfr	rep.padata = NULL;
1759127808Snectar    }
1760178825Sdfr
1761178825Sdfr    /* Add the PAC */
1762178825Sdfr    if (send_pac_p(context, req)) {
1763178825Sdfr	krb5_pac p = NULL;
1764178825Sdfr	krb5_data data;
1765178825Sdfr
1766178825Sdfr	ret = _kdc_pac_generate(context, client, &p);
1767178825Sdfr	if (ret) {
1768233294Sstas	    kdc_log(context, config, 0, "PAC generation failed for -- %s",
1769178825Sdfr		    client_name);
1770178825Sdfr	    goto out;
1771103423Snectar	}
1772178825Sdfr	if (p != NULL) {
1773178825Sdfr	    ret = _krb5_pac_sign(context, p, et.authtime,
1774178825Sdfr				 client->entry.principal,
1775233294Sstas				 &skey->key, /* Server key */
1776178825Sdfr				 &skey->key, /* FIXME: should be krbtgt key */
1777178825Sdfr				 &data);
1778178825Sdfr	    krb5_pac_free(context, p);
1779178825Sdfr	    if (ret) {
1780233294Sstas		kdc_log(context, config, 0, "PAC signing failed for -- %s",
1781178825Sdfr			client_name);
1782178825Sdfr		goto out;
1783127808Snectar	    }
1784178825Sdfr
1785178825Sdfr	    ret = _kdc_tkt_add_if_relevant_ad(context, &et,
1786178825Sdfr					      KRB5_AUTHDATA_WIN2K_PAC,
1787178825Sdfr					      &data);
1788178825Sdfr	    krb5_data_free(&data);
1789178825Sdfr	    if (ret)
1790178825Sdfr		goto out;
1791127808Snectar	}
179255682Smarkm    }
179355682Smarkm
1794233294Sstas    _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
1795178825Sdfr		       et.endtime, et.renew_till);
179655682Smarkm
1797178825Sdfr    /* do this as the last thing since this signs the EncTicketPart */
1798178825Sdfr    ret = _kdc_add_KRB5SignedPath(context,
1799178825Sdfr				  config,
1800178825Sdfr				  server,
1801178825Sdfr				  setype,
1802233294Sstas				  client->entry.principal,
1803178825Sdfr				  NULL,
1804178825Sdfr				  NULL,
1805178825Sdfr				  &et);
1806178825Sdfr    if (ret)
1807120945Snectar	goto out;
180855682Smarkm
1809233294Sstas    log_as_req(context, config, reply_key->keytype, setype, b);
1810233294Sstas
1811233294Sstas    ret = _kdc_encode_reply(context, config,
1812233294Sstas			    &rep, &et, &ek, setype, server->entry.kvno,
1813233294Sstas			    &skey->key, client->entry.kvno,
1814233294Sstas			    reply_key, 0, &e_text, reply);
1815178825Sdfr    free_EncTicketPart(&et);
1816178825Sdfr    free_EncKDCRepPart(&ek);
1817178825Sdfr    if (ret)
1818120945Snectar	goto out;
181955682Smarkm
1820178825Sdfr    /* */
1821178825Sdfr    if (datagram_reply && reply->length > config->max_datagram_reply_length) {
1822178825Sdfr	krb5_data_free(reply);
1823178825Sdfr	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
1824178825Sdfr	e_text = "Reply packet too large";
182555682Smarkm    }
182655682Smarkm
1827178825Sdfrout:
1828178825Sdfr    free_AS_REP(&rep);
1829233294Sstas    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){
1830178825Sdfr	krb5_mk_error(context,
1831178825Sdfr		      ret,
1832178825Sdfr		      e_text,
1833178825Sdfr		      (e_data.data ? &e_data : NULL),
1834178825Sdfr		      client_princ,
1835178825Sdfr		      server_princ,
1836178825Sdfr		      NULL,
1837178825Sdfr		      NULL,
1838178825Sdfr		      reply);
1839178825Sdfr	ret = 0;
184055682Smarkm    }
1841178825Sdfr#ifdef PKINIT
1842178825Sdfr    if (pkp)
1843178825Sdfr	_kdc_pk_free_client_param(context, pkp);
184455682Smarkm#endif
1845178825Sdfr    if (e_data.data)
1846178825Sdfr        free(e_data.data);
1847178825Sdfr    if (client_princ)
1848178825Sdfr	krb5_free_principal(context, client_princ);
1849178825Sdfr    free(client_name);
1850178825Sdfr    if (server_princ)
1851178825Sdfr	krb5_free_principal(context, server_princ);
1852178825Sdfr    free(server_name);
1853178825Sdfr    if(client)
1854178825Sdfr	_kdc_free_ent(context, client);
1855178825Sdfr    if(server)
1856178825Sdfr	_kdc_free_ent(context, server);
185755682Smarkm    return ret;
185855682Smarkm}
185955682Smarkm
186078527Sassar/*
1861233294Sstas * Add the AuthorizationData `data�� of `type�� to the last element in
1862233294Sstas * the sequence of authorization_data in `tkt�� wrapped in an IF_RELEVANT
186378527Sassar */
186478527Sassar
1865178825Sdfrkrb5_error_code
1866178825Sdfr_kdc_tkt_add_if_relevant_ad(krb5_context context,
1867178825Sdfr			    EncTicketPart *tkt,
1868178825Sdfr			    int type,
1869178825Sdfr			    const krb5_data *data)
187055682Smarkm{
187155682Smarkm    krb5_error_code ret;
1872233294Sstas    size_t size = 0;
187355682Smarkm
1874178825Sdfr    if (tkt->authorization_data == NULL) {
1875178825Sdfr	tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data));
1876178825Sdfr	if (tkt->authorization_data == NULL) {
1877233294Sstas	    krb5_set_error_message(context, ENOMEM, "out of memory");
1878178825Sdfr	    return ENOMEM;
1879178825Sdfr	}
188055682Smarkm    }
1881233294Sstas
1882178825Sdfr    /* add the entry to the last element */
188378527Sassar    {
1884178825Sdfr	AuthorizationData ad = { 0, NULL };
1885178825Sdfr	AuthorizationDataElement ade;
188678527Sassar
1887178825Sdfr	ade.ad_type = type;
1888178825Sdfr	ade.ad_data = *data;
188978527Sassar
1890178825Sdfr	ret = add_AuthorizationData(&ad, &ade);
189172445Sassar	if (ret) {
1892233294Sstas	    krb5_set_error_message(context, ret, "add AuthorizationData failed");
1893178825Sdfr	    return ret;
189472445Sassar	}
189555682Smarkm
1896178825Sdfr	ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT;
189755682Smarkm
1898233294Sstas	ASN1_MALLOC_ENCODE(AuthorizationData,
1899233294Sstas			   ade.ad_data.data, ade.ad_data.length,
1900178825Sdfr			   &ad, &size, ret);
1901178825Sdfr	free_AuthorizationData(&ad);
1902178825Sdfr	if (ret) {
1903233294Sstas	    krb5_set_error_message(context, ret, "ASN.1 encode of "
1904233294Sstas				   "AuthorizationData failed");
1905178825Sdfr	    return ret;
190655682Smarkm	}
1907178825Sdfr	if (ade.ad_data.length != size)
1908178825Sdfr	    krb5_abortx(context, "internal asn.1 encoder error");
1909233294Sstas
1910178825Sdfr	ret = add_AuthorizationData(tkt->authorization_data, &ade);
1911178825Sdfr	der_free_octet_string(&ade.ad_data);
1912178825Sdfr	if (ret) {
1913233294Sstas	    krb5_set_error_message(context, ret, "add AuthorizationData failed");
1914178825Sdfr	    return ret;
191555682Smarkm	}
191655682Smarkm    }
191755682Smarkm
191855682Smarkm    return 0;
191955682Smarkm}
1920