Example #1
0
static void
log_as_req(krb5_context context,
	   krb5_kdc_configuration *config,
	   krb5_enctype cetype,
	   krb5_enctype setype,
	   const KDC_REQ_BODY *b)
{
    krb5_error_code ret;
    struct rk_strpool *p = NULL;
    char *str;
    int i;
    
    for (i = 0; i < b->etype.len; i++) {
	ret = krb5_enctype_to_string(context, b->etype.val[i], &str);
	if (ret == 0) {
	    p = rk_strpoolprintf(p, "%s", str);
	    free(str);
	} else
	    p = rk_strpoolprintf(p, "%d", b->etype.val[i]);
	if (p && i + 1 < b->etype.len)
	    p = rk_strpoolprintf(p, ", ");
	if (p == NULL) {
	    kdc_log(context, config, 0, "out of memory");
	    return;
	}
    }
    if (p == NULL)
	p = rk_strpoolprintf(p, "no encryption types");
    
    str = rk_strpoolcollect(p);
    kdc_log(context, config, 0, "Client supported enctypes: %s", str);
    free(str);

    {
	char *cet;
	char *set;

	ret = krb5_enctype_to_string(context, cetype, &cet);
	if(ret == 0) {
	    ret = krb5_enctype_to_string(context, setype, &set);
	    if (ret == 0) {
		kdc_log(context, config, 5, "Using %s/%s", cet, set);
		free(set);
	    }
	    free(cet);
	}
	if (ret != 0)
	    kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype);
    }
    
    {
	char fixedstr[128];
	unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), 
		      fixedstr, sizeof(fixedstr));
	if(*fixedstr)
	    kdc_log(context, config, 2, "Requested flags: %s", fixedstr);
    }
}
Example #2
0
static krb5_error_code copy_one_entry(krb5_context context, 
				      krb5_keytab src_keytab, krb5_keytab dst_keytab, krb5_keytab_entry entry) 
{
    krb5_error_code ret;
    krb5_keytab_entry dummy;

    char *name_str;
    char *etype_str;
    ret = krb5_unparse_name (context, entry.principal, &name_str);
    if(ret) {
	krb5_set_error_message(context, ret, "krb5_unparse_name");
	name_str = NULL; /* XXX */
	return ret;
    }
    ret = krb5_enctype_to_string(context, entry.keyblock.keytype, &etype_str);
    if(ret) {
	krb5_set_error_message(context, ret, "krb5_enctype_to_string");
	etype_str = NULL; /* XXX */
	return ret;
    }
    ret = krb5_kt_get_entry(context, dst_keytab,
			    entry.principal,
			    entry.vno,
			    entry.keyblock.keytype,
			    &dummy);
    if(ret == 0) {
	/* this entry is already in the new keytab, so no need to
	   copy it; if the keyblocks are not the same, something
	   is weird, so complain about that */
	if(!compare_keyblock(&entry.keyblock, &dummy.keyblock)) {
		krb5_warn(context, 0, "entry with different keyvalue "
			  "already exists for %s, keytype %s, kvno %d",
			  name_str, etype_str, entry.vno);
	}
	krb5_kt_free_entry(context, &dummy);
	krb5_kt_free_entry (context, &entry);
	free(name_str);
	free(etype_str);
	return ret;
    } else if(ret != KRB5_KT_NOTFOUND) {
	krb5_set_error_message (context, ret, "fetching %s/%s/%u",
				name_str, etype_str, entry.vno);
	krb5_kt_free_entry (context, &entry);
	free(name_str);
	free(etype_str);
	return ret;
    } 
    ret = krb5_kt_add_entry (context, dst_keytab, &entry);
    krb5_kt_free_entry (context, &entry);
    if (ret) {
	krb5_set_error_message (context, ret, "adding %s/%s/%u",
				name_str, etype_str, entry.vno);
	free(name_str);
	free(etype_str);
	return ret;
    }
    free(name_str);
    free(etype_str);
    return ret;
}
Example #3
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kt_principal_not_found(krb5_context context,
			     krb5_error_code ret,
			     krb5_keytab id,
			     krb5_const_principal principal,
			     krb5_enctype enctype,
			     int kvno)
{
    char princ[256], kvno_str[25], *kt_name;
    char *enctype_str = NULL;

    krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
    krb5_kt_get_full_name (context, id, &kt_name);
    if (enctype)
	krb5_enctype_to_string(context, enctype, &enctype_str);

    if (kvno)
	snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
    else
	kvno_str[0] = '\0';

    krb5_set_error_message (context, ret,
			    N_("Failed to find %s%s in keytab %s (%s)",
			       "principal, kvno, keytab file, enctype"),
			    princ,
			    kvno_str,
			    kt_name ? kt_name : "unknown keytab",
			    enctype_str ? enctype_str : "unknown enctype");
    free(kt_name);
    if (enctype_str)
	free(enctype_str);
    return ret;
}
Example #4
0
static void
tokey(krb5_context context,
      krb5_enctype enctype,
      const char *pw,
      krb5_salt salt,
      const char *label)
{
    krb5_error_code ret;
    size_t i;
    krb5_keyblock key;
    char *e;

    ret = krb5_string_to_key_salt(context, enctype, pw, salt, &key);
    if (ret)
	krb5_err(context, 1, ret, "krb5_string_to_key_salt");
    ret = krb5_enctype_to_string(context, enctype, &e);
    if (ret)
	krb5_err(context, 1, ret, "krb5_enctype_to_string");
    printf(label, e);
    printf(": ");
    for(i = 0; i < key.keyvalue.length; i++)
	printf("%02x", ((unsigned char*)key.keyvalue.data)[i]);
    printf("\n");
    krb5_free_keyblock_contents(context, &key);
    free(e);
}
Example #5
0
/*
 * call-seq:
 *   krb5.get_permitted_enctypes
 *
 * Returns a hash containing the permitted encoding types. The key is the
 * numeric constant, with a string description as its value.
 *
 * Example:
 *
 *   krb.get_permitted_enctypes
 *
 *   # Results:
 *   {
 *      1  => "DES cbc mode with CRC-32",
 *      2  => "DES cbc mode with RSA-MD4",
 *      3  => "DES cbc mode with RSA-MD5"}
 *      16 => "Triple DES cbc mode with HMAC/sha1",
 *      17 => "AES-128 CTS mode with 96-bit SHA-1 HMAC",
 *      18 => "AES-256 CTS mode with 96-bit SHA-1 HMAC",
 *      23 => "ArcFour with HMAC/md5"
 *   }
 */
static VALUE rkrb5_get_permitted_enctypes(VALUE self){
  RUBY_KRB5* ptr;
  VALUE v_enctypes;
  krb5_enctype* ktypes;
  krb5_error_code kerror;

  Data_Get_Struct(self, RUBY_KRB5, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  kerror = krb5_get_permitted_enctypes(ptr->ctx, &ktypes);

  if(kerror){
    rb_raise(cKrb5Exception, "krb5_get_permitted_types: %s", error_message(kerror));
  }
  else{
    int i;
    char encoding[128];
    v_enctypes = rb_hash_new();

    for(i = 0; ktypes[i]; i++){
      if(krb5_enctype_to_string(ktypes[i], encoding, 128)){
        rb_raise(cKrb5Exception, "krb5_enctype_to_string: %s", error_message(kerror));
      }
      rb_hash_aset(v_enctypes, INT2FIX(ktypes[i]), rb_str_new2(encoding));
    }
  }

  return v_enctypes;
}
static void
test_wrapping(krb5_context context,
	      size_t min_size,
	      size_t max_size,
	      size_t step,
	      krb5_enctype etype)
{
    krb5_error_code ret;
    krb5_keyblock key;
    krb5_crypto crypto;
    krb5_data data;
    char *etype_name;
    void *buf;
    size_t size;

    ret = krb5_generate_random_keyblock(context, etype, &key);
    if (ret)
	krb5_err(context, 1, ret, "krb5_generate_random_keyblock");

    ret = krb5_enctype_to_string(context, etype, &etype_name);
    if (ret)
	krb5_err(context, 1, ret, "krb5_enctype_to_string");

    buf = malloc(max_size);
    if (buf == NULL)
	krb5_errx(context, 1, "out of memory");
    memset(buf, 0, max_size);

    ret = krb5_crypto_init(context, &key, 0, &crypto);
    if (ret)
	krb5_err(context, 1, ret, "krb5_crypto_init");

    for (size = min_size; size < max_size; size += step) {
	size_t wrapped_size;

	ret = krb5_encrypt(context, crypto, 0, buf, size, &data);
	if (ret)
	    krb5_err(context, 1, ret, "encrypt size %lu using %s",
		     (unsigned long)size, etype_name);

	wrapped_size = krb5_get_wrapped_length(context, crypto, size);

	if (wrapped_size != data.length)
	    krb5_errx(context, 1, "calculated wrapped length %lu != "
		      "real wrapped length %lu for data length %lu using "
		      "enctype %s",
		      (unsigned long)wrapped_size,
		      (unsigned long)data.length,
		      (unsigned long)size,
		      etype_name);
	krb5_data_free(&data);
    }

    free(etype_name);
    free(buf);
    krb5_crypto_destroy(context, crypto);
    krb5_free_keyblock_contents(context, &key);
}
Example #7
0
char *enctype_to_string (krb5_enctype enctype)
{
    static char enctypeString[100];

    if (krb5_enctype_to_string (enctype, enctypeString, sizeof(enctypeString)) != 0) {
        sprintf (enctypeString, "etype %d", enctype); /* default if error */
    }

    return enctypeString;
}
Example #8
0
static void
format_keytype(krb5_key_data *k, krb5_salt *def_salt, char *buf, size_t buf_len)
{
    krb5_error_code ret;
    char *s;
    int aret;

    buf[0] = '\0';
    ret = krb5_enctype_to_string (context,
				  k->key_data_type[0],
				  &s);
    if (ret) {
	aret = asprintf (&s, "unknown(%d)", k->key_data_type[0]);
	if (aret == -1)
	    return;	/* Nothing to do here, we have no way to pass the err */
    }
    strlcpy(buf, s, buf_len);
    free(s);

    strlcat(buf, "(", buf_len);

    ret = krb5_salttype_to_string (context,
				   k->key_data_type[0],
				   k->key_data_type[1],
				   &s);
    if (ret) {
	aret = asprintf (&s, "unknown(%d)", k->key_data_type[1]);
	if (aret == -1)
	    return;	/* Again, nothing else to do... */
    }
    strlcat(buf, s, buf_len);
    free(s);

    aret = 0;
    if (cmp_salt(def_salt, k) == 0)
	s = strdup("");
    else if(k->key_data_length[1] == 0)
	s = strdup("()");
    else
	aret = asprintf (&s, "(%.*s)", k->key_data_length[1],
			 (char *)k->key_data_contents[1]);
    if (aret == -1 || s == NULL)
	return;		/* Again, nothing else we can do... */
    strlcat(buf, s, buf_len);
    free(s);
    aret = asprintf (&s, "[%d]", k->key_data_kvno);
    if (aret == -1)
	return;
    strlcat(buf, ")", buf_len);

    strlcat(buf, s, buf_len);
    free(s);
}
Example #9
0
static krb5_error_code
negotiate_etype(krb5_context context,
                const krb5_enctype *desired_etypes,
                int desired_etypes_len,
                int mandatory_etypes_index,
                const krb5_enctype *permitted_etypes,
                int permitted_etypes_len,
                krb5_enctype *negotiated_etype)
{
    int i, j;

    *negotiated_etype = ENCTYPE_NULL;

    /* mandatory segment of desired_etypes must be permitted */
    for (i = mandatory_etypes_index; i < desired_etypes_len; i++) {
        krb5_boolean permitted = FALSE;

        for (j = 0; j < permitted_etypes_len; j++) {
            if (desired_etypes[i] == permitted_etypes[j]) {
                permitted = TRUE;
                break;
            }
        }

        if (permitted == FALSE) {
            char enctype_name[30];

            if (krb5_enctype_to_string(desired_etypes[i],
                                       enctype_name,
                                       sizeof(enctype_name)) == 0)
                krb5_set_error_message(context, KRB5_NOPERM_ETYPE,
                                       _("Encryption type %s not permitted"),
                                       enctype_name);
            return KRB5_NOPERM_ETYPE;
        }
    }

    /*
     * permitted_etypes is ordered from most to least preferred;
     * find first desired_etype that matches.
     */
    for (j = 0; j < permitted_etypes_len; j++) {
        for (i = 0; i < desired_etypes_len; i++) {
            if (desired_etypes[i] == permitted_etypes[j]) {
                *negotiated_etype = permitted_etypes[j];
                return 0;
            }
        }
    }

    /*NOTREACHED*/
    return KRB5_NOPERM_ETYPE;
}
Example #10
0
static void
time_encryption(krb5_context context, size_t size,
		krb5_enctype etype, int iterations)
{
    struct timeval tv1, tv2;
    krb5_error_code ret;
    krb5_keyblock key;
    krb5_crypto crypto;
    krb5_data data;
    char *etype_name;
    void *buf;
    int i;

    ret = krb5_generate_random_keyblock(context, etype, &key);
    if (ret)
	krb5_err(context, 1, ret, "krb5_generate_random_keyblock");

    ret = krb5_enctype_to_string(context, etype, &etype_name);
    if (ret)
	krb5_err(context, 1, ret, "krb5_enctype_to_string");

    buf = malloc(size);
    if (buf == NULL)
	krb5_errx(context, 1, "out of memory");
    memset(buf, 0, size);

    ret = krb5_crypto_init(context, &key, 0, &crypto);
    if (ret)
	krb5_err(context, 1, ret, "krb5_crypto_init");

    gettimeofday(&tv1, NULL);

    for (i = 0; i < iterations; i++) {
	ret = krb5_encrypt(context, crypto, 0, buf, size, &data);
	if (ret)
	    krb5_err(context, 1, ret, "encrypt: %d", i);
	krb5_data_free(&data);
    }

    gettimeofday(&tv2, NULL);

    timevalsub(&tv2, &tv1);

    printf("%s size: %7lu iterations: %d time: %3ld.%06ld\n",
	   etype_name, (unsigned long)size, iterations,
	   (long)tv2.tv_sec, (long)tv2.tv_usec);

    free(buf);
    free(etype_name);
    krb5_crypto_destroy(context, crypto);
    krb5_free_keyblock_contents(context, &key);
}
Example #11
0
QString
v5::etype2String(krb5_enctype enctype)
{
	static char buf[100];
	krb5_error_code retval;
	
	if((retval = krb5_enctype_to_string(enctype, buf, sizeof(buf))))
	{
		/* XXX if there's an error != EINVAL, I should probably report it */
		sprintf(buf, "etype %d", enctype);
	}

	return buf;
}
Example #12
0
static
krb5_error_code
ks_tuple2str(krb5_context context, int n_ks_tuple,
	     krb5_key_salt_tuple *ks_tuple, char ***ks_tuple_strs)
{
	size_t i;
	char **ksnames;
	krb5_error_code rc = KRB5_PROG_ETYPE_NOSUPP;

	*ks_tuple_strs = NULL;
	if (n_ks_tuple < 1)
		return 0;

	if ((ksnames = calloc(n_ks_tuple + 1, sizeof (*ksnames))) == NULL)
		return (errno);

	for (i = 0; i < n_ks_tuple; i++) {
            char *ename, *sname;

	    if (krb5_enctype_to_string(context, ks_tuple[i].ks_enctype, &ename))
		goto out;
	    if (krb5_salttype_to_string(context, ks_tuple[i].ks_enctype,
					ks_tuple[i].ks_salttype, &sname)) {
                free(ename);
		goto out;
            }

	    if (asprintf(&ksnames[i], "%s:%s", ename, sname) == -1) {
		    rc = errno;
		    free(ename);
		    free(sname);
		    goto out;
	    }
	    free(ename);
	    free(sname);
	}

	ksnames[i] = NULL;
	*ks_tuple_strs = ksnames;
	return 0;

out:
	for (i = 0; i < n_ks_tuple; i++)
		free(ksnames[i]);
	free(ksnames);
	return (rc);
}
Example #13
0
File: creds.c Project: B-Rich/smart
/* FIXME */
PyObject *creds_parse(krb5CredsObject *self, PyObject *args)
{
	krb5_error_code ret;
	PyObject *tuple;
	Ticket t;
	size_t len;
	char *s;
	int error = 0;

	if ((tuple = PyTuple_New(3)) == NULL) {
		error = 1;
		goto out;
	};

	decode_Ticket(self->creds.ticket.data, self->creds.ticket.length, &t, &len);

	ret = krb5_enctype_to_string(self->context, t.enc_part.etype, &s);
	if (ret != 0) {
		asprintf(&s, "unknown (%d)", t.enc_part.etype);
	}
	PyTuple_SetItem(tuple, 0, PyString_FromString(s));
	free(s);

	if (t.enc_part.kvno)
		PyTuple_SetItem(tuple, 1, PyInt_FromLong(*t.enc_part.kvno));
	else
		PyTuple_SetItem(tuple, 1, PyInt_FromLong(-1));

	ret = krb5_unparse_name(self->context, self->creds.server, &s);
	if (ret)
		krb5_exception(self->context, 1, ret, "krb5_unparse_name");
	PyTuple_SetItem(tuple, 2, PyString_FromString(s));
	free(s);

	//PyTuple_SetItem(tuple, 0, PyInt_FromLong(entry.vno));
	//PyTuple_SetItem(tuple, 1, PyString_FromString(etype));
	//PyTuple_SetItem(tuple, 2, PyString_FromString(principal));
	//PyTuple_SetItem(tuple, 3, PyInt_FromLong(entry.timestamp));
	//PyTuple_SetItem(tuple, 4, PyString_FromString(entry.keyblock.keyvalue.data));

 out:
	if (error)
		return NULL;
	else
		return tuple;
}
static void
print_keys(krb5_context context, Key *keys, size_t nkeys)
{
    krb5_error_code ret;
    char *str;
    int i;

    printf("keys:\n");

    for (i = 0; i < nkeys; i++) {

	ret = krb5_enctype_to_string(context, keys[i].key.keytype, &str);
	if (ret)
	    krb5_err(context, ret, 1, "krb5_enctype_to_string: %d\n",
		     (int)keys[i].key.keytype);

	printf("\tenctype %s", str);
	free(str);

	if (keys[i].salt) {
	    printf(" salt: ");

	    switch (keys[i].salt->type) {
	    case KRB5_PW_SALT:
		printf("pw-salt:");
		break;
	    case KRB5_AFS3_SALT:
		printf("afs3-salt:");
		break;
	    default:
		printf("unknown salt: %d", keys[i].salt->type);
		break;
	    }
	    if (keys[i].salt->salt.length)
		printf("%.*s", (int)keys[i].salt->salt.length,
		       (char *)keys[i].salt->salt.data);
	}
	printf("\n");
    }
    printf("end keys:\n");
}
Example #15
0
static void
time_s2k(krb5_context context,
	 krb5_enctype etype,
	 const char *password,
	 krb5_salt salt,
	 int iterations)
{
    struct timeval tv1, tv2;
    krb5_error_code ret;
    krb5_keyblock key;
    krb5_data opaque;
    char *etype_name;
    int i;

    ret = krb5_enctype_to_string(context, etype, &etype_name);
    if (ret)
	krb5_err(context, 1, ret, "krb5_enctype_to_string");

    opaque.data = NULL;
    opaque.length = 0;

    gettimeofday(&tv1, NULL);

    for (i = 0; i < iterations; i++) {
	ret = krb5_string_to_key_salt_opaque(context, etype, password, salt,
					 opaque, &key);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_string_to_key_data_salt_opaque");
	krb5_free_keyblock_contents(context, &key);
    }

    gettimeofday(&tv2, NULL);

    timevalsub(&tv2, &tv1);

    printf("%s string2key %d iterations time: %3ld.%06ld\n",
	   etype_name, iterations, (long)tv2.tv_sec, (long)tv2.tv_usec);
    free(etype_name);

}
Example #16
0
static void
format_keytype(krb5_key_data *k, krb5_salt *def_salt, char *buf, size_t buf_len)
{
    krb5_error_code ret;
    char *s;

    ret = krb5_enctype_to_string (context,
				  k->key_data_type[0],
				  &s);
    if (ret)
	asprintf (&s, "unknown(%d)", k->key_data_type[0]);
    strlcpy(buf, s, buf_len);
    free(s);

    strlcat(buf, "(", buf_len);

    ret = krb5_salttype_to_string (context,
				   k->key_data_type[0],
				   k->key_data_type[1],
				   &s);
    if (ret)
	asprintf (&s, "unknown(%d)", k->key_data_type[1]);
    strlcat(buf, s, buf_len);
    free(s);

    if (cmp_salt(def_salt, k) == 0)
	s = strdup("");
    else if(k->key_data_length[1] == 0)
	s = strdup("()");
    else
	asprintf (&s, "(%.*s)", k->key_data_length[1],
		  (char *)k->key_data_contents[1]);
    strlcat(buf, s, buf_len);
    free(s);
    asprintf (&s, "[%d]", k->key_data_kvno);
    strlcat(buf, ")", buf_len);

    strlcat(buf, s, buf_len);
    free(s);
}
Example #17
0
static krb5_error_code
krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
			const krb5_ap_req *req, krb5_const_principal server,
			krb5_keytab keytab, krb5_flags *ap_req_options,
			krb5_ticket **ticket, int check_valid_flag)
{
    krb5_error_code 	  retval = 0;
    krb5_timestamp 	  currenttime;
    krb5_principal_data princ_data;
    
    req->ticket->enc_part2 == NULL;
    if (server && krb5_is_referral_realm(&server->realm)) {
	char *realm;
	princ_data = *server;
	server = &princ_data;
	retval = krb5_get_default_realm(context, &realm);
	if (retval)
	    return retval;
	princ_data.realm.data = realm;
	princ_data.realm.length = strlen(realm);
    }
    if (server && !krb5_principal_compare(context, server, req->ticket->server)) {
	char *found_name = 0, *wanted_name = 0;
	if (krb5_unparse_name(context, server, &wanted_name) == 0
	    && krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
	    krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
				   "Wrong principal in request (found %s, wanted %s)",
				   found_name, wanted_name);
	krb5_free_unparsed_name(context, wanted_name);
	krb5_free_unparsed_name(context, found_name);
	retval =  KRB5KRB_AP_WRONG_PRINC;
	goto cleanup;
    }

    /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
       do we need special processing here ?	*/

    /* decrypt the ticket */
    if ((*auth_context)->keyblock) { /* User to User authentication */
    	if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock,
					    req->ticket)))
goto cleanup;
	krb5_free_keyblock(context, (*auth_context)->keyblock);
	(*auth_context)->keyblock = NULL;
    } else {
    	if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab)))
	    goto cleanup;
    }

    /* XXX this is an evil hack.  check_valid_flag is set iff the call
       is not from inside the kdc.  we can use this to determine which
       key usage to use */
    if ((retval = decrypt_authenticator(context, req, 
					&((*auth_context)->authentp),
					check_valid_flag)))
	goto cleanup;

    if (!krb5_principal_compare(context, (*auth_context)->authentp->client,
				req->ticket->enc_part2->client)) {
	retval = KRB5KRB_AP_ERR_BADMATCH;
	goto cleanup;
    }

    if ((*auth_context)->remote_addr && 
      !krb5_address_search(context, (*auth_context)->remote_addr, 
			   req->ticket->enc_part2->caddrs)) {
	retval = KRB5KRB_AP_ERR_BADADDR;
	goto cleanup;
    }

    /* okay, now check cross-realm policy */

#if defined(_SINGLE_HOP_ONLY)

    /* Single hop cross-realm tickets only */

    { 
	krb5_transited *trans = &(req->ticket->enc_part2->transited);

      	/* If the transited list is empty, then we have at most one hop */
      	if (trans->tr_contents.data && trans->tr_contents.data[0])
            retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
    }

#elif defined(_NO_CROSS_REALM)

    /* No cross-realm tickets */

    { 
	char		* lrealm;
      	krb5_data      	* realm;
      	krb5_transited 	* trans;
  
	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
	trans = &(req->ticket->enc_part2->transited);

	/*
      	 * If the transited list is empty, then we have at most one hop 
      	 * So we also have to check that the client's realm is the local one 
	 */
      	krb5_get_default_realm(context, &lrealm);
      	if ((trans->tr_contents.data && trans->tr_contents.data[0]) ||
          strlen(lrealm) != realm->length ||
          memcmp(lrealm, realm->data, strlen(lrealm))) {
            retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
      	}
      	free(lrealm);
    }

#else

    /* Hierarchical Cross-Realm */
  
    {
      	krb5_data      * realm;
      	krb5_transited * trans;
  
	realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
	trans = &(req->ticket->enc_part2->transited);

	/*
      	 * If the transited list is not empty, then check that all realms 
      	 * transited are within the hierarchy between the client's realm  
      	 * and the local realm.                                        
  	 */
	if (trans->tr_contents.data && trans->tr_contents.data[0]) {
	    retval = krb5_check_transited_list(context, &(trans->tr_contents), 
					       realm,
					       krb5_princ_realm (context,
								 server));
      	}
    }

#endif

    if (retval)  goto cleanup;

    /* only check rcache if sender has provided one---some services
       may not be able to use replay caches (such as datagram servers) */

    if ((*auth_context)->rcache) {
	krb5_donot_replay  rep;
        krb5_tkt_authent   tktauthent;

	tktauthent.ticket = req->ticket;	
	tktauthent.authenticator = (*auth_context)->authentp;
	if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) {
	    retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
	    krb5_xfree(rep.server);
	    krb5_xfree(rep.client);
	}

	if (retval)
	    goto cleanup;
    }

    retval = krb5_validate_times(context, &req->ticket->enc_part2->times);
    if (retval != 0)
	    goto cleanup;

    if ((retval = krb5_timeofday(context, &currenttime)))
	goto cleanup;

    if (!in_clock_skew((*auth_context)->authentp->ctime)) {
	retval = KRB5KRB_AP_ERR_SKEW;
	goto cleanup;
    }

    if (check_valid_flag) {
      if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
	retval = KRB5KRB_AP_ERR_TKT_INVALID;
	goto cleanup;
      }
    }

    /* check if the various etypes are permitted */

    if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
	/* no etype check needed */;
    } else if ((*auth_context)->permitted_etypes == NULL) {
	int etype;
	/* check against the default set */
	if ((!krb5_is_permitted_enctype(context,
					etype = req->ticket->enc_part.enctype)) ||
	    (!krb5_is_permitted_enctype(context,
					etype = req->ticket->enc_part2->session->enctype)) ||
	    (((*auth_context)->authentp->subkey) &&
	     !krb5_is_permitted_enctype(context,
					etype = (*auth_context)->authentp->subkey->enctype))) {
	    char enctype_name[30];
	    retval = KRB5_NOPERM_ETYPE;
	    if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
		krb5_set_error_message(context, retval,
				       "Encryption type %s not permitted",
				       enctype_name);
	    goto cleanup;
	}
    } else {
	/* check against the set in the auth_context */
	int i;

	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
	    if ((*auth_context)->permitted_etypes[i] ==
		req->ticket->enc_part.enctype)
		break;
	if (!(*auth_context)->permitted_etypes[i]) {
	    char enctype_name[30];
	    retval = KRB5_NOPERM_ETYPE;
	    if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
				       enctype_name, sizeof(enctype_name)) == 0)
		krb5_set_error_message(context, retval,
				       "Encryption type %s not permitted",
				       enctype_name);
	    goto cleanup;
	}
	
	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
	    if ((*auth_context)->permitted_etypes[i] ==
		req->ticket->enc_part2->session->enctype)
		break;
	if (!(*auth_context)->permitted_etypes[i]) {
	    char enctype_name[30];
	    retval = KRB5_NOPERM_ETYPE;
	    if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
				       enctype_name, sizeof(enctype_name)) == 0)
		krb5_set_error_message(context, retval,
				       "Encryption type %s not permitted",
				       enctype_name);
	    goto cleanup;
	}
	
	if ((*auth_context)->authentp->subkey) {
	    for (i=0; (*auth_context)->permitted_etypes[i]; i++)
		if ((*auth_context)->permitted_etypes[i] ==
		    (*auth_context)->authentp->subkey->enctype)
		    break;
	    if (!(*auth_context)->permitted_etypes[i]) {
		char enctype_name[30];
		retval = KRB5_NOPERM_ETYPE;
		if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
					   enctype_name,
					   sizeof(enctype_name)) == 0)
		    krb5_set_error_message(context, retval,
					   "Encryption type %s not permitted",
					   enctype_name);
		goto cleanup;
	    }
	}
    }

    (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
    if ((*auth_context)->authentp->subkey) {
	if ((retval = krb5_copy_keyblock(context,
					 (*auth_context)->authentp->subkey,
					 &((*auth_context)->recv_subkey))))
	    goto cleanup;
	retval = krb5_copy_keyblock(context, (*auth_context)->authentp->subkey,
				    &((*auth_context)->send_subkey));
	if (retval) {
	    krb5_free_keyblock(context, (*auth_context)->recv_subkey);
	    (*auth_context)->recv_subkey = NULL;
	    goto cleanup;
	}
    } else {
	(*auth_context)->recv_subkey = 0;
	(*auth_context)->send_subkey = 0;
    }

    if ((retval = krb5_copy_keyblock(context, req->ticket->enc_part2->session,
				     &((*auth_context)->keyblock))))
	goto cleanup;

    /*
     * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used 
     * then the default sequence number is the one's complement of the
     * sequence number sent ot us.
     */
    if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) && 
      (*auth_context)->remote_seq_number) {
	(*auth_context)->local_seq_number ^= 
	  (*auth_context)->remote_seq_number;
    }

    if (ticket)
   	if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
	    goto cleanup;
    if (ap_req_options)
    	*ap_req_options = req->ap_options;
    retval = 0;
    
cleanup:
    if (server == &princ_data)
	krb5_free_default_realm(context, princ_data.realm.data);
    if (retval) {
	/* only free if we're erroring out...otherwise some
	   applications will need the output. */
	if (req->ticket->enc_part2)
	    krb5_free_enc_tkt_part(context, req->ticket->enc_part2);
	req->ticket->enc_part2 = NULL;
    }
    return retval;
}
Example #18
0
krb5_error_code
_kdc_as_rep(krb5_context context,
	    krb5_kdc_configuration *config,
	    KDC_REQ *req,
	    const krb5_data *req_buffer,
	    krb5_data *reply,
	    const char *from,
	    struct sockaddr *from_addr,
	    int datagram_reply)
{
    KDC_REQ_BODY *b = &req->req_body;
    AS_REP rep;
    KDCOptions f = b->kdc_options;
    hdb_entry_ex *client = NULL, *server = NULL;
    HDB *clientdb;
    krb5_enctype setype, sessionetype;
    krb5_data e_data;
    EncTicketPart et;
    EncKDCRepPart ek;
    krb5_principal client_princ = NULL, server_princ = NULL;
    char *client_name = NULL, *server_name = NULL;
    krb5_error_code ret = 0;
    const char *e_text = NULL;
    krb5_crypto crypto;
    Key *ckey, *skey;
    EncryptionKey *reply_key = NULL, session_key;
    int flags = HDB_F_FOR_AS_REQ;
#ifdef PKINIT
    pk_client_params *pkp = NULL;
#endif

    memset(&rep, 0, sizeof(rep));
    memset(&session_key, 0, sizeof(session_key));
    krb5_data_zero(&e_data);

    ALLOC(rep.padata);
    rep.padata->len = 0;
    rep.padata->val = NULL;

    if (f.canonicalize)
	flags |= HDB_F_CANON;

    if(b->sname == NULL){
	ret = KRB5KRB_ERR_GENERIC;
	e_text = "No server in request";
    } else{
	ret = _krb5_principalname2krb5_principal (context,
						  &server_princ,
						  *(b->sname),
						  b->realm);
	if (ret == 0)
	    ret = krb5_unparse_name(context, server_princ, &server_name);
    }
    if (ret) {
	kdc_log(context, config, 0,
		"AS-REQ malformed server name from %s", from);
	goto out;
    }
    if(b->cname == NULL){
	ret = KRB5KRB_ERR_GENERIC;
	e_text = "No client in request";
    } else {
	ret = _krb5_principalname2krb5_principal (context,
						  &client_princ,
						  *(b->cname),
						  b->realm);
	if (ret)
	    goto out;

	ret = krb5_unparse_name(context, client_princ, &client_name);
    }
    if (ret) {
	kdc_log(context, config, 0,
		"AS-REQ malformed client name from %s", from);
	goto out;
    }

    kdc_log(context, config, 0, "AS-REQ %s from %s for %s",
	    client_name, from, server_name);

    /*
     *
     */

    if (_kdc_is_anonymous(context, client_princ)) {
	if (!b->kdc_options.request_anonymous) {
	    kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag");
	    ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	    goto out;
	}
    } else if (b->kdc_options.request_anonymous) {
	kdc_log(context, config, 0,
		"Request for a anonymous ticket with non "
		"anonymous client name: %s", client_name);
	ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	goto out;
    }

    /*
     *
     */

    ret = _kdc_db_fetch(context, config, client_princ,
			HDB_F_GET_CLIENT | flags, NULL,
			&clientdb, &client);
    if(ret == HDB_ERR_NOT_FOUND_HERE) {
	kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name);
	goto out;
    } else if(ret){
	const char *msg = krb5_get_error_message(context, ret);
	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg);
	krb5_free_error_message(context, msg);
	ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	goto out;
    }
    ret = _kdc_db_fetch(context, config, server_princ,
			HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags,
			NULL, NULL, &server);
    if(ret == HDB_ERR_NOT_FOUND_HERE) {
	kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name);
	goto out;
    } else if(ret){
	const char *msg = krb5_get_error_message(context, ret);
	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg);
	krb5_free_error_message(context, msg);
	ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
	goto out;
    }

    memset(&et, 0, sizeof(et));
    memset(&ek, 0, sizeof(ek));

    /*
     * Select a session enctype from the list of the crypto system
     * supported enctypes that is supported by the client and is one of
     * the enctype of the enctype of the service (likely krbtgt).
     *
     * The latter is used as a hint of what enctypes all KDC support,
     * to make sure a newer version of KDC won't generate a session
     * enctype that an older version of a KDC in the same realm can't
     * decrypt.
     */
    ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE,
			  client, b->etype.val, b->etype.len, &sessionetype,
			  NULL);
    if (ret) {
	kdc_log(context, config, 0,
		"Client (%s) from %s has no common enctypes with KDC "
		"to use for the session key",
		client_name, from);
	goto out;
    }
    /*
     * But if the KDC admin is paranoid and doesn't want to have "not
     * the best" enctypes on the krbtgt, lets save the best pick from
     * the client list and hope that that will work for any other
     * KDCs.
     */

    /*
     * Pre-auth processing
     */

    if(req->padata){
	int i;
	const PA_DATA *pa;
	int found_pa = 0;

	log_patypes(context, config, req->padata);

#ifdef PKINIT
	kdc_log(context, config, 5,
		"Looking for PKINIT pa-data -- %s", client_name);

	e_text = "No PKINIT PA found";

	i = 0;
	pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ);
	if (pa == NULL) {
	    i = 0;
	    pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN);
	}
	if (pa) {
	    char *client_cert = NULL;

	    ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp);
	    if (ret) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		kdc_log(context, config, 5,
			"Failed to decode PKINIT PA-DATA -- %s",
			client_name);
		goto ts_enc;
	    }
	    if (ret == 0 && pkp == NULL)
		goto ts_enc;

	    ret = _kdc_pk_check_client(context,
				       config,
				       clientdb,
				       client,
				       pkp,
				       &client_cert);
	    if (ret) {
		e_text = "PKINIT certificate not allowed to "
		    "impersonate principal";
		_kdc_pk_free_client_param(context, pkp);

		kdc_log(context, config, 0, "%s", e_text);
		pkp = NULL;
		goto out;
	    }

	    found_pa = 1;
	    et.flags.pre_authent = 1;
	    kdc_log(context, config, 0,
		    "PKINIT pre-authentication succeeded -- %s using %s",
		    client_name, client_cert);
	    free(client_cert);
	    if (pkp)
		goto preauth_done;
	}
    ts_enc:
#endif

	if (client->entry.flags.locked_out) {
	    ret = KRB5KDC_ERR_CLIENT_REVOKED;
	    kdc_log(context, config, 0,
		    "Client (%s) is locked out", client_name);
	    goto out;
	}

	kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s",
		client_name);

	i = 0;
	e_text = "No ENC-TS found";
	while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
	    krb5_data ts_data;
	    PA_ENC_TS_ENC p;
	    size_t len;
	    EncryptedData enc_data;
	    Key *pa_key;
	    char *str;

	    found_pa = 1;

	    if (b->kdc_options.request_anonymous) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		kdc_log(context, config, 0, "ENC-TS doesn't support anon");
		goto out;
	    }

	    ret = decode_EncryptedData(pa->padata_value.data,
				       pa->padata_value.length,
				       &enc_data,
				       &len);
	    if (ret) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s",
			client_name);
		goto out;
	    }

	    ret = hdb_enctype2key(context, &client->entry,
				  enc_data.etype, &pa_key);
	    if(ret){
		char *estr;
		e_text = "No key matches pa-data";
		ret = KRB5KDC_ERR_ETYPE_NOSUPP;
		if(krb5_enctype_to_string(context, enc_data.etype, &estr))
		    estr = NULL;
		if(estr == NULL)
		    kdc_log(context, config, 5,
			    "No client key matching pa-data (%d) -- %s",
			    enc_data.etype, client_name);
		else
		    kdc_log(context, config, 5,
			    "No client key matching pa-data (%s) -- %s",
			    estr, client_name);
		free(estr);
		free_EncryptedData(&enc_data);

		continue;
	    }

	try_next_key:
	    ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto);
	    if (ret) {
		const char *msg = krb5_get_error_message(context, ret);
		kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
		krb5_free_error_message(context, msg);
		free_EncryptedData(&enc_data);
		continue;
	    }

	    ret = krb5_decrypt_EncryptedData (context,
					      crypto,
					      KRB5_KU_PA_ENC_TIMESTAMP,
					      &enc_data,
					      &ts_data);
	    krb5_crypto_destroy(context, crypto);
	    /*
	     * Since the user might have several keys with the same
	     * enctype but with diffrent salting, we need to try all
	     * the keys with the same enctype.
	     */
	    if(ret){
		krb5_error_code ret2;
		const char *msg = krb5_get_error_message(context, ret);

		ret2 = krb5_enctype_to_string(context,
					      pa_key->key.keytype, &str);
		if (ret2)
		    str = NULL;
		kdc_log(context, config, 5,
			"Failed to decrypt PA-DATA -- %s "
			"(enctype %s) error %s",
			client_name, str ? str : "unknown enctype", msg);
		krb5_free_error_message(context, msg);
		free(str);

		if(hdb_next_enctype2key(context, &client->entry,
					enc_data.etype, &pa_key) == 0)
		    goto try_next_key;
		e_text = "Failed to decrypt PA-DATA";

		free_EncryptedData(&enc_data);

		if (clientdb->hdb_auth_status)
		    (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD);

		ret = KRB5KDC_ERR_PREAUTH_FAILED;
		continue;
	    }
	    free_EncryptedData(&enc_data);
	    ret = decode_PA_ENC_TS_ENC(ts_data.data,
				       ts_data.length,
				       &p,
				       &len);
	    krb5_data_free(&ts_data);
	    if(ret){
		e_text = "Failed to decode PA-ENC-TS-ENC";
		ret = KRB5KDC_ERR_PREAUTH_FAILED;
		kdc_log(context, config,
			5, "Failed to decode PA-ENC-TS_ENC -- %s",
			client_name);
		continue;
	    }
	    free_PA_ENC_TS_ENC(&p);
	    if (abs(kdc_time - p.patimestamp) > context->max_skew) {
		char client_time[100];

		krb5_format_time(context, p.patimestamp,
				 client_time, sizeof(client_time), TRUE);

 		ret = KRB5KRB_AP_ERR_SKEW;
 		kdc_log(context, config, 0,
			"Too large time skew, "
			"client time %s is out by %u > %u seconds -- %s",
			client_time,
			(unsigned)abs(kdc_time - p.patimestamp),
			context->max_skew,
			client_name);

		/*
		 * The following is needed to make windows clients to
		 * retry using the timestamp in the error message, if
		 * there is a e_text, they become unhappy.
		 */
		e_text = NULL;
		goto out;
	    }
	    et.flags.pre_authent = 1;

	    set_salt_padata(rep.padata, pa_key->salt);

	    reply_key = &pa_key->key;

	    ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str);
	    if (ret)
		str = NULL;

	    kdc_log(context, config, 2,
		    "ENC-TS Pre-authentication succeeded -- %s using %s",
		    client_name, str ? str : "unknown enctype");
	    free(str);
	    break;
	}
#ifdef PKINIT
    preauth_done:
#endif
	if(found_pa == 0 && config->require_preauth)
	    goto use_pa;
	/* We come here if we found a pa-enc-timestamp, but if there
           was some problem with it, other than too large skew */
	if(found_pa && et.flags.pre_authent == 0){
	    kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
	    e_text = NULL;
	    goto out;
	}
    }else if (config->require_preauth
	      || b->kdc_options.request_anonymous /* hack to force anon */
	      || client->entry.flags.require_preauth
	      || server->entry.flags.require_preauth) {
	METHOD_DATA method_data;
	PA_DATA *pa;
	unsigned char *buf;
	size_t len;

    use_pa:
	method_data.len = 0;
	method_data.val = NULL;

	ret = realloc_method_data(&method_data);
	if (ret) {
	    free_METHOD_DATA(&method_data);
	    goto out;
	}
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_ENC_TIMESTAMP;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;

#ifdef PKINIT
	ret = realloc_method_data(&method_data);
	if (ret) {
	    free_METHOD_DATA(&method_data);
	    goto out;
	}
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_PK_AS_REQ;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;

	ret = realloc_method_data(&method_data);
	if (ret) {
	    free_METHOD_DATA(&method_data);
	    goto out;
	}
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_PK_AS_REQ_WIN;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;
#endif

	/*
	 * If there is a client key, send ETYPE_INFO{,2}
	 */
	ret = _kdc_find_etype(context,
			      config->preauth_use_strongest_session_key, TRUE,
			      client, b->etype.val, b->etype.len, NULL, &ckey);
	if (ret == 0) {

	    /*
	     * RFC4120 requires:
	     * - If the client only knows about old enctypes, then send
	     *   both info replies (we send 'info' first in the list).
	     * - If the client is 'modern', because it knows about 'new'
	     *   enctype types, then only send the 'info2' reply.
	     *
	     * Before we send the full list of etype-info data, we pick
	     * the client key we would have used anyway below, just pick
	     * that instead.
	     */

	    if (older_enctype(ckey->key.keytype)) {
		ret = get_pa_etype_info(context, config,
					&method_data, ckey);
		if (ret) {
		    free_METHOD_DATA(&method_data);
		    goto out;
		}
	    }
	    ret = get_pa_etype_info2(context, config,
				     &method_data, ckey);
	    if (ret) {
		free_METHOD_DATA(&method_data);
		goto out;
	    }
	}

	ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
	free_METHOD_DATA(&method_data);

	e_data.data   = buf;
	e_data.length = len;
	e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",

	ret = KRB5KDC_ERR_PREAUTH_REQUIRED;

	kdc_log(context, config, 0,
		"No preauth found, returning PREAUTH-REQUIRED -- %s",
		client_name);
	goto out;
    }

    /*
     * Verify flags after the user been required to prove its identity
     * with in a preauth mech.
     */

    ret = _kdc_check_access(context, config, client, client_name,
			    server, server_name,
			    req, &e_data);
    if(ret)
	goto out;

    if (clientdb->hdb_auth_status)
	(clientdb->hdb_auth_status)(context, clientdb, client,
				    HDB_AUTH_SUCCESS);

    /*
     * Selelct the best encryption type for the KDC with out regard to
     * the client since the client never needs to read that data.
     */

    ret = _kdc_get_preferred_key(context, config,
				 server, server_name,
				 &setype, &skey);
    if(ret)
	goto out;

    if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
       || (f.request_anonymous && !config->allow_anonymous)) {
	ret = KRB5KDC_ERR_BADOPTION;
	e_text = "Bad KDC options";
	kdc_log(context, config, 0, "Bad KDC options -- %s", client_name);
	goto out;
    }

    rep.pvno = 5;
    rep.msg_type = krb_as_rep;

    ret = copy_Realm(&client->entry.principal->realm, &rep.crealm);
    if (ret)
	goto out;
    ret = _krb5_principal2principalname(&rep.cname, client->entry.principal);
    if (ret)
	goto out;

    rep.ticket.tkt_vno = 5;
    copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
    _krb5_principal2principalname(&rep.ticket.sname,
				  server->entry.principal);
    /* java 1.6 expects the name to be the same type, lets allow that
     * uncomplicated name-types. */
#define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
    if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST))
	rep.ticket.sname.name_type = b->sname->name_type;
#undef CNT

    et.flags.initial = 1;
    if(client->entry.flags.forwardable && server->entry.flags.forwardable)
	et.flags.forwardable = f.forwardable;
    else if (f.forwardable) {
	e_text = "Ticket may not be forwardable";
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0,
		"Ticket may not be forwardable -- %s", client_name);
	goto out;
    }
    if(client->entry.flags.proxiable && server->entry.flags.proxiable)
	et.flags.proxiable = f.proxiable;
    else if (f.proxiable) {
	e_text = "Ticket may not be proxiable";
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0,
		"Ticket may not be proxiable -- %s", client_name);
	goto out;
    }
    if(client->entry.flags.postdate && server->entry.flags.postdate)
	et.flags.may_postdate = f.allow_postdate;
    else if (f.allow_postdate){
	e_text = "Ticket may not be postdate";
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0,
		"Ticket may not be postdatable -- %s", client_name);
	goto out;
    }

    /* check for valid set of addresses */
    if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
	e_text = "Bad address list in requested";
	ret = KRB5KRB_AP_ERR_BADADDR;
	kdc_log(context, config, 0,
		"Bad address list requested -- %s", client_name);
	goto out;
    }

    ret = copy_PrincipalName(&rep.cname, &et.cname);
    if (ret)
	goto out;
    ret = copy_Realm(&rep.crealm, &et.crealm);
    if (ret)
	goto out;

    {
	time_t start;
	time_t t;

	start = et.authtime = kdc_time;

	if(f.postdated && req->req_body.from){
	    ALLOC(et.starttime);
	    start = *et.starttime = *req->req_body.from;
	    et.flags.invalid = 1;
	    et.flags.postdated = 1; /* XXX ??? */
	}
	_kdc_fix_time(&b->till);
	t = *b->till;

	/* be careful not overflowing */

	if(client->entry.max_life)
	    t = start + min(t - start, *client->entry.max_life);
	if(server->entry.max_life)
	    t = start + min(t - start, *server->entry.max_life);
#if 0
	t = min(t, start + realm->max_life);
#endif
	et.endtime = t;
	if(f.renewable_ok && et.endtime < *b->till){
	    f.renewable = 1;
	    if(b->rtime == NULL){
		ALLOC(b->rtime);
		*b->rtime = 0;
	    }
	    if(*b->rtime < *b->till)
		*b->rtime = *b->till;
	}
	if(f.renewable && b->rtime){
	    t = *b->rtime;
	    if(t == 0)
		t = MAX_TIME;
	    if(client->entry.max_renew)
		t = start + min(t - start, *client->entry.max_renew);
	    if(server->entry.max_renew)
		t = start + min(t - start, *server->entry.max_renew);
#if 0
	    t = min(t, start + realm->max_renew);
#endif
	    ALLOC(et.renew_till);
	    *et.renew_till = t;
	    et.flags.renewable = 1;
	}
    }

    if (f.request_anonymous)
	et.flags.anonymous = 1;

    if(b->addresses){
	ALLOC(et.caddr);
	copy_HostAddresses(b->addresses, et.caddr);
    }

    et.transited.tr_type = DOMAIN_X500_COMPRESS;
    krb5_data_zero(&et.transited.contents);

    /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded
     * as 0 and as 0x80 (meaning indefinite length) apart, and is thus
     * incapable of correctly decoding SEQUENCE OF's of zero length.
     *
     * To fix this, always send at least one no-op last_req
     *
     * If there's a pw_end or valid_end we will use that,
     * otherwise just a dummy lr.
     */
    ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
    if (ek.last_req.val == NULL) {
	ret = ENOMEM;
	goto out;
    }
    ek.last_req.len = 0;
    if (client->entry.pw_end
	&& (config->kdc_warn_pwexpire == 0
	    || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_PW_EXPTIME;
	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
	++ek.last_req.len;
    }
    if (client->entry.valid_end) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_ACCT_EXPTIME;
	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
	++ek.last_req.len;
    }
    if (ek.last_req.len == 0) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_NONE;
	ek.last_req.val[ek.last_req.len].lr_value = 0;
	++ek.last_req.len;
    }
    ek.nonce = b->nonce;
    if (client->entry.valid_end || client->entry.pw_end) {
	ALLOC(ek.key_expiration);
	if (client->entry.valid_end) {
	    if (client->entry.pw_end)
		*ek.key_expiration = min(*client->entry.valid_end,
					 *client->entry.pw_end);
	    else
		*ek.key_expiration = *client->entry.valid_end;
	} else
	    *ek.key_expiration = *client->entry.pw_end;
    } else
	ek.key_expiration = NULL;
    ek.flags = et.flags;
    ek.authtime = et.authtime;
    if (et.starttime) {
	ALLOC(ek.starttime);
	*ek.starttime = *et.starttime;
    }
    ek.endtime = et.endtime;
    if (et.renew_till) {
	ALLOC(ek.renew_till);
	*ek.renew_till = *et.renew_till;
    }
    copy_Realm(&rep.ticket.realm, &ek.srealm);
    copy_PrincipalName(&rep.ticket.sname, &ek.sname);
    if(et.caddr){
	ALLOC(ek.caddr);
	copy_HostAddresses(et.caddr, ek.caddr);
    }

#if PKINIT
    if (pkp) {
        e_text = "Failed to build PK-INIT reply";
	ret = _kdc_pk_mk_pa_reply(context, config, pkp, client,
				  sessionetype, req, req_buffer,
				  &reply_key, &et.key, rep.padata);
	if (ret)
	    goto out;
	ret = _kdc_add_inital_verified_cas(context,
					   config,
					   pkp,
					   &et);
	if (ret)
	    goto out;

    } else
#endif
    {
	ret = krb5_generate_random_keyblock(context, sessionetype, &et.key);
	if (ret)
	    goto out;
    }

    if (reply_key == NULL) {
	e_text = "Client have no reply key";
	ret = KRB5KDC_ERR_CLIENT_NOTYET;
	goto out;
    }

    ret = copy_EncryptionKey(&et.key, &ek.key);
    if (ret)
	goto out;

    if (rep.padata->len == 0) {
	free(rep.padata);
	rep.padata = NULL;
    }

    /* Add the PAC */
    if (send_pac_p(context, req)) {
	krb5_pac p = NULL;
	krb5_data data;

	ret = _kdc_pac_generate(context, client, &p);
	if (ret) {
	    kdc_log(context, config, 0, "PAC generation failed for -- %s",
		    client_name);
	    goto out;
	}
	if (p != NULL) {
	    ret = _krb5_pac_sign(context, p, et.authtime,
				 client->entry.principal,
				 &skey->key, /* Server key */
				 &skey->key, /* FIXME: should be krbtgt key */
				 &data);
	    krb5_pac_free(context, p);
	    if (ret) {
		kdc_log(context, config, 0, "PAC signing failed for -- %s",
			client_name);
		goto out;
	    }

	    ret = _kdc_tkt_add_if_relevant_ad(context, &et,
					      KRB5_AUTHDATA_WIN2K_PAC,
					      &data);
	    krb5_data_free(&data);
	    if (ret)
		goto out;
	}
    }

    _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
		       et.endtime, et.renew_till);

    /* do this as the last thing since this signs the EncTicketPart */
    ret = _kdc_add_KRB5SignedPath(context,
				  config,
				  server,
				  setype,
				  client->entry.principal,
				  NULL,
				  NULL,
				  &et);
    if (ret)
	goto out;

    log_as_req(context, config, reply_key->keytype, setype, b);

    ret = _kdc_encode_reply(context, config,
			    &rep, &et, &ek, setype, server->entry.kvno,
			    &skey->key, client->entry.kvno,
			    reply_key, 0, &e_text, reply);
    free_EncTicketPart(&et);
    free_EncKDCRepPart(&ek);
    if (ret)
	goto out;

    /* */
    if (datagram_reply && reply->length > config->max_datagram_reply_length) {
	krb5_data_free(reply);
	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
	e_text = "Reply packet too large";
    }

out:
    free_AS_REP(&rep);
    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){
	krb5_mk_error(context,
		      ret,
		      e_text,
		      (e_data.data ? &e_data : NULL),
		      client_princ,
		      server_princ,
		      NULL,
		      NULL,
		      reply);
	ret = 0;
    }
#ifdef PKINIT
    if (pkp)
	_kdc_pk_free_client_param(context, pkp);
#endif
    if (e_data.data)
        free(e_data.data);
    if (client_princ)
	krb5_free_principal(context, client_princ);
    free(client_name);
    if (server_princ)
	krb5_free_principal(context, server_princ);
    free(server_name);
    if(client)
	_kdc_free_ent(context, client);
    if(server)
	_kdc_free_ent(context, server);
    return ret;
}
Example #19
0
int main(int argc, const char *argv[])
{
	static const char *server = NULL;
	static const char *principal = NULL;
	static const char *keytab = NULL;
	static const char *enctypes_string = NULL;
	static const char *binddn = NULL;
	static const char *bindpw = NULL;
	char *ldap_uri = NULL;
	static const char *sasl_mech = NULL;
	static const char *ca_cert_file = NULL;
	int quiet = 0;
	int askpass = 0;
	int permitted_enctypes = 0;
	int retrieve = 0;
        struct poptOption options[] = {
            { "quiet", 'q', POPT_ARG_NONE, &quiet, 0,
              _("Print as little as possible"), _("Output only on errors")},
            { "server", 's', POPT_ARG_STRING, &server, 0,
              _("Contact this specific KDC Server"),
              _("Server Name") },
            { "principal", 'p', POPT_ARG_STRING, &principal, 0,
              _("The principal to get a keytab for (ex: ftp/[email protected])"),
              _("Kerberos Service Principal Name") },
            { "keytab", 'k', POPT_ARG_STRING, &keytab, 0,
              _("The keytab file to append the new key to (will be "
                "created if it does not exist)."),
              _("Keytab File Name") },
	    { "enctypes", 'e', POPT_ARG_STRING, &enctypes_string, 0,
              _("Encryption types to request"),
              _("Comma separated encryption types list") },
	    { "permitted-enctypes", 0, POPT_ARG_NONE, &permitted_enctypes, 0,
              _("Show the list of permitted encryption types and exit"),
              _("Permitted Encryption Types") },
	    { "password", 'P', POPT_ARG_NONE, &askpass, 0,
              _("Asks for a non-random password to use for the principal"), NULL },
	    { "binddn", 'D', POPT_ARG_STRING, &binddn, 0,
              _("LDAP DN"), _("DN to bind as if not using kerberos") },
	    { "bindpw", 'w', POPT_ARG_STRING, &bindpw, 0,
              _("LDAP password"), _("password to use if not using kerberos") },
	    { "cacert", 0, POPT_ARG_STRING, &ca_cert_file, 0,
              _("Path to the IPA CA certificate"), _("IPA CA certificate")},
	    { "ldapuri", 'H', POPT_ARG_STRING, &ldap_uri, 0,
              _("LDAP uri to connect to. Mutually exclusive with --server"),
              _("url")},
	    { "mech", 'Y', POPT_ARG_STRING, &sasl_mech, 0,
              _("LDAP SASL bind mechanism if no bindd/bindpw"),
              _("GSSAPI|EXTERNAL") },
	    { "retrieve", 'r', POPT_ARG_NONE, &retrieve, 0,
              _("Retrieve current keys without changing them"), NULL },
            POPT_AUTOHELP
            POPT_TABLEEND
	};
	poptContext pc;
	char *ktname;
	char *password = NULL;
	krb5_context krbctx;
	krb5_ccache ccache;
	krb5_principal uprinc = NULL;
	krb5_principal sprinc;
	krb5_error_code krberr;
	struct keys_container keys = { 0 };
	krb5_keytab kt;
	int kvno;
	int i, ret;
	char *err_msg;

    ret = init_gettext();
    if (ret) {
        fprintf(stderr, "Failed to load translations\n");
    }

	krberr = krb5_init_context(&krbctx);
	if (krberr) {
		fprintf(stderr, _("Kerberos context initialization failed\n"));
		exit(1);
	}

	pc = poptGetContext("ipa-getkeytab", argc, (const char **)argv, options, 0);
	ret = poptGetNextOpt(pc);
	if (ret == -1 && permitted_enctypes &&
	    !(server || principal || keytab || quiet)) {
		krb5_enctype *ktypes;
		char enc[79]; /* fit std terminal or truncate */

		krberr = krb5_get_permitted_enctypes(krbctx, &ktypes);
		if (krberr) {
			fprintf(stderr, _("No system preferred enctypes ?!\n"));
			exit(1);
		}
		fprintf(stdout, _("Supported encryption types:\n"));
		for (i = 0; ktypes[i]; i++) {
			krberr = krb5_enctype_to_string(ktypes[i], enc, 79);
			if (krberr) {
				fprintf(stderr, _("Warning: "
                                        "failed to convert type (#%d)\n"), i);
				continue;
			}
			fprintf(stdout, "%s\n", enc);
		}
		ipa_krb5_free_ktypes(krbctx, ktypes);
		exit (0);
	}

	if (ret != -1 || !principal || !keytab || permitted_enctypes) {
		if (!quiet) {
			poptPrintUsage(pc, stderr, 0);
		}
		exit(2);
	}

	if (NULL!=binddn && NULL==bindpw) {
		fprintf(stderr,
                        _("Bind password required when using a bind DN.\n"));
		if (!quiet)
			poptPrintUsage(pc, stderr, 0);
		exit(10);
	}

    if (NULL != binddn && NULL != sasl_mech) {
        fprintf(stderr, _("Cannot specify both SASL mechanism "
                          "and bind DN simultaneously.\n"));
        if (!quiet)
            poptPrintUsage(pc, stderr, 0);
        exit(2);
    }

    if (sasl_mech && check_sasl_mech(sasl_mech)) {
        fprintf(stderr, _("Invalid SASL bind mechanism\n"));
        if (!quiet)
            poptPrintUsage(pc, stderr, 0);
        exit(2);
    }

    if (!binddn && !sasl_mech) {
        sasl_mech = LDAP_SASL_GSSAPI;
    }

    if (server && ldap_uri) {
        fprintf(stderr, _("Cannot specify server and LDAP uri "
                          "simultaneously.\n"));
        if (!quiet)
            poptPrintUsage(pc, stderr, 0);
        exit(2);
    }

    if (!server && !ldap_uri) {
        struct ipa_config *ipacfg = NULL;

        ret = read_ipa_config(&ipacfg);
        if (ret == 0) {
            server = ipacfg->server_name;
            ipacfg->server_name = NULL;
        }
        free(ipacfg);
        if (!server) {
            fprintf(stderr, _("Server name not provided and unavailable\n"));
            exit(2);
        }
    }
    if (server) {
        ret = ipa_server_to_uri(server, sasl_mech, &ldap_uri);
        if (ret) {
            exit(ret);
        }
    }

    if (!ca_cert_file) {
        ca_cert_file = DEFAULT_CA_CERT_FILE;
    }

    if (askpass && retrieve) {
        fprintf(stderr, _("Incompatible options provided (-r and -P)\n"));
        exit(2);
    }

        if (askpass) {
		password = ask_password(krbctx);
		if (!password) {
			exit(2);
		}
	} else if (enctypes_string && strchr(enctypes_string, ':')) {
		if (!quiet) {
			fprintf(stderr, _("Warning: salt types are not honored"
                                " with randomized passwords (see opt. -P)\n"));
		}
	}

	ret = asprintf(&ktname, "WRFILE:%s", keytab);
	if (ret == -1) {
		exit(3);
	}

	krberr = krb5_parse_name(krbctx, principal, &sprinc);
	if (krberr) {
		fprintf(stderr, _("Invalid Service Principal Name\n"));
		exit(4);
	}

	if (NULL == bindpw && strcmp(sasl_mech, LDAP_SASL_GSSAPI) == 0) {
		krberr = krb5_cc_default(krbctx, &ccache);
		if (krberr) {
			fprintf(stderr,
                                _("Kerberos Credential Cache not found. "
				  "Do you have a Kerberos Ticket?\n"));
			exit(5);
		}

		krberr = krb5_cc_get_principal(krbctx, ccache, &uprinc);
		if (krberr) {
			fprintf(stderr,
                                _("Kerberos User Principal not found. "
				  "Do you have a valid Credential Cache?\n"));
			exit(6);
		}
	}

	krberr = krb5_kt_resolve(krbctx, ktname, &kt);
	if (krberr) {
		fprintf(stderr, _("Failed to open Keytab\n"));
		exit(7);
	}

    kvno = -1;
    ret = ldap_get_keytab(krbctx, (retrieve == 0), password, enctypes_string,
                          ldap_uri, principal, uprinc, binddn, bindpw,
                          sasl_mech, ca_cert_file,
                          &keys, &kvno, &err_msg);
    if (ret) {
        if (!quiet && err_msg != NULL) {
            fprintf(stderr, "%s", err_msg);
        }
    }

    if (retrieve == 0 && kvno == -1) {
        if (!quiet) {
            fprintf(stderr,
                    _("Retrying with pre-4.0 keytab retrieval method...\n"));
        }

        /* create key material */
        ret = create_keys(krbctx, sprinc, password, enctypes_string, &keys, &err_msg);
        if (!ret) {
            if (err_msg != NULL) {
                fprintf(stderr, "%s", err_msg);
            }

            fprintf(stderr, _("Failed to create key material\n"));
            exit(8);
        }

        kvno = ldap_set_keytab(krbctx, ldap_uri, principal, uprinc, binddn,
                               bindpw, sasl_mech, ca_cert_file, &keys);
    }

    if (kvno == -1) {
        fprintf(stderr, _("Failed to get keytab\n"));
        exit(9);
    }

	for (i = 0; i < keys.nkeys; i++) {
		krb5_keytab_entry kt_entry;
		memset((char *)&kt_entry, 0, sizeof(kt_entry));
		kt_entry.principal = sprinc;
		kt_entry.key = keys.ksdata[i].key;
		kt_entry.vno = kvno;

		krberr = krb5_kt_add_entry(krbctx, kt, &kt_entry);
		if (krberr) {
			fprintf(stderr,
                                _("Failed to add key to the keytab\n"));
			exit (11);
		}
	}

	free_keys_contents(krbctx, &keys);

	krberr = krb5_kt_close(krbctx, kt);
	if (krberr) {
		fprintf(stderr, _("Failed to close the keytab\n"));
		exit (12);
	}

	if (!quiet) {
		fprintf(stderr,
			_("Keytab successfully retrieved and stored in: %s\n"),
			keytab);
	}
	exit(0);
}
Example #20
0
static int ldap_set_keytab(krb5_context krbctx,
			   const char *ldap_uri,
			   const char *principal_name,
			   krb5_principal princ,
			   const char *binddn,
			   const char *bindpw,
			   const char *mech,
			   const char *ca_cert_file,
			   struct keys_container *keys)
{
	LDAP *ld = NULL;
	BerElement *sctrl = NULL;
	struct berval *control = NULL;
	LDAPControl **srvctrl = NULL;
	int ret;
	int kvno, i;
	ber_tag_t rtag;
	ber_int_t *encs = NULL;
	int successful_keys = 0;

	/* cant' return more than nkeys, sometimes less */
	encs = calloc(keys->nkeys + 1, sizeof(ber_int_t));
	if (!encs) {
		fprintf(stderr, _("Out of Memory!\n"));
		return 0;
	}

	/* build password change control */
	control = create_key_control(keys, principal_name);
	if (!control) {
		fprintf(stderr, _("Failed to create control!\n"));
		goto error_out;
	}

    ret = ipa_ldap_bind(ldap_uri, princ, binddn, bindpw, mech, ca_cert_file, &ld);
    if (ret != LDAP_SUCCESS) {
        fprintf(stderr, _("Failed to bind to server!\n"));
        goto error_out;
    }

    /* perform password change */
    ret = ipa_ldap_extended_op(ld, KEYTAB_SET_OID, control, &srvctrl);
    if (ret != LDAP_SUCCESS) {
        fprintf(stderr, _("Failed to get keytab!\n"));
        goto error_out;
    }

	ber_bvfree(control);
	control = NULL;

	sctrl = get_control_data(srvctrl, KEYTAB_RET_OID);
	if (!sctrl) {
		fprintf(stderr, _("ber_init() failed, Invalid control ?!\n"));
		goto error_out;
	}

	/* Format of response
	 *
	 * KeytabGetRequest ::= SEQUENCE {
	 * 	new_kvno	Int32
	 * 	SEQUENCE OF	KeyTypes
	 * }
	 *
	 * * List of accepted enctypes *
	 * KeyTypes ::= SEQUENCE {
	 * 	enctype		Int32
	 * }
	 */

	rtag = ber_scanf(sctrl, "{i{", &kvno);
	if (rtag == LBER_ERROR) {
		fprintf(stderr, _("ber_scanf() failed, unable to find kvno ?!\n"));
		goto error_out;
	}

	for (i = 0; i < keys->nkeys; i++) {
		ret = ber_scanf(sctrl, "{i}", &encs[i]);
		if (ret == LBER_ERROR) {
			char enc[79]; /* fit std terminal or truncate */
			krb5_error_code krberr;
			krberr = krb5_enctype_to_string(
				keys->ksdata[i].enctype, enc, 79);
			if (krberr) {
				fprintf(stderr, _("Failed to retrieve "
					"encryption type type #%d\n"),
					keys->ksdata[i].enctype);
			} else {
				fprintf(stderr, _("Failed to retrieve "
					"encryption type %1$s (#%2$d)\n"),
					enc, keys->ksdata[i].enctype);
			}
                } else {
			successful_keys++;
		}
	}

	if (successful_keys == 0) {
		fprintf(stderr, _("Failed to retrieve any keys"));
		goto error_out;
	}

	ret = filter_keys(krbctx, keys, encs);
	if (ret == 0) goto error_out;

	ber_free(sctrl, 1);
	ldap_controls_free(srvctrl);
	ldap_unbind_ext(ld, NULL, NULL);
	free(encs);
	return kvno;

error_out:
	if (sctrl) ber_free(sctrl, 1);
	if (srvctrl) ldap_controls_free(srvctrl);
	if (ld) ldap_unbind_ext(ld, NULL, NULL);
	if (control) ber_bvfree(control);
	free(encs);
	return -1;
}
Example #21
0
static krb5_error_code
tgs_parse_request(krb5_context context, 
		  krb5_kdc_configuration *config,
		  KDC_REQ_BODY *b,
		  const PA_DATA *tgs_req,
		  hdb_entry_ex **krbtgt,
		  krb5_enctype *krbtgt_etype,
		  krb5_ticket **ticket,
		  const char **e_text,
		  const char *from,
		  const struct sockaddr *from_addr,
		  time_t **csec,
		  int **cusec,
		  AuthorizationData **auth_data)
{
    krb5_ap_req ap_req;
    krb5_error_code ret;
    krb5_principal princ;
    krb5_auth_context ac = NULL;
    krb5_flags ap_req_options;
    krb5_flags verify_ap_req_flags;
    krb5_crypto crypto;
    Key *tkey;

    *auth_data = NULL;
    *csec  = NULL;
    *cusec = NULL;

    memset(&ap_req, 0, sizeof(ap_req));
    ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
    if(ret){
	kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", 
		krb5_get_err_text(context, ret));
	goto out;
    }

    if(!get_krbtgt_realm(&ap_req.ticket.sname)){
	/* XXX check for ticket.sname == req.sname */
	kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
	ret = KRB5KDC_ERR_POLICY; /* ? */
	goto out;
    }
    
    _krb5_principalname2krb5_principal(context,
				       &princ,
				       ap_req.ticket.sname,
				       ap_req.ticket.realm);
    
    ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);

    if(ret) {
	char *p;
	ret = krb5_unparse_name(context, princ, &p);
	if (ret != 0)
	    p = "<unparse_name failed>";
	krb5_free_principal(context, princ);
	kdc_log(context, config, 0,
		"Ticket-granting ticket not found in database: %s: %s",
		p, krb5_get_err_text(context, ret));
	if (ret == 0)
	    free(p);
	ret = KRB5KRB_AP_ERR_NOT_US;
	goto out;
    }
    
    if(ap_req.ticket.enc_part.kvno && 
       *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
	char *p;

	ret = krb5_unparse_name (context, princ, &p);
	krb5_free_principal(context, princ);
	if (ret != 0)
	    p = "<unparse_name failed>";
	kdc_log(context, config, 0,
		"Ticket kvno = %d, DB kvno = %d (%s)", 
		*ap_req.ticket.enc_part.kvno,
		(*krbtgt)->entry.kvno,
		p);
	if (ret == 0)
	    free (p);
	ret = KRB5KRB_AP_ERR_BADKEYVER;
	goto out;
    }

    *krbtgt_etype = ap_req.ticket.enc_part.etype;

    ret = hdb_enctype2key(context, &(*krbtgt)->entry, 
			  ap_req.ticket.enc_part.etype, &tkey);
    if(ret){
	char *str, *p;
	krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
	krb5_unparse_name(context, princ, &p);
	kdc_log(context, config, 0,
		"No server key with enctype %s found for %s", str, p);
	free(str);
	free(p);
	ret = KRB5KRB_AP_ERR_BADKEYVER;
	goto out;
    }
    
    if (b->kdc_options.validate)
	verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
    else
	verify_ap_req_flags = 0;

    ret = krb5_verify_ap_req2(context,
			      &ac,
			      &ap_req,
			      princ,
			      &tkey->key,
			      verify_ap_req_flags,
			      &ap_req_options,
			      ticket,
			      KRB5_KU_TGS_REQ_AUTH);
			     
    krb5_free_principal(context, princ);
    if(ret) {
	kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", 
		krb5_get_err_text(context, ret));
	goto out;
    }

    {
	krb5_authenticator auth;

	ret = krb5_auth_con_getauthenticator(context, ac, &auth);
	if (ret == 0) {
	    *csec   = malloc(sizeof(**csec));
	    if (*csec == NULL) {
		krb5_free_authenticator(context, &auth);
		kdc_log(context, config, 0, "malloc failed");
		goto out;
	    }
	    **csec  = auth->ctime;
	    *cusec  = malloc(sizeof(**cusec));
	    if (*cusec == NULL) {
		krb5_free_authenticator(context, &auth);
		kdc_log(context, config, 0, "malloc failed");
		goto out;
	    }
	    **cusec  = auth->cusec;
	    krb5_free_authenticator(context, &auth);
	}
    }

    ret = tgs_check_authenticator(context, config, 
				  ac, b, e_text, &(*ticket)->ticket.key);
    if (ret) {
	krb5_auth_con_free(context, ac);
	goto out;
    }

    if (b->enc_authorization_data) {
	krb5_keyblock *subkey;
	krb5_data ad;
	ret = krb5_auth_con_getremotesubkey(context,
					    ac,
					    &subkey);
	if(ret){
	    krb5_auth_con_free(context, ac);
	    kdc_log(context, config, 0, "Failed to get remote subkey: %s", 
		    krb5_get_err_text(context, ret));
	    goto out;
	}
	if(subkey == NULL){
	    ret = krb5_auth_con_getkey(context, ac, &subkey);
	    if(ret) {
		krb5_auth_con_free(context, ac);
		kdc_log(context, config, 0, "Failed to get session key: %s", 
			krb5_get_err_text(context, ret));
		goto out;
	    }
	}
	if(subkey == NULL){
	    krb5_auth_con_free(context, ac);
	    kdc_log(context, config, 0,
		    "Failed to get key for enc-authorization-data");
	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
	    goto out;
	}
	ret = krb5_crypto_init(context, subkey, 0, &crypto);
	if (ret) {
	    krb5_auth_con_free(context, ac);
	    kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
		    krb5_get_err_text(context, ret));
	    goto out;
	}
	ret = krb5_decrypt_EncryptedData (context,
					  crypto,
					  KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
					  b->enc_authorization_data,
					  &ad);
	krb5_crypto_destroy(context, crypto);
	if(ret){
	    krb5_auth_con_free(context, ac);
	    kdc_log(context, config, 0, 
		    "Failed to decrypt enc-authorization-data");
	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
	    goto out;
	}
	krb5_free_keyblock(context, subkey);
	ALLOC(*auth_data);
	if (*auth_data == NULL) {
	    krb5_auth_con_free(context, ac);
	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
	    goto out;
	}
	ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
	if(ret){
	    krb5_auth_con_free(context, ac);
	    free(*auth_data);
	    *auth_data = NULL;
	    kdc_log(context, config, 0, "Failed to decode authorization data");
	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
	    goto out;
	}
    }

    krb5_auth_con_free(context, ac);
    
out:
    free_AP_REQ(&ap_req);
    
    return ret;
}
Example #22
0
static void
print_cred_verbose(krb5_context context, krb5_creds *cred)
{
    int j;
    char *str;
    krb5_error_code ret;
    krb5_timestamp sec;

    krb5_timeofday (context, &sec);

    ret = krb5_unparse_name(context, cred->server, &str);
    if(ret)
	exit(1);
    printf(N_("Server: %s\n", ""), str);
    free (str);
    
    ret = krb5_unparse_name(context, cred->client, &str);
    if(ret)
	exit(1);
    printf(N_("Client: %s\n", ""), str);
    free (str);
    
    {
	Ticket t;
	size_t len;
	char *s;
	
	decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len);
	ret = krb5_enctype_to_string(context, t.enc_part.etype, &s);
	printf(N_("Ticket etype: ", ""));
	if (ret == 0) {
	    printf("%s", s);
	    free(s);
	} else {
	    printf(N_("unknown-enctype(%d)", ""), t.enc_part.etype);
	}
	if(t.enc_part.kvno)
	    printf(N_(", kvno %d", ""), *t.enc_part.kvno);
	printf("\n");
	if(cred->session.keytype != t.enc_part.etype) {
	    ret = krb5_enctype_to_string(context, cred->session.keytype, &str);
	    if(ret)
		krb5_warn(context, ret, "session keytype");
	    else {
		printf(N_("Session key: %s\n", "enctype"), str);
		free(str);
	    }
	}
	free_Ticket(&t);
	printf(N_("Ticket length: %lu\n", ""),
	       (unsigned long)cred->ticket.length);
    }
    printf(N_("Auth time:  %s\n", ""),
	   printable_time_long(cred->times.authtime));
    if(cred->times.authtime != cred->times.starttime)
	printf(N_("Start time: %s\n", ""),
	       printable_time_long(cred->times.starttime));
    printf(N_("End time:   %s", ""),
	   printable_time_long(cred->times.endtime));
    if(sec > cred->times.endtime)
	printf(N_(" (expired)", ""));
    printf("\n");
    if(cred->flags.b.renewable)
	printf(N_("Renew till: %s\n", ""),
	       printable_time_long(cred->times.renew_till));
    {
	char flags[1024];
	unparse_flags(TicketFlags2int(cred->flags.b), 
		      asn1_TicketFlags_units(),
		      flags, sizeof(flags));
	printf(N_("Ticket flags: %s\n", ""), flags);
    }
    printf(N_("Addresses: ", ""));
    if (cred->addresses.len != 0) {
	for(j = 0; j < cred->addresses.len; j++){
	    char buf[128];
	    size_t len;
	    if(j) printf(", ");
	    ret = krb5_print_address(&cred->addresses.val[j],
				     buf, sizeof(buf), &len);
	
	    if(ret == 0)
		printf("%s", buf);
	}
    } else {
	printf(N_("addressless", ""));
    }
    printf("\n\n");
}
Example #23
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_kt_get_entry(krb5_context context,
		  krb5_keytab id,
		  krb5_const_principal principal,
		  krb5_kvno kvno,
		  krb5_enctype enctype,
		  krb5_keytab_entry *entry)
{
    krb5_keytab_entry tmp;
    krb5_error_code ret;
    krb5_kt_cursor cursor;

    if(id->get)
	return (*id->get)(context, id, principal, kvno, enctype, entry);

    ret = krb5_kt_start_seq_get (context, id, &cursor);
    if (ret) {
	/* This is needed for krb5_verify_init_creds, but keep error
	 * string from previous error for the human. */
	context->error_code = KRB5_KT_NOTFOUND;
	return KRB5_KT_NOTFOUND;
    }

    entry->vno = 0;
    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
	    /* the file keytab might only store the lower 8 bits of
	       the kvno, so only compare those bits */
	    if (kvno == tmp.vno
		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
		krb5_kt_copy_entry_contents (context, &tmp, entry);
		krb5_kt_free_entry (context, &tmp);
		krb5_kt_end_seq_get(context, id, &cursor);
		return 0;
	    } else if (kvno == 0 && tmp.vno > entry->vno) {
		if (entry->vno)
		    krb5_kt_free_entry (context, entry);
		krb5_kt_copy_entry_contents (context, &tmp, entry);
	    }
	}
	krb5_kt_free_entry(context, &tmp);
    }
    krb5_kt_end_seq_get (context, id, &cursor);
    if (entry->vno) {
	return 0;
    } else {
	char princ[256], kvno_str[25], *kt_name;
	char *enctype_str = NULL;

	krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
	krb5_kt_get_full_name (context, id, &kt_name);
	krb5_enctype_to_string(context, enctype, &enctype_str);

	if (kvno)
	    snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
	else
	    kvno_str[0] = '\0';

	krb5_set_error_message (context, KRB5_KT_NOTFOUND,
				N_("Failed to find %s%s in keytab %s (%s)",
				   "principal, kvno, keytab file, enctype"),
				princ,
				kvno_str,
				kt_name ? kt_name : "unknown keytab",
				enctype_str ? enctype_str : "unknown enctype");
	free(kt_name);
	free(enctype_str);
	return KRB5_KT_NOTFOUND;
    }
}
Example #24
0
static int
do_list(struct list_options *opt, const char *keytab_str)
{
    krb5_error_code ret;
    krb5_keytab keytab;
    krb5_keytab_entry entry;
    krb5_kt_cursor cursor;
    rtbl_t table;

    /* XXX specialcase the ANY type */
    if(strncasecmp(keytab_str, "ANY:", 4) == 0) {
	int flag = 0;
	char buf[1024];
	keytab_str += 4;
	ret = 0;
	while (strsep_copy((const char**)&keytab_str, ",", 
			   buf, sizeof(buf)) != -1) {
	    if(flag)
		printf("\n");
	    if(do_list(opt, buf))
		ret = 1;
	    flag = 1;
	}
	return ret;
    }

    ret = krb5_kt_resolve(context, keytab_str, &keytab);
    if (ret) {
	krb5_warn(context, ret, "resolving keytab %s", keytab_str);
	return ret;
    }

    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    if(ret) {
	krb5_warn(context, ret, "krb5_kt_start_seq_get %s", keytab_str);
	krb5_kt_close(context, keytab);
	return ret;
    }

    printf ("%s:\n\n", keytab_str);
	
    table = rtbl_create();
    rtbl_add_column_by_id(table, 0, "Vno", RTBL_ALIGN_RIGHT);
    rtbl_add_column_by_id(table, 1, "Type", 0);
    rtbl_add_column_by_id(table, 2, "Principal", 0);
    if (opt->timestamp_flag)
	rtbl_add_column_by_id(table, 3, "Date", 0);
    if(opt->keys_flag)
	rtbl_add_column_by_id(table, 4, "Key", 0);
    rtbl_set_separator(table, "  ");

    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
	char buf[1024], *s;

	snprintf(buf, sizeof(buf), "%d", entry.vno);
	rtbl_add_column_entry_by_id(table, 0, buf);

	ret = krb5_enctype_to_string(context, 
				     entry.keyblock.keytype, &s);
	if (ret != 0) {
	    snprintf(buf, sizeof(buf), "unknown (%d)", entry.keyblock.keytype);
	    rtbl_add_column_entry_by_id(table, 1, buf);
	} else {
	    rtbl_add_column_entry_by_id(table, 1, s);
	    free(s);
	}

	krb5_unparse_name_fixed(context, entry.principal, buf, sizeof(buf));
	rtbl_add_column_entry_by_id(table, 2, buf);

	if (opt->timestamp_flag) {
	    krb5_format_time(context, entry.timestamp, buf, 
			     sizeof(buf), FALSE);
	    rtbl_add_column_entry_by_id(table, 3, buf);
	}
	if(opt->keys_flag) {
	    int i;
	    s = malloc(2 * entry.keyblock.keyvalue.length + 1);
	    if (s == NULL) {
		krb5_warnx(context, "malloc failed");
		ret = ENOMEM;
		goto out;
	    }
	    for(i = 0; i < entry.keyblock.keyvalue.length; i++)
		snprintf(s + 2 * i, 3, "%02x", 
			 ((unsigned char*)entry.keyblock.keyvalue.data)[i]);
	    rtbl_add_column_entry_by_id(table, 4, s);
	    free(s);
	}
	krb5_kt_free_entry(context, &entry);
    }
    ret = krb5_kt_end_seq_get(context, keytab, &cursor);
    rtbl_format(table, stdout);

out:
    rtbl_destroy(table);

    krb5_kt_close(context, keytab);
    return ret;
}
Example #25
0
void
auth_heimdal_gssapi_init(auth_instance *ablock)
{
  krb5_context context;
  krb5_keytab keytab;
  krb5_kt_cursor cursor;
  krb5_keytab_entry entry;
  krb5_error_code krc;
  char *principal, *enctype_s;
  const char *k_keytab_typed_name = NULL;
  auth_heimdal_gssapi_options_block *ob =
    (auth_heimdal_gssapi_options_block *)(ablock->options_block);

  ablock->server = FALSE;
  ablock->client = FALSE;

  if (!ob->server_service || !*ob->server_service) {
    HDEBUG(D_auth) debug_printf("heimdal: missing server_service\n");
    return;
  }

  krc = krb5_init_context(&context);
  if (krc != 0) {
    int kerr = errno;
    HDEBUG(D_auth) debug_printf("heimdal: failed to initialise krb5 context: %s\n",
        strerror(kerr));
    return;
  }

  if (ob->server_keytab) {
    k_keytab_typed_name = CCS string_sprintf("file:%s", expand_string(ob->server_keytab));
    HDEBUG(D_auth) debug_printf("heimdal: using keytab %s\n", k_keytab_typed_name);
    krc = krb5_kt_resolve(context, k_keytab_typed_name, &keytab);
    if (krc) {
      HDEBUG(D_auth) exim_heimdal_error_debug("krb5_kt_resolve", context, krc);
      return;
    }
  } else {
    HDEBUG(D_auth) debug_printf("heimdal: using system default keytab\n");
    krc = krb5_kt_default(context, &keytab);
    if (krc) {
      HDEBUG(D_auth) exim_heimdal_error_debug("krb5_kt_default", context, krc);
      return;
    }
  }

  HDEBUG(D_auth) {
    /* http://www.h5l.org/manual/HEAD/krb5/krb5_keytab_intro.html */
    krc = krb5_kt_start_seq_get(context, keytab, &cursor);
    if (krc)
      exim_heimdal_error_debug("krb5_kt_start_seq_get", context, krc);
    else {
      while ((krc = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
        principal = enctype_s = NULL;
        krb5_unparse_name(context, entry.principal, &principal);
        krb5_enctype_to_string(context, entry.keyblock.keytype, &enctype_s);
        debug_printf("heimdal: keytab principal: %s  vno=%d  type=%s\n",
            principal ? principal : "??",
            entry.vno,
            enctype_s ? enctype_s : "??");
        free(principal);
        free(enctype_s);
        krb5_kt_free_entry(context, &entry);
      }
      krc = krb5_kt_end_seq_get(context, keytab, &cursor);
      if (krc)
        exim_heimdal_error_debug("krb5_kt_end_seq_get", context, krc);
    }
  }

  krc = krb5_kt_close(context, keytab);
  if (krc)
    HDEBUG(D_auth) exim_heimdal_error_debug("krb5_kt_close", context, krc);

  krb5_free_context(context);

  /* RFC 4121 section 5.2, SHOULD support 64K input buffers */
  if (big_buffer_size < (64 * 1024)) {
    uschar *newbuf;
    big_buffer_size = 64 * 1024;
    newbuf = store_malloc(big_buffer_size);
    store_free(big_buffer);
    big_buffer = newbuf;
  }

  ablock->server = TRUE;
}
Example #26
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_kt_get_entry(krb5_context context,
		  krb5_keytab id,
		  krb5_const_principal principal,
		  krb5_kvno kvno,
		  krb5_enctype enctype,
		  krb5_keytab_entry *entry)
{
    krb5_keytab_entry tmp;
    krb5_error_code ret;
    krb5_kt_cursor cursor;

    if(id->get)
	return (*id->get)(context, id, principal, kvno, enctype, entry);

    ret = krb5_kt_start_seq_get (context, id, &cursor);
    if (ret) {
	krb5_clear_error_string(context);
	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
    }

    entry->vno = 0;
    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
	    /* the file keytab might only store the lower 8 bits of
	       the kvno, so only compare those bits */
	    if (kvno == tmp.vno
		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
		krb5_kt_copy_entry_contents (context, &tmp, entry);
		krb5_kt_free_entry (context, &tmp);
		krb5_kt_end_seq_get(context, id, &cursor);
		return 0;
	    } else if (kvno == 0 && tmp.vno > entry->vno) {
		if (entry->vno)
		    krb5_kt_free_entry (context, entry);
		krb5_kt_copy_entry_contents (context, &tmp, entry);
	    }
	}
	krb5_kt_free_entry(context, &tmp);
    }
    krb5_kt_end_seq_get (context, id, &cursor);
    if (entry->vno) {
	return 0;
    } else {
	char princ[256], kt_name[256], kvno_str[25];
	char *enctype_str = NULL;

	krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
	krb5_kt_get_name (context, id, kt_name, sizeof(kt_name));
	krb5_enctype_to_string(context, enctype, &enctype_str);

	if (kvno)
	    snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
	else
	    kvno_str[0] = '\0';

	krb5_set_error_string (context,
 			       "failed to find %s%s in keytab %s (%s)",
			       princ,
			       kvno_str,
			       kt_name,
			       enctype_str ? enctype_str : "unknown enctype");
	free(enctype_str);
	return KRB5_KT_NOTFOUND;
    }
}
Example #27
0
int
kt_copy (void *opt, int argc, char **argv)
{
    krb5_error_code ret;
    krb5_keytab src_keytab, dst_keytab;
    krb5_kt_cursor cursor;
    krb5_keytab_entry entry, dummy;
    const char *from = argv[0];
    const char *to = argv[1];

    ret = krb5_kt_resolve (context, from, &src_keytab);
    if (ret) {
	krb5_warn (context, ret, "resolving src keytab `%s'", from);
	return 1;
    }

    ret = krb5_kt_resolve (context, to, &dst_keytab);
    if (ret) {
	krb5_kt_close (context, src_keytab);
	krb5_warn (context, ret, "resolving dst keytab `%s'", to);
	return 1;
    }

    ret = krb5_kt_start_seq_get (context, src_keytab, &cursor);
    if (ret) {
	krb5_warn (context, ret, "krb5_kt_start_seq_get %s", keytab_string);
	goto out;
    }

    if (verbose_flag)
	fprintf(stderr, "copying %s to %s\n", from, to);

    while((ret = krb5_kt_next_entry(context, src_keytab,
				    &entry, &cursor)) == 0) {
	char *name_str;
	char *etype_str;
	ret = krb5_unparse_name (context, entry.principal, &name_str);
	if(ret) {
	    krb5_warn(context, ret, "krb5_unparse_name");
	    name_str = NULL; /* XXX */
	}
	ret = krb5_enctype_to_string(context, entry.keyblock.keytype, &etype_str);
	if(ret) {
	    krb5_warn(context, ret, "krb5_enctype_to_string");
	    etype_str = NULL; /* XXX */
	}
	ret = krb5_kt_get_entry(context, dst_keytab,
				entry.principal,
				entry.vno,
				entry.keyblock.keytype,
				&dummy);
	if(ret == 0) {
	    /* this entry is already in the new keytab, so no need to
               copy it; if the keyblocks are not the same, something
               is weird, so complain about that */
	    if(!compare_keyblock(&entry.keyblock, &dummy.keyblock)) {
		krb5_warnx(context, "entry with different keyvalue "
			   "already exists for %s, keytype %s, kvno %d",
			   name_str, etype_str, entry.vno);
	    }
	    krb5_kt_free_entry(context, &dummy);
	    krb5_kt_free_entry (context, &entry);
	    free(name_str);
	    free(etype_str);
	    continue;
	} else if(ret != KRB5_KT_NOTFOUND) {
	    krb5_warn (context, ret, "%s: fetching %s/%s/%u",
		       to, name_str, etype_str, entry.vno);
	    krb5_kt_free_entry (context, &entry);
	    free(name_str);
	    free(etype_str);
	    break;
	}
	if (verbose_flag)
	    fprintf (stderr, "copying %s, keytype %s, kvno %d\n", name_str,
		     etype_str, entry.vno);
	ret = krb5_kt_add_entry (context, dst_keytab, &entry);
	krb5_kt_free_entry (context, &entry);
	if (ret) {
	    krb5_warn (context, ret, "%s: adding %s/%s/%u",
		       to, name_str, etype_str, entry.vno);
	    free(name_str);
	    free(etype_str);
	    break;
	}
	free(name_str);
	free(etype_str);
    }
    krb5_kt_end_seq_get (context, src_keytab, &cursor);

  out:
    krb5_kt_close (context, src_keytab);
    krb5_kt_close (context, dst_keytab);
    return ret != 0;
}
Example #28
0
krb5_error_code
_kdc_as_rep(krb5_context context, 
	    krb5_kdc_configuration *config,
	    KDC_REQ *req, 
	    const krb5_data *req_buffer, 
	    krb5_data *reply,
	    const char *from,
	    struct sockaddr *from_addr,
	    int datagram_reply)
{
    KDC_REQ_BODY *b = &req->req_body;
    AS_REP rep;
    KDCOptions f = b->kdc_options;
    hdb_entry_ex *client = NULL, *server = NULL;
    krb5_enctype cetype, setype, sessionetype;
    krb5_data e_data;
    EncTicketPart et;
    EncKDCRepPart ek;
    krb5_principal client_princ = NULL, server_princ = NULL;
    char *client_name = NULL, *server_name = NULL;
    krb5_error_code ret = 0;
    const char *e_text = NULL;
    krb5_crypto crypto;
    Key *ckey, *skey;
    EncryptionKey *reply_key;
    int flags = 0;
#ifdef PKINIT
    pk_client_params *pkp = NULL;
#endif

    memset(&rep, 0, sizeof(rep));
    krb5_data_zero(&e_data);

    if (f.canonicalize)
	flags |= HDB_F_CANON;

    if(b->sname == NULL){
	ret = KRB5KRB_ERR_GENERIC;
	e_text = "No server in request";
    } else{
	ret = _krb5_principalname2krb5_principal (context,
						  &server_princ,
						  *(b->sname),
						  b->realm);
	if (ret == 0)
	    ret = krb5_unparse_name(context, server_princ, &server_name);
    }
    if (ret) {
	kdc_log(context, config, 0, 
		"AS-REQ malformed server name from %s", from);
	goto out;
    }
    
    if(b->cname == NULL){
	ret = KRB5KRB_ERR_GENERIC;
	e_text = "No client in request";
    } else {

	if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
	    if (b->cname->name_string.len != 1) {
		kdc_log(context, config, 0,
			"AS-REQ malformed canon request from %s, "
			"enterprise name with %d name components", 
			from, b->cname->name_string.len);
		ret = KRB5_PARSE_MALFORMED;
		goto out;
	    }
	    ret = krb5_parse_name(context, b->cname->name_string.val[0],
				  &client_princ);
	    if (ret)
		goto out;
	} else {
	    ret = _krb5_principalname2krb5_principal (context,
						      &client_princ,
						      *(b->cname),
						      b->realm);
	    if (ret)
		goto out;
	}
	ret = krb5_unparse_name(context, client_princ, &client_name);
    }
    if (ret) {
	kdc_log(context, config, 0,
		"AS-REQ malformed client name from %s", from);
	goto out;
    }

    kdc_log(context, config, 0, "AS-REQ %s from %s for %s", 
	    client_name, from, server_name);

    ret = _kdc_db_fetch(context, config, client_princ, 
			HDB_F_GET_CLIENT | flags, NULL, &client);
    if(ret){
	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name,
		krb5_get_err_text(context, ret));
	ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	goto out;
    }

    ret = _kdc_db_fetch(context, config, server_princ,
			HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
			NULL, &server);
    if(ret){
	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name,
		krb5_get_err_text(context, ret));
	ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
	goto out;
    }

    ret = _kdc_windc_client_access(context, client, req);
    if(ret)
	goto out;

    ret = _kdc_check_flags(context, config, 
			   client, client_name,
			   server, server_name,
			   TRUE);
    if(ret)
	goto out;

    memset(&et, 0, sizeof(et));
    memset(&ek, 0, sizeof(ek));

    if(req->padata){
	int i;
	const PA_DATA *pa;
	int found_pa = 0;

	log_patypes(context, config, req->padata);

#ifdef PKINIT
	kdc_log(context, config, 5, 
		"Looking for PKINIT pa-data -- %s", client_name);

	e_text = "No PKINIT PA found";

	i = 0;
	if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ)))
	    ;
	if (pa == NULL) {
	    i = 0;
	    if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN)))
		;
	}
	if (pa) {
	    char *client_cert = NULL;

	    ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp);
	    if (ret) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		kdc_log(context, config, 5, 
			"Failed to decode PKINIT PA-DATA -- %s", 
			client_name);
		goto ts_enc;
	    }
	    if (ret == 0 && pkp == NULL)
		goto ts_enc;

	    ret = _kdc_pk_check_client(context,
				       config,
				       client,
				       pkp,
				       &client_cert);
	    if (ret) {
		e_text = "PKINIT certificate not allowed to "
		    "impersonate principal";
		_kdc_pk_free_client_param(context, pkp);

		kdc_log(context, config, 0, "%s", e_text);
		pkp = NULL;
		goto out;
	    }
	    found_pa = 1;
	    et.flags.pre_authent = 1;
	    kdc_log(context, config, 0,
		    "PKINIT pre-authentication succeeded -- %s using %s", 
		    client_name, client_cert);
	    free(client_cert);
	    if (pkp)
		goto preauth_done;
	}
    ts_enc:
#endif
	kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", 
		client_name);

	i = 0;
	e_text = "No ENC-TS found";
	while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
	    krb5_data ts_data;
	    PA_ENC_TS_ENC p;
	    size_t len;
	    EncryptedData enc_data;
	    Key *pa_key;
	    char *str;
	    
	    found_pa = 1;
	    
	    ret = decode_EncryptedData(pa->padata_value.data,
				       pa->padata_value.length,
				       &enc_data,
				       &len);
	    if (ret) {
		ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", 
			client_name);
		goto out;
	    }
	    
	    ret = hdb_enctype2key(context, &client->entry, 
				  enc_data.etype, &pa_key);
	    if(ret){
		char *estr;
		e_text = "No key matches pa-data";
		ret = KRB5KDC_ERR_ETYPE_NOSUPP;
		if(krb5_enctype_to_string(context, enc_data.etype, &estr))
		    estr = NULL;
		if(estr == NULL)
		    kdc_log(context, config, 5, 
			    "No client key matching pa-data (%d) -- %s", 
			    enc_data.etype, client_name);
		else
		    kdc_log(context, config, 5,
			    "No client key matching pa-data (%s) -- %s", 
			    estr, client_name);
		free(estr);
		    
		free_EncryptedData(&enc_data);
		continue;
	    }

	try_next_key:
	    ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto);
	    if (ret) {
		kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
			krb5_get_err_text(context, ret));
		free_EncryptedData(&enc_data);
		continue;
	    }

	    ret = krb5_decrypt_EncryptedData (context,
					      crypto,
					      KRB5_KU_PA_ENC_TIMESTAMP,
					      &enc_data,
					      &ts_data);
	    krb5_crypto_destroy(context, crypto);
	    if(ret){
		krb5_error_code ret2;
		ret2 = krb5_enctype_to_string(context, 
					      pa_key->key.keytype, &str);
		if (ret2)
		    str = NULL;
		kdc_log(context, config, 5, 
			"Failed to decrypt PA-DATA -- %s "
			"(enctype %s) error %s",
			client_name,
			str ? str : "unknown enctype", 
			krb5_get_err_text(context, ret));
		free(str);

		if(hdb_next_enctype2key(context, &client->entry, 
					enc_data.etype, &pa_key) == 0)
		    goto try_next_key;
		e_text = "Failed to decrypt PA-DATA";

		free_EncryptedData(&enc_data);
		ret = KRB5KDC_ERR_PREAUTH_FAILED;
		continue;
	    }
	    free_EncryptedData(&enc_data);
	    ret = decode_PA_ENC_TS_ENC(ts_data.data,
				       ts_data.length,
				       &p,
				       &len);
	    krb5_data_free(&ts_data);
	    if(ret){
		e_text = "Failed to decode PA-ENC-TS-ENC";
		ret = KRB5KDC_ERR_PREAUTH_FAILED;
		kdc_log(context, config, 
			5, "Failed to decode PA-ENC-TS_ENC -- %s",
			client_name);
		continue;
	    }
	    free_PA_ENC_TS_ENC(&p);
	    if (abs(kdc_time - p.patimestamp) > context->max_skew) {
		char client_time[100];
		
		krb5_format_time(context, p.patimestamp, 
				 client_time, sizeof(client_time), TRUE); 

 		ret = KRB5KRB_AP_ERR_SKEW;
 		kdc_log(context, config, 0,
			"Too large time skew, "
			"client time %s is out by %u > %u seconds -- %s", 
			client_time, 
			(unsigned)abs(kdc_time - p.patimestamp), 
			context->max_skew,
			client_name);
#if 0
		/* This code is from samba, needs testing */
		/* 
		 * the following is needed to make windows clients
		 * to retry using the timestamp in the error message
		 *
		 * this is maybe a bug in windows to not trying when e_text
		 * is present...
		 */
		e_text = NULL;
#else
		e_text = "Too large time skew";
#endif
		goto out;
	    }
	    et.flags.pre_authent = 1;

	    ret = krb5_enctype_to_string(context,pa_key->key.keytype, &str);
	    if (ret)
		str = NULL;

	    kdc_log(context, config, 2,
		    "ENC-TS Pre-authentication succeeded -- %s using %s", 
		    client_name, str ? str : "unknown enctype");
	    free(str);
	    break;
	}
#ifdef PKINIT
    preauth_done:
#endif
	if(found_pa == 0 && config->require_preauth)
	    goto use_pa;
	/* We come here if we found a pa-enc-timestamp, but if there
           was some problem with it, other than too large skew */
	if(found_pa && et.flags.pre_authent == 0){
	    kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
	    e_text = NULL;
	    goto out;
	}
    }else if (config->require_preauth
	      || client->entry.flags.require_preauth
	      || server->entry.flags.require_preauth) {
	METHOD_DATA method_data;
	PA_DATA *pa;
	unsigned char *buf;
	size_t len;

    use_pa: 
	method_data.len = 0;
	method_data.val = NULL;

	ret = realloc_method_data(&method_data);
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_ENC_TIMESTAMP;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;

#ifdef PKINIT
	ret = realloc_method_data(&method_data);
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_PK_AS_REQ;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;

	ret = realloc_method_data(&method_data);
	pa = &method_data.val[method_data.len-1];
	pa->padata_type		= KRB5_PADATA_PK_AS_REQ_WIN;
	pa->padata_value.length	= 0;
	pa->padata_value.data	= NULL;
#endif

	/* 
	 * RFC4120 requires: 
	 * - If the client only knows about old enctypes, then send
	 *   both info replies (we send 'info' first in the list).
	 * - If the client is 'modern', because it knows about 'new'
	 *   enctype types, then only send the 'info2' reply.
	 */

	/* XXX check ret */
	if (only_older_enctype_p(req))
	    ret = get_pa_etype_info(context, config,
				    &method_data, &client->entry, 
				    b->etype.val, b->etype.len); 
	/* XXX check ret */
	ret = get_pa_etype_info2(context, config, &method_data, 
				 &client->entry, b->etype.val, b->etype.len);

	
	ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
	free_METHOD_DATA(&method_data);

	e_data.data   = buf;
	e_data.length = len;
	e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",

	ret = KRB5KDC_ERR_PREAUTH_REQUIRED;

	kdc_log(context, config, 0,
		"No preauth found, returning PREAUTH-REQUIRED -- %s",
		client_name);
	goto out;
    }
    
    /*
     * Find the client key (for preauth ENC-TS verification and reply
     * encryption).  Then the best encryption type for the KDC and
     * last the best session key that shared between the client and
     * KDC runtime enctypes.
     */

    ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len,
			  &ckey, &cetype);
    if (ret) {
	kdc_log(context, config, 0, 
		"Client (%s) has no support for etypes", client_name);
	goto out;
    }
	
    ret = _kdc_get_preferred_key(context, config,
				 server, server_name,
				 &setype, &skey);
    if(ret)
	goto out;

    /* 
     * Select a session enctype from the list of the crypto systems
     * supported enctype, is supported by the client and is one of the
     * enctype of the enctype of the krbtgt.
     *
     * The later is used as a hint what enctype all KDC are supporting
     * to make sure a newer version of KDC wont generate a session
     * enctype that and older version of a KDC in the same realm can't
     * decrypt.
     *
     * But if the KDC admin is paranoid and doesn't want to have "no
     * the best" enctypes on the krbtgt, lets save the best pick from
     * the client list and hope that that will work for any other
     * KDCs.
     */
    {
	const krb5_enctype *p;
	krb5_enctype clientbest = ETYPE_NULL;
	int i, j;

	p = krb5_kerberos_enctypes(context);

	sessionetype = ETYPE_NULL;

	for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) {
	    if (krb5_enctype_valid(context, p[i]) != 0)
		continue;

	    for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) {
		Key *dummy;
		/* check with client */
		if (p[i] != b->etype.val[j])
		    continue; 
		/* save best of union of { client, crypto system } */
		if (clientbest == ETYPE_NULL)
		    clientbest = p[i];
		/* check with krbtgt */
		ret = hdb_enctype2key(context, &server->entry, p[i], &dummy);
		if (ret) 
		    continue;
		sessionetype = p[i];
	    }
	}
	/* if krbtgt had no shared keys with client, pick clients best */
	if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) {
	    sessionetype = clientbest;
	} else if (sessionetype == ETYPE_NULL) {
	    kdc_log(context, config, 0,
		    "Client (%s) from %s has no common enctypes with KDC"
		    "to use for the session key", 
		    client_name, from); 
	    goto out;
	}
    }

    log_as_req(context, config, cetype, setype, b);

    if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
       || (f.request_anonymous && !config->allow_anonymous)) {
	ret = KRB5KDC_ERR_BADOPTION;
	kdc_log(context, config, 0, "Bad KDC options -- %s", client_name);
	goto out;
    }
    
    rep.pvno = 5;
    rep.msg_type = krb_as_rep;
    copy_Realm(&client->entry.principal->realm, &rep.crealm);
    if (f.request_anonymous)
	_kdc_make_anonymous_principalname (&rep.cname);
    else
	_krb5_principal2principalname(&rep.cname, 
				      client->entry.principal);
    rep.ticket.tkt_vno = 5;
    copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
    _krb5_principal2principalname(&rep.ticket.sname, 
				  server->entry.principal);
    /* java 1.6 expects the name to be the same type, lets allow that
     * uncomplicated name-types. */
#define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
    if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST))
	rep.ticket.sname.name_type = b->sname->name_type;
#undef CNT

    et.flags.initial = 1;
    if(client->entry.flags.forwardable && server->entry.flags.forwardable)
	et.flags.forwardable = f.forwardable;
    else if (f.forwardable) {
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0,
		"Ticket may not be forwardable -- %s", client_name);
	goto out;
    }
    if(client->entry.flags.proxiable && server->entry.flags.proxiable)
	et.flags.proxiable = f.proxiable;
    else if (f.proxiable) {
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0, 
		"Ticket may not be proxiable -- %s", client_name);
	goto out;
    }
    if(client->entry.flags.postdate && server->entry.flags.postdate)
	et.flags.may_postdate = f.allow_postdate;
    else if (f.allow_postdate){
	ret = KRB5KDC_ERR_POLICY;
	kdc_log(context, config, 0,
		"Ticket may not be postdatable -- %s", client_name);
	goto out;
    }

    /* check for valid set of addresses */
    if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
	ret = KRB5KRB_AP_ERR_BADADDR;
	kdc_log(context, config, 0,
		"Bad address list requested -- %s", client_name);
	goto out;
    }

    ret = krb5_generate_random_keyblock(context, sessionetype, &et.key);
    if (ret)
	goto out;
    copy_PrincipalName(&rep.cname, &et.cname);
    copy_Realm(&rep.crealm, &et.crealm);
    
    {
	time_t start;
	time_t t;
	
	start = et.authtime = kdc_time;
    
	if(f.postdated && req->req_body.from){
	    ALLOC(et.starttime);
	    start = *et.starttime = *req->req_body.from;
	    et.flags.invalid = 1;
	    et.flags.postdated = 1; /* XXX ??? */
	}
	_kdc_fix_time(&b->till);
	t = *b->till;

	/* be careful not overflowing */

	if(client->entry.max_life)
	    t = start + min(t - start, *client->entry.max_life);
	if(server->entry.max_life)
	    t = start + min(t - start, *server->entry.max_life);
#if 0
	t = min(t, start + realm->max_life);
#endif
	et.endtime = t;
	if(f.renewable_ok && et.endtime < *b->till){
	    f.renewable = 1;
	    if(b->rtime == NULL){
		ALLOC(b->rtime);
		*b->rtime = 0;
	    }
	    if(*b->rtime < *b->till)
		*b->rtime = *b->till;
	}
	if(f.renewable && b->rtime){
	    t = *b->rtime;
	    if(t == 0)
		t = MAX_TIME;
	    if(client->entry.max_renew)
		t = start + min(t - start, *client->entry.max_renew);
	    if(server->entry.max_renew)
		t = start + min(t - start, *server->entry.max_renew);
#if 0
	    t = min(t, start + realm->max_renew);
#endif
	    ALLOC(et.renew_till);
	    *et.renew_till = t;
	    et.flags.renewable = 1;
	}
    }

    if (f.request_anonymous)
	et.flags.anonymous = 1;
    
    if(b->addresses){
	ALLOC(et.caddr);
	copy_HostAddresses(b->addresses, et.caddr);
    }
    
    et.transited.tr_type = DOMAIN_X500_COMPRESS;
    krb5_data_zero(&et.transited.contents); 
     
    copy_EncryptionKey(&et.key, &ek.key);

    /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded
     * as 0 and as 0x80 (meaning indefinite length) apart, and is thus
     * incapable of correctly decoding SEQUENCE OF's of zero length.
     *
     * To fix this, always send at least one no-op last_req
     *
     * If there's a pw_end or valid_end we will use that,
     * otherwise just a dummy lr.
     */
    ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
    if (ek.last_req.val == NULL) {
	ret = ENOMEM;
	goto out;
    }
    ek.last_req.len = 0;
    if (client->entry.pw_end
	&& (config->kdc_warn_pwexpire == 0
	    || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_PW_EXPTIME;
	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
	++ek.last_req.len;
    }
    if (client->entry.valid_end) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_ACCT_EXPTIME;
	ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
	++ek.last_req.len;
    }
    if (ek.last_req.len == 0) {
	ek.last_req.val[ek.last_req.len].lr_type  = LR_NONE;
	ek.last_req.val[ek.last_req.len].lr_value = 0;
	++ek.last_req.len;
    }
    ek.nonce = b->nonce;
    if (client->entry.valid_end || client->entry.pw_end) {
	ALLOC(ek.key_expiration);
	if (client->entry.valid_end) {
	    if (client->entry.pw_end)
		*ek.key_expiration = min(*client->entry.valid_end, 
					 *client->entry.pw_end);
	    else
		*ek.key_expiration = *client->entry.valid_end;
	} else
	    *ek.key_expiration = *client->entry.pw_end;
    } else
	ek.key_expiration = NULL;
    ek.flags = et.flags;
    ek.authtime = et.authtime;
    if (et.starttime) {
	ALLOC(ek.starttime);
	*ek.starttime = *et.starttime;
    }
    ek.endtime = et.endtime;
    if (et.renew_till) {
	ALLOC(ek.renew_till);
	*ek.renew_till = *et.renew_till;
    }
    copy_Realm(&rep.ticket.realm, &ek.srealm);
    copy_PrincipalName(&rep.ticket.sname, &ek.sname);
    if(et.caddr){
	ALLOC(ek.caddr);
	copy_HostAddresses(et.caddr, ek.caddr);
    }

    ALLOC(rep.padata);
    rep.padata->len = 0;
    rep.padata->val = NULL;

    reply_key = &ckey->key;
#if PKINIT
    if (pkp) {
	ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, 
				  req, req_buffer, 
				  &reply_key, rep.padata);
	if (ret)
	    goto out;
	ret = _kdc_add_inital_verified_cas(context,
					   config,
					   pkp,
					   &et);
	if (ret)
	    goto out;
    }
#endif

    set_salt_padata (rep.padata, ckey->salt);

    /* Add signing of alias referral */
    if (f.canonicalize) {
	PA_ClientCanonicalized canon;
	krb5_data data;
	PA_DATA pa;
	krb5_crypto crypto;
	size_t len;

	memset(&canon, 0, sizeof(canon));

	canon.names.requested_name = *b->cname;
	canon.names.real_name = client->entry.principal->name;

	ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
			   &canon.names, &len, ret);
	if (ret) 
	    goto out;
	if (data.length != len)
	    krb5_abortx(context, "internal asn.1 error");

	/* sign using "returned session key" */
	ret = krb5_crypto_init(context, &et.key, 0, &crypto);
	if (ret) {
	    free(data.data);
	    goto out;
	}

	ret = krb5_create_checksum(context, crypto, 
				   KRB5_KU_CANONICALIZED_NAMES, 0,
				   data.data, data.length,
				   &canon.canon_checksum);
	free(data.data);
	krb5_crypto_destroy(context, crypto);
	if (ret)
	    goto out;
	  
	ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length,
			   &canon, &len, ret);
	free_Checksum(&canon.canon_checksum);
	if (ret) 
	    goto out;
	if (data.length != len)
	    krb5_abortx(context, "internal asn.1 error");

	pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED;
	pa.padata_value = data;
	ret = add_METHOD_DATA(rep.padata, &pa);
	free(data.data);
	if (ret)
	    goto out;
    }

    if (rep.padata->len == 0) {
	free(rep.padata);
	rep.padata = NULL;
    }

    /* Add the PAC */
    if (send_pac_p(context, req)) {
	krb5_pac p = NULL;
	krb5_data data;

	ret = _kdc_pac_generate(context, client, &p);
	if (ret) {
	    kdc_log(context, config, 0, "PAC generation failed for -- %s", 
		    client_name);
	    goto out;
	}
	if (p != NULL) {
	    ret = _krb5_pac_sign(context, p, et.authtime,
				 client->entry.principal,
				 &skey->key, /* Server key */ 
				 &skey->key, /* FIXME: should be krbtgt key */
				 &data);
	    krb5_pac_free(context, p);
	    if (ret) {
		kdc_log(context, config, 0, "PAC signing failed for -- %s", 
			client_name);
		goto out;
	    }

	    ret = _kdc_tkt_add_if_relevant_ad(context, &et,
					      KRB5_AUTHDATA_WIN2K_PAC,
					      &data);
	    krb5_data_free(&data);
	    if (ret)
		goto out;
	}
    }

    _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, 
		       et.endtime, et.renew_till);

    /* do this as the last thing since this signs the EncTicketPart */
    ret = _kdc_add_KRB5SignedPath(context,
				  config,
				  server,
				  setype,
				  NULL,
				  NULL,
				  &et);
    if (ret)
	goto out;

    ret = _kdc_encode_reply(context, config, 
			    &rep, &et, &ek, setype, server->entry.kvno, 
			    &skey->key, client->entry.kvno, 
			    reply_key, &e_text, reply);
    free_EncTicketPart(&et);
    free_EncKDCRepPart(&ek);
    if (ret)
	goto out;

    /* */
    if (datagram_reply && reply->length > config->max_datagram_reply_length) {
	krb5_data_free(reply);
	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
	e_text = "Reply packet too large";
    }

out:
    free_AS_REP(&rep);
    if(ret){
	krb5_mk_error(context,
		      ret,
		      e_text,
		      (e_data.data ? &e_data : NULL),
		      client_princ,
		      server_princ,
		      NULL,
		      NULL,
		      reply);
	ret = 0;
    }
#ifdef PKINIT
    if (pkp)
	_kdc_pk_free_client_param(context, pkp);
#endif
    if (e_data.data)
        free(e_data.data);
    if (client_princ)
	krb5_free_principal(context, client_princ);
    free(client_name);
    if (server_princ)
	krb5_free_principal(context, server_princ);
    free(server_name);
    if(client)
	_kdc_free_ent(context, client);
    if(server)
	_kdc_free_ent(context, server);
    return ret;
}