Example #1
0
/*
 * Build a tree given a set of profile values retrieved by
 * walk_rtree_capath_vals().
 */
static krb5_error_code
rtree_capath_tree(
    krb5_context context,
    const krb5_data *client,
    const krb5_data *server,
    char **vals,
    krb5_principal **rettree)
{
    krb5_error_code retval = 0;
    unsigned int nvals, nlinks, nprincs, i;
    krb5_data srcrealm, dstrealm;
    krb5_principal *tree, *pprinc;

    *rettree = NULL;
    tree = pprinc = NULL;
    for (nvals = 0; vals[nvals] != NULL; nvals++)
	;
    if (vals[0] != NULL && *vals[0] == '.') {
	nlinks = 0;
    } else {
	nlinks = nvals;
    }
    nprincs = nlinks + 2;
    tree = calloc(nprincs + 1, sizeof(krb5_principal));
    if (tree == NULL) {
	retval = ENOMEM;
	goto error;
    }
    for (i = 0; i < nprincs + 1; i++)
	tree[i] = NULL;
    /* Invariant: PPRINC points one past end of list. */
    pprinc = &tree[0];
    /* Local TGS name */
    retval = krb5_tgtname(context, client, client, pprinc++);
    if (retval) goto error;
    srcrealm = *client;
    for (i = 0; i < nlinks; i++) {
	dstrealm.data = vals[i];
	dstrealm.length = strcspn(vals[i], "\t ");
	retval = krb5_tgtname(context, &dstrealm, &srcrealm, pprinc++);
	if (retval) goto error;
	srcrealm = dstrealm;
    }
    retval = krb5_tgtname(context, server, &srcrealm, pprinc++);
    if (retval) goto error;
    *rettree = tree;

error:
    profile_free_list(vals);
    if (retval) {
	while (pprinc != NULL && pprinc > &tree[0]) {
	    /* krb5_free_principal() correctly handles null input */
	    krb5_free_principal(context, *--pprinc);
	    *pprinc = NULL;
	}
	free(tree);
    }
    return retval;
}
Example #2
0
/*
 * kdc_mcred()
 *
 * Return MCREDS for use as a match criterion.
 *
 * Resulting credential has CLIENT as the client principal, and
 * krbtgt/remote_realm(NXT_KDC)@local_realm(CUR_KDC) as the server
 * principal.  Zeroes MCREDS first, does not allocate MCREDS, and
 * cleans MCREDS on failure.
 */
static krb5_error_code
kdc_mcred(struct tr_state *ts, krb5_principal client, krb5_creds *mcreds)
{
    krb5_error_code retval;
    krb5_data *rdst, *rsrc;

    retval = 0;
    memset(mcreds, 0, sizeof(*mcreds));

    rdst = krb5_princ_component(ts->ctx, *ts->nxt_kdc, 1);
    rsrc = krb5_princ_component(ts->ctx, *ts->cur_kdc, 1);
    retval = krb5_copy_principal(ts->ctx, client, &mcreds->client);
    if (retval)
	goto cleanup;

    retval = krb5_tgtname(ts->ctx, rdst, rsrc, &mcreds->server);
    if (retval)
	goto cleanup;

cleanup:
    if (retval)
	krb5_free_cred_contents(ts->ctx, mcreds);

    return retval;
}
Example #3
0
/*
 * tgt_mcred()
 *
 * Return MCREDS for use as a match criterion.
 *
 * Resulting credential has CLIENT as the client principal, and
 * krbtgt/realm_of(DST)@realm_of(SRC) as the server principal.  Zeroes
 * MCREDS first, does not allocate MCREDS, and cleans MCREDS on
 * failure.  The peculiar ordering of DST and SRC args is for
 * consistency with krb5_tgtname().
 */
static krb5_error_code
tgt_mcred(krb5_context ctx, krb5_principal client,
	  krb5_principal dst, krb5_principal src,
	  krb5_creds *mcreds)
{
    krb5_error_code retval;

    retval = 0;
    memset(mcreds, 0, sizeof(*mcreds));

    retval = krb5_copy_principal(ctx, client, &mcreds->client);
    if (retval)
	goto cleanup;

    retval = krb5_tgtname(ctx, krb5_princ_realm(ctx, dst),
			  krb5_princ_realm(ctx, src), &mcreds->server);
    if (retval)
	goto cleanup;

cleanup:
    if (retval)
	krb5_free_cred_contents(ctx, mcreds);

    return retval;
}
Example #4
0
static krb5_error_code fast_armor_ap_request
(krb5_context context, struct krb5int_fast_request_state *state,
 krb5_ccache ccache, krb5_data *target_realm)
{
    krb5_error_code retval = 0;
    krb5_creds creds, *out_creds = NULL;
    krb5_auth_context authcontext = NULL;
    krb5_data encoded_authenticator;
    krb5_fast_armor *armor = NULL;
    krb5_keyblock *subkey = NULL, *armor_key = NULL;
    encoded_authenticator.data = NULL;
    memset(&creds, 0, sizeof(creds));
    retval = krb5_tgtname(context, target_realm, target_realm, &creds.server);
    if (retval ==0)
	retval = krb5_cc_get_principal(context, ccache, &creds.client);
    if (retval == 0)
	retval = krb5_get_credentials(context, 0, ccache,  &creds, &out_creds);
    if (retval == 0)
	retval = krb5_mk_req_extended(context, &authcontext, AP_OPTS_USE_SUBKEY, NULL /*data*/,
				      out_creds, &encoded_authenticator);
    if (retval == 0)
	retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
    if (retval == 0)
	retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
				      &out_creds->keyblock, "ticketarmor", &armor_key);
    if (retval == 0) {
	armor = calloc(1, sizeof(krb5_fast_armor));
	if (armor == NULL)
	    retval = ENOMEM;
    }
    if (retval == 0) {
	armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
	armor->armor_value = encoded_authenticator;
	encoded_authenticator.data = NULL;
	encoded_authenticator.length = 0;
	state->armor = armor;
	armor = NULL;
	state->armor_key = armor_key;
	armor_key = NULL;
    }
    krb5_free_keyblock(context, armor_key);
    krb5_free_keyblock(context, subkey);
    if (out_creds)
	krb5_free_creds(context, out_creds);
    krb5_free_cred_contents(context, &creds);
    if (encoded_authenticator.data)
	krb5_free_data_contents(context, &encoded_authenticator);
    krb5_auth_con_free(context, authcontext);
    return retval;
}
Example #5
0
/*
 * Build tree by hierarchical traversal.
 */
static krb5_error_code
rtree_hier_tree(
    krb5_context context,
    const krb5_data *client,
    const krb5_data *server,
    krb5_principal **rettree,
    int sep)
{
    krb5_error_code retval;
    krb5_data *realms;
    const krb5_data *dstrealm, *srcrealm;
    krb5_principal *tree, *pprinc;
    size_t nrealms, nprincs, i;

    *rettree = NULL;
    retval = rtree_hier_realms(context, client, server,
			       &realms, &nrealms, sep);
    if (retval)
	return retval;
    nprincs = nrealms;
    pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
    if (tree == NULL) {
	retval = ENOMEM;
	goto error;
    }
    for (i = 0; i < nrealms; i++)
	tree[i] = NULL;
    srcrealm = client;
    for (i = 0; i < nrealms; i++) {
	dstrealm = &realms[i];
	retval = krb5_tgtname(context, dstrealm, srcrealm, pprinc++);
	if (retval) goto error;
	srcrealm = dstrealm;
    }
    *rettree = tree;
    free_realmlist(context, realms, nrealms);
    return 0;
error:
    while (pprinc != NULL && pprinc > tree) {
	krb5_free_principal(context, *--pprinc);
	*pprinc = NULL;
    }
    free_realmlist(context, realms, nrealms);
    free(tree);
    return retval;
}
Example #6
0
/*
 * chase_offpath()
 *
 * Chase off-path TGT referrals.
 *
 * If we are traversing a trusted path (either hierarchically derived
 * or explicit capath) and get a TGT pointing to a realm off this
 * path, query the realm referenced by that off-path TGT.  Repeat
 * until we get to the destination realm or encounter an error.
 *
 * CUR_TGT is always either pointing into REFTGTS or is an alias for
 * TS->OFFPATH_TGT.
 */
static krb5_error_code
chase_offpath(struct tr_state *ts,
	      krb5_principal client, krb5_principal server)
{
    krb5_error_code retval;
    krb5_creds mcred;
    krb5_creds *cur_tgt, *nxt_tgt, *reftgts[KRB5_REFERRAL_MAXHOPS];
    krb5_data *rsrc, *rdst, *r1;
    int rcount, i;

    rdst = krb5_princ_realm(ts->ctx, server);
    cur_tgt = ts->offpath_tgt;

    for (rcount = 0; rcount < KRB5_REFERRAL_MAXHOPS; rcount++) {
	nxt_tgt = NULL;
	memset(&mcred, 0, sizeof(mcred));
	rsrc = krb5_princ_component(ts->ctx, cur_tgt->server, 1);
	retval = krb5_tgtname(ts->ctx, rdst, rsrc, &mcred.server);
	if (retval)
	    goto cleanup;
	mcred.client = client;
        retval = krb5_get_cred_via_tkt(ts->ctx, cur_tgt,
				       FLAGS2OPTS(cur_tgt->ticket_flags),
				       cur_tgt->addresses, &mcred, &nxt_tgt);
	mcred.client = NULL;
	krb5_free_principal(ts->ctx, mcred.server);
	mcred.server = NULL;
	if (retval)
	    goto cleanup;
	if (!IS_TGS_PRINC(ts->ctx, nxt_tgt->server)) {
	    retval = KRB5_KDCREP_MODIFIED;
	    goto cleanup;
	}
	r1 = krb5_princ_component(ts->ctx, nxt_tgt->server, 1);
	if (rdst->length == r1->length &&
	    !memcmp(rdst->data, r1->data, rdst->length)) {
	    retval = 0;
	    goto cleanup;
	}
	retval = offpath_loopchk(ts, nxt_tgt, reftgts, rcount);
	if (retval)
	    goto cleanup;
	reftgts[rcount] = nxt_tgt;
	cur_tgt = nxt_tgt;
	nxt_tgt = NULL;
    }
    /* Max hop count exceeded. */
    retval = KRB5_KDCREP_MODIFIED;

cleanup:
    if (mcred.server != NULL) {
	krb5_free_principal(ts->ctx, mcred.server);
    }
    /*
     * Don't free TS->OFFPATH_TGT if it's in the list of cacheable
     * TGTs to be returned by do_traversal().
     */
    if (ts->offpath_tgt != ts->nxt_tgt) {
	krb5_free_creds(ts->ctx, ts->offpath_tgt);
    }
    ts->offpath_tgt = NULL;
    if (nxt_tgt != NULL) {
	if (retval)
	    krb5_free_creds(ts->ctx, nxt_tgt);
	else
	    ts->offpath_tgt = nxt_tgt;
    }
    for (i = 0; i < rcount; i++) {
	krb5_free_creds(ts->ctx, reftgts[i]);
    }
    return retval;
}
Example #7
0
static krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts, int kdcopt)
{
  krb5_creds      **ret_tgts = NULL;
  int             ntgts = 0;

  krb5_creds      tgt, tgtq, *tgtr = NULL;
  krb5_error_code retval;
  krb5_principal  int_server = NULL;    /* Intermediate server for request */

  krb5_principal  *tgs_list = NULL;
  krb5_principal  *top_server = NULL;
  krb5_principal  *next_server = NULL;
  unsigned int             nservers = 0;
  krb5_boolean	  old_use_conf_ktypes = context->use_conf_ktypes;

  /* in case we never get a TGT, zero the return */

  *tgts = NULL;

  memset((char *)&tgtq, 0, sizeof(tgtq));
  memset((char *)&tgt, 0, sizeof(tgt));

  /*
   * we know that the desired credentials aren't in the cache yet.
   *
   * To get them, we first need a tgt for the realm of the server.
   * first, we see if we have such a TGT in cache.  if not, then
   * we ask the kdc to give us one.  if that doesn't work, then
   * we try to get a tgt for a realm that is closest to the target.
   * once we have that, then we ask that realm if it can give us
   * tgt for the target.  if not, we do the process over with this
   * new tgt.
   */

  /*
   * (the ticket may be issued by some other intermediate
   *  realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY)
   */
  if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client)))
      goto cleanup;

  /* get target tgt from cache */
  if ((retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->server),
			     krb5_princ_realm(context, in_cred->client),
			     &int_server))) {
      goto cleanup;
  }

  if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) {
      goto cleanup;
  }

  /* set endtime to now so krb5_cc_retrieve_cred won't return an expired tik */
  if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	goto cleanup;
  }

  context->use_conf_ktypes = 1;
  if ((retval = krb5_cc_retrieve_cred(context, ccache,
				    KRB5_TC_MATCH_SRV_NAMEONLY |
				    KRB5_TC_SUPPORTED_KTYPES |
				    KRB5_TC_MATCH_TIMES,
				    &tgtq, &tgt)) != 0) {

    if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
	goto cleanup;
    }

    /*
     * Note that we want to request a TGT from our local KDC, even
     * if we already have a TGT for some intermediate realm.  The
     * reason is that our local KDC may have a shortcut to the
     * destination realm, and if it does we want to use the
     * shortcut because it will provide greater security. - bcn
     */

    /*
     * didn't find it in the cache so it is time to get a local
     * tgt and walk the realms tree.
     */
    krb5_free_principal(context, int_server);
    int_server = NULL;
    if ((retval = krb5_tgtname(context,
			       krb5_princ_realm(context, in_cred->client),
			       krb5_princ_realm(context, in_cred->client),
			       &int_server))) {
	goto cleanup;
    }

    krb5_free_cred_contents(context, &tgtq);
    memset((char *)&tgtq, 0, sizeof(tgtq));
    if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client)))
	goto cleanup;
    if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	goto cleanup;

    if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	goto cleanup;
    }

    if ((retval = krb5_cc_retrieve_cred(context, ccache,
					KRB5_TC_MATCH_SRV_NAMEONLY |
					KRB5_TC_SUPPORTED_KTYPES |
					KRB5_TC_MATCH_TIMES,
					&tgtq, &tgt)) != 0) {
	goto cleanup;
    }

    /* get a list of realms to consult */

    if ((retval = krb5_walk_realm_tree(context,
				       krb5_princ_realm(context,in_cred->client),
				       krb5_princ_realm(context,in_cred->server),
				       &tgs_list,
				       KRB5_REALM_BRANCH_CHAR))) {
	goto cleanup;
    }

    for (nservers = 0; tgs_list[nservers]; nservers++)
      ;

    /* allocate storage for TGT pointers. */

    if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) {
      retval = ENOMEM;
      goto cleanup;
    }
    *tgts = ret_tgts;

    /*
     * step one is to take the current tgt and see if there is a tgt for
     * krbtgt/realmof(target)@realmof(tgt).  if not, try to get one with
     * the tgt.
     *
     * if we don't get a tgt for the target, then try to find a tgt as
     * close to the target realm as possible. at each step if there isn't
     * a tgt in the cache we have to try and get one with our latest tgt.
     * once we have a tgt for a closer realm, we go back to step one.
     *
     * once we have a tgt for the target, we go try and get credentials.
     */

    for (top_server = tgs_list;
         top_server < tgs_list + nservers;
         top_server = next_server) {

      /* look in cache for a tgt for the destination */

      krb5_free_cred_contents(context, &tgtq);
      memset(&tgtq, 0, sizeof(tgtq));
      if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client)))
	  goto cleanup;

      krb5_free_principal(context, int_server);
      int_server = NULL;
      if ((retval = krb5_tgtname(context,
				 krb5_princ_realm(context, in_cred->server),
				 krb5_princ_realm(context, *top_server),
				 &int_server))) {
	  goto cleanup;
      }

      if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	  goto cleanup;

      if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	    goto cleanup;
      }

      if ((retval = krb5_cc_retrieve_cred(context, ccache,
					KRB5_TC_MATCH_SRV_NAMEONLY |
					KRB5_TC_SUPPORTED_KTYPES |
					KRB5_TC_MATCH_TIMES,
					  &tgtq, &tgt)) != 0) {

	if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
	    goto cleanup;
	}

	/* didn't find it in the cache so try and get one */
	/* with current tgt.                              */

	if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
	    retval = KRB5_PROG_ETYPE_NOSUPP;
	    goto cleanup;
	}

	krb5_free_cred_contents(context, &tgtq);
	memset(&tgtq, 0, sizeof(tgtq));
	tgtq.times        = tgt.times;

	if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client)))
	    goto cleanup;
	if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	    goto cleanup;
	tgtq.is_skey      = FALSE;
	tgtq.ticket_flags = tgt.ticket_flags;
	retval = krb5_get_cred_via_tkt(context, &tgt,
					    FLAGS2OPTS(tgtq.ticket_flags),
				            tgt.addresses, &tgtq, &tgtr);
	if (retval) {

       /*
	* couldn't get one so now loop backwards through the realms
	* list and try and get a tgt for a realm as close to the
	* target as possible. the kdc should give us a tgt for the
	* closest one it knows about, but not all kdc's do this yet.
	*/

	  for (next_server = tgs_list + nservers - 1;
	       next_server > top_server;
	       next_server--) {
	    krb5_free_cred_contents(context, &tgtq);
	    memset(&tgtq, 0, sizeof(tgtq));
	    if ((retval = krb5_copy_principal(context, tgt.client,
					      &tgtq.client)))
		goto cleanup;

	    krb5_free_principal(context, int_server);
	    int_server = NULL;
	    if ((retval = krb5_tgtname(context,
				       krb5_princ_realm(context, *next_server),
				       krb5_princ_realm(context, *top_server),
				       &int_server))) {
		goto cleanup;
	    }

	    if ((retval = krb5_copy_principal(context, int_server,
					      &tgtq.server)))
		goto cleanup;

	    if ((retval = krb5_timeofday(context,
					&(tgtq.times.endtime))) != 0) {
		goto cleanup;
	    }

	    if ((retval = krb5_cc_retrieve_cred(context, ccache,
						KRB5_TC_MATCH_SRV_NAMEONLY |
						KRB5_TC_SUPPORTED_KTYPES |
						KRB5_TC_MATCH_TIMES,
						&tgtq, &tgt)) != 0) {
	      if (retval != KRB5_CC_NOTFOUND) {
		  goto cleanup;
	      }

	      /* not in the cache so try and get one with our current tgt. */

	      if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
		  retval = KRB5_PROG_ETYPE_NOSUPP;
		  goto cleanup;
	      }

	      krb5_free_cred_contents(context, &tgtq);
	      memset(&tgtq, 0, sizeof(tgtq));
	      tgtq.times        = tgt.times;
	      if ((retval = krb5_copy_principal(context, tgt.client,
						&tgtq.client)))
		  goto cleanup;
	      if ((retval = krb5_copy_principal(context, int_server,
						&tgtq.server)))
		  goto cleanup;
	      tgtq.is_skey      = FALSE;
	      tgtq.ticket_flags = tgt.ticket_flags;
	      retval = krb5_get_cred_via_tkt(context, &tgt,
					     FLAGS2OPTS(tgtq.ticket_flags),
					     tgt.addresses,
					     &tgtq, &tgtr);
	      if (retval)
		  continue;
	
	      /* save tgt in return array */
	      if ((retval = krb5_copy_creds(context, tgtr,
					    &ret_tgts[ntgts]))) {
		  goto cleanup;
	      }
	      krb5_free_creds(context, tgtr);
	      tgtr = NULL;
	
	      tgt = *ret_tgts[ntgts++];
	    }

	    /* got one as close as possible, now start all over */

	    break;
	  }

	  if (next_server == top_server) {
	      goto cleanup;
	  }
	  continue;
        }

	/*
	 * Got a tgt.  If it is for the target realm we can go try for the
	 * credentials.  If it is not for the target realm, then make sure it
	 * is in the realms hierarchy and if so, save it and start the loop
	 * over from there.  Note that we only need to compare the instance
	 * names since that is the target realm of the tgt.
	 */

	for (next_server = top_server; *next_server; next_server++) {
            krb5_data *realm_1 = krb5_princ_component(context, next_server[0], 1);
            krb5_data *realm_2 = krb5_princ_component(context, tgtr->server, 1);
            if (realm_1 != NULL &&
		realm_2 != NULL &&
		realm_1->length == realm_2->length &&
                !memcmp(realm_1->data, realm_2->data, realm_1->length)) {
		break;
            }
	}

	if (!next_server) {
	    retval = KRB5_KDCREP_MODIFIED;
	    goto cleanup;
	}

	if ((retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts]))) {
	    goto cleanup;
	}
	krb5_free_creds(context, tgtr);
	tgtr = NULL;

        tgt = *ret_tgts[ntgts++];

        /* we're done if it is the target */

        if (!*next_server++) break;
      }
    }
  }

  /* got/finally have tgt!  try for the creds */

  if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
    retval = KRB5_PROG_ETYPE_NOSUPP;
    goto cleanup;
  }

  context->use_conf_ktypes = old_use_conf_ktypes;
  retval = krb5_get_cred_via_tkt(context, &tgt,
				 FLAGS2OPTS(tgt.ticket_flags) |
				 kdcopt |
  				 (in_cred->second_ticket.length ?
				  KDC_OPT_ENC_TKT_IN_SKEY : 0),
				 tgt.addresses, in_cred, out_cred);

  /* cleanup and return */

cleanup:

  if (tgtr) krb5_free_creds(context, tgtr);
  if(tgs_list)  krb5_free_realm_tree(context, tgs_list);
  krb5_free_cred_contents(context, &tgtq);
  if (int_server) krb5_free_principal(context, int_server);
  if (ntgts == 0) {
      *tgts = NULL;
      if (ret_tgts)  free(ret_tgts);
      krb5_free_cred_contents(context, &tgt);
  }
  context->use_conf_ktypes = old_use_conf_ktypes;
  return(retval);
}
Example #8
0
krb5_error_code
krb5_walk_realm_tree(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_principal **tree, int realm_branch_char)
{
    krb5_error_code retval;
    krb5_principal *rettree;
    register char *ccp, *scp;
    register char *prevccp = 0, *prevscp = 0;
    char *com_sdot = 0, *com_cdot = 0;
    register int i, links = 0;
    int clen, slen = -1;
    krb5_data tmpcrealm, tmpsrealm;
    int nocommon = 1;

#ifdef CONFIGURABLE_AUTHENTICATION_PATH
    const char *cap_names[4];
    char *cap_client, *cap_server;
    char **cap_nodes;
    krb5_error_code cap_code;
#endif

#ifdef DEBUG_REFERRALS
    printf("krb5_walk_realm_tree starting\n");
    printf("  client is %s\n",client->data);
    printf("  server is %s\n",server->data);
#endif

    if (!(client->data && server->data)) {
	/* Solaris Kerberos - enhance error message */
	if (!client->data && !server->data) {
	    krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
				dgettext(TEXT_DOMAIN,
					"Cannot find ticket for requested realm: unknown client and server"));
	} else {
	    if (!client->data) {
		krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
				    dgettext(TEXT_DOMAIN,
					    "Cannot find ticket for requested realm: unknown client"));
	    } else {
	       krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
				    dgettext(TEXT_DOMAIN,
					    "Cannot find ticket for requested realm: unknown server"));
	    }
	}
	return KRB5_NO_TKT_IN_RLM;
    }
#ifdef CONFIGURABLE_AUTHENTICATION_PATH
    if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
	return ENOMEM;
    strncpy(cap_client, client->data, client->length);
    cap_client[client->length] = '\0';
    if ((cap_server = (char *)malloc(server->length + 1)) == NULL) {
	krb5_xfree(cap_client);
	return ENOMEM;
    }
    strncpy(cap_server, server->data, server->length);
    cap_server[server->length] = '\0';
    cap_names[0] = "capaths";
    cap_names[1] = cap_client;
    cap_names[2] = cap_server;
    cap_names[3] = 0;
    cap_code = profile_get_values(context->profile, cap_names, &cap_nodes);
    krb5_xfree(cap_client);  /* done with client string */
    cap_names[1] = 0;
    if (cap_code == 0) {     /* found a path, so lets use it */
	links = 0;
	if (*cap_nodes[0] != '.') { /* a link of . means direct */
	    while(cap_nodes[links]) {
		links++;
	    }
	}
	if (cap_nodes[links] != NULL)
	    krb5_xfree(cap_nodes[links]);

	cap_nodes[links] = cap_server; /* put server on end of list */
	/* this simplifies the code later and make */
	/* cleanup eaiser as well */
	links++;		/* count the null entry at end */
    } else {			/* no path use hierarchical method */
	krb5_xfree(cap_server); /* failed, don't need server string */
	cap_names[2] = 0;
#endif
	clen = client->length;
	slen = server->length;

	for (com_cdot = ccp = client->data + clen - 1,
		 com_sdot = scp = server->data + slen - 1;
	     clen && slen && *ccp == *scp ;
	     ccp--, scp--, 	clen--, slen--) {
	    if (*ccp == realm_branch_char) {
		com_cdot = ccp;
		com_sdot = scp;
		nocommon = 0;
	    }
	}

	/* ccp, scp point to common root.
	   com_cdot, com_sdot point to common components. */
	/* handle case of one ran out */
	if (!clen) {
	    /* construct path from client to server, down the tree */
	    if (!slen) {
		/* in the same realm--this means there is no ticket
		   in this realm. */
	        krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
				    dgettext(TEXT_DOMAIN,
					    "Cannot find ticket for requested realm: client is '%s', server is '%s'"),
				    client->data, server->data);
		return KRB5_NO_TKT_IN_RLM;
	    }
	    if (*scp == realm_branch_char) {
		/* one is a subdomain of the other */
		com_cdot = client->data;
		com_sdot = scp;
		nocommon = 0;
	    } /* else normal case of two sharing parents */
	}
	if (!slen) {
	    /* construct path from client to server, up the tree */
	    if (*ccp == realm_branch_char) {
		/* one is a subdomain of the other */
		com_sdot = server->data;
		com_cdot = ccp;
		nocommon = 0;
	    } /* else normal case of two sharing parents */
	}
	/* determine #links to/from common ancestor */
	if (nocommon)
	    links = 1;
	else
	    links = 2;
	/* if no common ancestor, artificially set up common root at the last
	   component, then join with special code */
	for (ccp = client->data; ccp < com_cdot; ccp++) {
	    if (*ccp == realm_branch_char) {
		links++;
		if (nocommon)
		    prevccp = ccp;
	    }
	}

	for (scp = server->data; scp < com_sdot; scp++) {
	    if (*scp == realm_branch_char) {
		links++;
		if (nocommon)
		    prevscp = scp;
	    }
	}
	if (nocommon) {
	    if (prevccp)
		com_cdot = prevccp;
	    if (prevscp)
		com_sdot = prevscp;

	    if(com_cdot == client->data + client->length -1)
		com_cdot = client->data - 1 ;
	    if(com_sdot == server->data + server->length -1)
		com_sdot = server->data - 1 ;
	}
#ifdef CONFIGURABLE_AUTHENTICATION_PATH
    }		/* end of if use hierarchical method */
#endif

    if (!(rettree = (krb5_principal *)calloc(links+2,
					     sizeof(krb5_principal)))) {
	return ENOMEM;
    }
    i = 1;
    if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) {
	krb5_xfree(rettree);
	return retval;
    }
#ifdef CONFIGURABLE_AUTHENTICATION_PATH
    links--;				/* dont count the null entry on end */
    if (cap_code == 0) {    /* found a path above */
	tmpcrealm.data = client->data;
	tmpcrealm.length = client->length;
	while( i-1 <= links) {
			
	    tmpsrealm.data = cap_nodes[i-1];
	    /* don't count trailing whitespace from profile_get */
	    tmpsrealm.length = strcspn(cap_nodes[i-1],"\t ");
	    if ((retval = krb5_tgtname(context,
				       &tmpsrealm,
				       &tmpcrealm,
				       &rettree[i]))) {
		while (i) {
		    krb5_free_principal(context, rettree[i-1]);
		    i--;
		}
		krb5_xfree(rettree);
				/* cleanup the cap_nodes from profile_get */
		for (i = 0; i<=links; i++) {
		    krb5_xfree(cap_nodes[i]);
		}
		krb5_xfree((char *)cap_nodes);
		return retval;
	    }
	    tmpcrealm.data = tmpsrealm.data;	
	    tmpcrealm.length = tmpsrealm.length;
	    i++;
	}
	/* cleanup the cap_nodes from profile_get last one has server */
	for (i = 0; i<=links; i++) {
	    krb5_xfree(cap_nodes[i]);
	}
	krb5_xfree((char *)cap_nodes);
    } else {  /* if not cap then use hierarchical method */
#endif
	for (prevccp = ccp = client->data;
	     ccp <= com_cdot;
	     ccp++) {
	    if (*ccp != realm_branch_char)
		continue;
	    ++ccp;				/* advance past dot */
	    tmpcrealm.data = prevccp;
	    tmpcrealm.length = client->length -
		(prevccp - client->data);
	    tmpsrealm.data = ccp;
	    tmpsrealm.length = client->length -
		(ccp - client->data);
	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
				       &rettree[i]))) {
		while (i) {
		    krb5_free_principal(context, rettree[i-1]);
		    i--;
		}
		krb5_xfree(rettree);
		return retval;
	    }
	    prevccp = ccp;
	    i++;
	}
	if (nocommon) {
	    tmpcrealm.data = com_cdot + 1;
	    tmpcrealm.length = client->length -
		(com_cdot + 1 - client->data);
	    tmpsrealm.data = com_sdot + 1;
	    tmpsrealm.length = server->length -
		(com_sdot + 1 - server->data);
	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
				       &rettree[i]))) {
		while (i) {
		    krb5_free_principal(context, rettree[i-1]);
		    i--;
		}
		krb5_xfree(rettree);
		return retval;
	    }
	    i++;
	}

	for (prevscp = com_sdot + 1, scp = com_sdot - 1;
	     scp > server->data;
	     scp--) {
	    if (*scp != realm_branch_char)
		continue;
	    if (scp - 1 < server->data)
		break;			/* XXX only if . starts realm? */
	    tmpcrealm.data = prevscp;
	    tmpcrealm.length = server->length -
		(prevscp - server->data);
	    tmpsrealm.data = scp + 1;
	    tmpsrealm.length = server->length -
		(scp + 1 - server->data);
	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
				       &rettree[i]))) {
		while (i) {
		    krb5_free_principal(context, rettree[i-1]);
		    i--;
		}
		krb5_xfree(rettree);
		return retval;
	    }
	    prevscp = scp + 1;
	    i++;
	}
	if (slen && com_sdot >= server->data) {
	    /* only necessary if building down tree from ancestor or client */
	    /* however, we can get here if we have only one component
	       in the server realm name, hence we make sure we found a component
	       separator there... */
	    tmpcrealm.data = prevscp;
	    tmpcrealm.length = server->length -
		(prevscp - server->data);
	    if ((retval = krb5_tgtname(context, server, &tmpcrealm,
				       &rettree[i]))) {
		while (i) {
		    krb5_free_principal(context, rettree[i-1]);
		    i--;
		}
		krb5_xfree(rettree);
		return retval;
	    }
	}
#ifdef CONFIGURABLE_AUTHENTICATION_PATH
    }
#endif
    *tree = rettree;

#ifdef DEBUG_REFERRALS
    printf("krb5_walk_realm_tree ending; tree (length %d) is:\n",links);
    for(i=0;i<links+2;i++) {
        if ((*tree)[i])
	    krb5int_dbgref_dump_principal("krb5_walk_realm_tree tree",(*tree)[i]);
	else
	    printf("tree element %i null\n");
    }
#endif
    return 0;
}