Beispiel #1
0
/*
 * do_traversal()
 *
 * Find final TGT needed to get CLIENT a ticket for SERVER.  Point
 * OUT_TGT at the desired TGT, which may be an existing cached TGT
 * (copied into OUT_CC_TGT) or one of the newly obtained TGTs
 * (collected in OUT_KDC_TGTS).
 *
 * Get comfortable; this is somewhat complicated.
 *
 * Nomenclature: Cross-realm TGS principal names have the form:
 *
 *     krbtgt/REMOTE@LOCAL
 *
 * krb5_walk_realm_tree() returns a list like:
 *
 *     krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, ...
 *
 * These are prinicpal names, not realm names.  We only really use the
 * remote parts of the TGT principal names.
 *
 * The do_traversal loop calls next_closest_tgt() to find the next
 * closest TGT to the destination realm.  next_closest_tgt() updates
 * NXT_KDC for the following iteration of the do_traversal() loop.
 *
 * At the beginning of any given iteration of the do_traversal() loop,
 * CUR_KDC's remote realm is the remote realm of CUR_TGT->SERVER.  The
 * local realms of CUR_KDC and CUR_TGT->SERVER may not match due to
 * short-circuit paths provided by intermediate KDCs, e.g., CUR_KDC
 * might be krbtgt/D@C, while CUR_TGT->SERVER is krbtgt/D@B.
 *
 * For example, given KDC_LIST of
 *
 * krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, krbtgt/R4@R3,
 * krbtgt/R5@R4
 *
 * The next_closest_tgt() loop moves NXT_KDC to the left starting from
 * R5, stopping before it reaches CUR_KDC.  When next_closest_tgt()
 * returns, the do_traversal() loop updates CUR_KDC to be NXT_KDC, and
 * calls next_closest_tgt() again.
 *
 * next_closest_tgt() at start of its loop:
 *
 *      CUR                 NXT
 *       |                   |
 *       V                   V
 *     +----+----+----+----+----+
 *     | R1 | R2 | R3 | R4 | R5 |
 *     +----+----+----+----+----+
 *
 * next_closest_tgt() returns after finding a ticket for krbtgt/R3@R1:
 *
 *      CUR       NXT
 *       |         |
 *       V         V
 *     +----+----+----+----+----+
 *     | R1 | R2 | R3 | R4 | R5 |
 *     +----+----+----+----+----+
 *
 * do_traversal() updates CUR_KDC:
 *
 *                NXT
 *                CUR
 *                 |
 *                 V
 *     +----+----+----+----+----+
 *     | R1 | R2 | R3 | R4 | R5 |
 *     +----+----+----+----+----+
 *
 * next_closest_tgt() at start of its loop:
 *
 *                CUR       NXT
 *                 |         |
 *                 V         V
 *     +----+----+----+----+----+
 *     | R1 | R2 | R3 | R4 | R5 |
 *     +----+----+----+----+----+
 *
 * etc.
 *
 * The algorithm executes in n*(n-1)/2 (the sum of integers from 1 to
 * n-1) attempts in the worst case, i.e., each KDC only has a
 * cross-realm ticket for the immediately following KDC in the transit
 * path.  Typically, short-circuit paths will cause execution occur
 * faster than this worst-case scenario.
 *
 * When next_closest_tgt() updates NXT_KDC, it may not perform a
 * simple increment from CUR_KDC, in part because some KDC may
 * short-circuit pieces of the transit path.
 */
static krb5_error_code
do_traversal(krb5_context ctx,
	     krb5_ccache ccache,
	     krb5_principal client,
	     krb5_principal server,
	     krb5_creds *out_cc_tgt,
	     krb5_creds **out_tgt,
	     krb5_creds ***out_kdc_tgts)
{
    krb5_error_code retval;
    struct tr_state state, *ts;

    *out_tgt = NULL;
    *out_kdc_tgts = NULL;
    ts = &state;
    memset(ts, 0, sizeof(*ts));
    ts->ctx = ctx;
    ts->ccache = ccache;
    init_cc_tgts(ts);

    retval = init_rtree(ts, client, server);
    if (retval)
	goto cleanup;

    retval = retr_local_tgt(ts, client);
    if (retval)
	goto cleanup;

    for (ts->cur_kdc = ts->kdc_list, ts->nxt_kdc = NULL;
	 ts->cur_kdc != NULL && ts->cur_kdc < ts->lst_kdc;
	 ts->cur_kdc = ts->nxt_kdc, ts->cur_tgt = ts->nxt_tgt) {

	retval = next_closest_tgt(ts, client);
	if (retval)
	    goto cleanup;
	assert(ts->cur_kdc != ts->nxt_kdc);
    }

    if (NXT_TGT_IS_CACHED(ts)) {
	*out_cc_tgt = *ts->cur_cc_tgt;
	*out_tgt = out_cc_tgt;
	MARK_CUR_CC_TGT_CLEAN(ts);
    } else {
	/* CUR_TGT is somewhere in KDC_TGTS; no need to copy. */
	*out_tgt = ts->nxt_tgt;
    }

cleanup:
    clean_cc_tgts(ts);
    if (ts->kdc_list != NULL)
	krb5_free_realm_tree(ctx, ts->kdc_list);
    if (ts->ntgts == 0) {
	*out_kdc_tgts = NULL;
	if (ts->kdc_tgts != NULL)
	    free(ts->kdc_tgts);
    } else
	*out_kdc_tgts = ts->kdc_tgts;
    return retval;
}
Beispiel #2
0
int
main(int argc, char **argv)
{
    krb5_data client, server;
    char    realm_branch_char = '.';
    krb5_principal *tree, *p;
    char *name;
    krb5_error_code retval;
    krb5_context context;

    krb5_init_context(&context);

    if (argc < 3 || argc > 4) {
        fprintf(stderr,
                "Usage: %s client-realm server-realm [sep_char]\n",
                argv[0]);
        exit(99);
    }
    client.data = argv[1];
    client.length = strlen(client.data);

    server.data = argv[2];
    server.length = strlen(server.data);

    if (argc == 4)
        realm_branch_char = argv[3][0];

    retval = krb5_walk_realm_tree(context, &client, &server, &tree,
                                  realm_branch_char);
    if (retval) {
        com_err("krb5_walk_realm_tree", retval, " ");
        exit(1);
    }

    for (p = tree; *p; p++) {
        retval = krb5_unparse_name(context, *p, &name);
        if (retval) {
            com_err("krb5_unprase_name", retval, " ");
            exit(2);
        }
        printf("%s\n", name);
        free(name);
    }

    krb5_free_realm_tree(context, tree);
    krb5_free_context(context);

    exit(0);
}
Beispiel #3
0
krb5_error_code
krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in,
                           const krb5_data *crealm, const krb5_data *srealm)
{
    krb5_data trans;
    struct check_data cdata;
    krb5_error_code r;
    const krb5_data *anonymous;

    trans.length = trans_in->length;
    trans.data = (char *) trans_in->data;
    if (trans.length && (trans.data[trans.length-1] == '\0'))
        trans.length--;

    Tprintf (("krb5_check_transited_list(trans=\"%.*s\", crealm=\"%.*s\", srealm=\"%.*s\")\n",
              (int) trans.length, trans.data,
              (int) crealm->length, crealm->data,
              (int) srealm->length, srealm->data));
    if (trans.length == 0)
        return 0;
    anonymous = krb5_anonymous_realm();
    if (crealm->length == anonymous->length &&
            (memcmp(crealm->data, anonymous->data, anonymous->length) == 0))
        return 0; /* Nothing to check for anonymous */

    r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs,
                              KRB5_REALM_BRANCH_CHAR);
    if (r) {
        Tprintf (("error %ld\n", (long) r));
        return r;
    }
#ifdef DEBUG /* avoid compiler warning about 'd' unused */
    {
        int i;
        Tprintf (("tgs list = {\n"));
        for (i = 0; cdata.tgs[i]; i++) {
            char *name;
            r = krb5_unparse_name (ctx, cdata.tgs[i], &name);
            Tprintf (("\t'%s'\n", name));
            free (name);
        }
        Tprintf (("}\n"));
    }
#endif
    cdata.ctx = ctx;
    r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans);
    krb5_free_realm_tree (ctx, cdata.tgs);
    return r;
}
Beispiel #4
0
/*
 * The request seems to be for a ticket-granting service somewhere else,
 * but we don't have a ticket for the final TGS.  Try to give the requestor
 * some intermediate realm.
 */
static krb5_error_code
find_alternate_tgs(kdc_realm_t *kdc_active_realm, krb5_principal princ,
                   krb5_db_entry **server_ptr, const char **status)
{
    krb5_error_code retval;
    krb5_principal *plist = NULL, *pl2;
    krb5_data tmp;
    krb5_db_entry *server = NULL;

    *server_ptr = NULL;
    assert(is_cross_tgs_principal(princ));
    if ((retval = krb5_walk_realm_tree(kdc_context,
                                       krb5_princ_realm(kdc_context, princ),
                                       krb5_princ_component(kdc_context, princ, 1),
                                       &plist, KRB5_REALM_BRANCH_CHAR))) {
        goto cleanup;
    }
    /* move to the end */
    for (pl2 = plist; *pl2; pl2++);

    /* the first entry in this array is for krbtgt/local@local, so we
       ignore it */
    while (--pl2 > plist) {
        tmp = *krb5_princ_realm(kdc_context, *pl2);
        krb5_princ_set_realm(kdc_context, *pl2,
                             krb5_princ_realm(kdc_context, princ));
        retval = db_get_svc_princ(kdc_context, *pl2, 0, &server, status);
        krb5_princ_set_realm(kdc_context, *pl2, &tmp);
        if (retval == KRB5_KDB_NOENTRY)
            continue;
        else if (retval)
            goto cleanup;

        log_tgs_alt_tgt(kdc_context, server->princ);
        *server_ptr = server;
        server = NULL;
        goto cleanup;
    }
cleanup:
    if (retval == 0 && *server_ptr == NULL)
        retval = KRB5_KDB_NOENTRY;
    if (retval != 0)
        *status = "UNKNOWN_SERVER";

    krb5_free_realm_tree(kdc_context, plist);
    krb5_db_free_principal(kdc_context, server);
    return retval;
}
Beispiel #5
0
/* Initialize the realm path fields for getting a TGT for
 * ctx->server->realm. */
static krb5_error_code
init_realm_path(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    krb5_principal *tgt_princ_list = NULL;
    krb5_data *realm_path;
    size_t nrealms, i;

    /* Construct a list of TGT principals from client to server.  We will throw
     * this away after grabbing the remote realms from each principal. */
    code = krb5_walk_realm_tree(context, &ctx->client->realm,
                                &ctx->server->realm,
                                &tgt_princ_list, KRB5_REALM_BRANCH_CHAR);
    if (code != 0)
        return code;

    /* Count the number of principals and allocate the realm path. */
    for (nrealms = 0; tgt_princ_list[nrealms]; nrealms++);
    assert(nrealms > 1);
    realm_path = k5alloc((nrealms + 1) * sizeof(*realm_path), &code);
    if (realm_path == NULL)
        goto cleanup;

    /* Steal the remote realm field from each TGT principal. */
    for (i = 0; i < nrealms; i++) {
        assert(tgt_princ_list[i]->length == 2);
        realm_path[i] = tgt_princ_list[i]->data[1];
        tgt_princ_list[i]->data[1].data = NULL;
    }
    realm_path[nrealms] = empty_data();

    /* Initialize the realm path fields in ctx. */
    krb5int_free_data_list(context, ctx->realm_path);
    ctx->realm_path = realm_path;
    ctx->last_realm = realm_path + nrealms - 1;
    ctx->cur_realm = realm_path;
    ctx->next_realm = ctx->last_realm;
    realm_path = NULL;

cleanup:
    krb5_free_realm_tree(context, tgt_princ_list);
    return 0;
}
Beispiel #6
0
/*
 * The request seems to be for a ticket-granting service somewhere else,
 * but we don't have a ticket for the final TGS.  Try to give the requestor
 * some intermediate realm.
 */
static void
find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server,
                   krb5_boolean *more, int *nprincs)
{
    krb5_error_code retval;
    krb5_principal *plist, *pl2;
    krb5_data tmp;

    *nprincs = 0;
    *more = FALSE;

    /*
     * Call to krb5_princ_component is normally not safe but is so
     * here only because find_alternate_tgs() is only called from
     * somewhere that has already checked the number of components in
     * the principal.
     */
    if ((retval = krb5_walk_realm_tree(kdc_context, 
      krb5_princ_realm(kdc_context, request->server),
      krb5_princ_component(kdc_context, request->server, 1),
                      &plist, KRB5_REALM_BRANCH_CHAR)))
        return;

    /* move to the end */
    for (pl2 = plist; *pl2; pl2++);

    /* the first entry in this array is for krbtgt/local@local, so we
       ignore it */
    while (--pl2 > plist) {
        *nprincs = 1;
        tmp = *krb5_princ_realm(kdc_context, *pl2);
        krb5_princ_set_realm(kdc_context, *pl2,
             krb5_princ_realm(kdc_context, tgs_server));
        retval = get_principal(kdc_context, *pl2, server, nprincs, more);
        krb5_princ_set_realm(kdc_context, *pl2, &tmp);
        if (retval) {
            *nprincs = 0;
            *more = FALSE;
            krb5_free_realm_tree(kdc_context, plist);
            return;
        }
        if (*more) {
            krb5_db_free_principal(kdc_context, server, *nprincs);
            continue;
        } else if (*nprincs == 1) {
            /* Found it! */
            krb5_principal tmpprinc;

            tmp = *krb5_princ_realm(kdc_context, *pl2);
            krb5_princ_set_realm(kdc_context, *pl2,
                 krb5_princ_realm(kdc_context, tgs_server));
            if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) {
                                              krb5_db_free_principal(kdc_context, server, *nprincs);
                                              krb5_princ_set_realm(kdc_context, *pl2, &tmp);
                                              continue;
            }
            krb5_princ_set_realm(kdc_context, *pl2, &tmp);

            krb5_free_principal(kdc_context, request->server);
            request->server = tmpprinc;
            log_tgs_alt_tgt(request->server);
            krb5_free_realm_tree(kdc_context, plist);
            return;
        }
        krb5_db_free_principal(kdc_context, server, *nprincs);
        continue;
    }

    *nprincs = 0;
    *more = FALSE;
    krb5_free_realm_tree(kdc_context, plist);
    return;
}
Beispiel #7
0
/*
 * The request seems to be for a ticket-granting service somewhere else,
 * but we don't have a ticket for the final TGS.  Try to give the requestor
 * some intermediate realm.
 */
static krb5_error_code
find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry **server_ptr)
{
    krb5_error_code retval;
    krb5_principal *plist = NULL, *pl2, tmpprinc;
    krb5_data tmp;
    krb5_db_entry *server = NULL;

    *server_ptr = NULL;

    /*
     * Call to krb5_princ_component is normally not safe but is so
     * here only because find_alternate_tgs() is only called from
     * somewhere that has already checked the number of components in
     * the principal.
     */
    if ((retval = krb5_walk_realm_tree(kdc_context,
                                       krb5_princ_realm(kdc_context, request->server),
                                       krb5_princ_component(kdc_context, request->server, 1),
                                       &plist, KRB5_REALM_BRANCH_CHAR)))
        return retval;

    /* move to the end */
    for (pl2 = plist; *pl2; pl2++);

    /* the first entry in this array is for krbtgt/local@local, so we
       ignore it */
    while (--pl2 > plist) {
        tmp = *krb5_princ_realm(kdc_context, *pl2);
        krb5_princ_set_realm(kdc_context, *pl2,
                             krb5_princ_realm(kdc_context, tgs_server));
        retval = krb5_db_get_principal(kdc_context, *pl2, 0, &server);
        krb5_princ_set_realm(kdc_context, *pl2, &tmp);
        if (retval == KRB5_KDB_NOENTRY)
            continue;
        else if (retval)
            goto cleanup;

        /* Found it. */
        tmp = *krb5_princ_realm(kdc_context, *pl2);
        krb5_princ_set_realm(kdc_context, *pl2,
                             krb5_princ_realm(kdc_context, tgs_server));
        retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc);
        if (retval)
            goto cleanup;
        krb5_princ_set_realm(kdc_context, *pl2, &tmp);

        krb5_free_principal(kdc_context, request->server);
        request->server = tmpprinc;
        log_tgs_alt_tgt(request->server);
        *server_ptr = server;
        server = NULL;
        goto cleanup;
    }
    retval = KRB5_KDB_NOENTRY;

cleanup:
    krb5_free_realm_tree(kdc_context, plist);
    krb5_db_free_principal(kdc_context, server);
    return retval;
}
Beispiel #8
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);
}
Beispiel #9
0
/*
 * The request seems to be for a ticket-granting service somewhere else,
 * but we don't have a ticket for the final TGS.  Try to give the requestor
 * some intermediate realm.
 */
static void
find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server,
		   krb5_boolean *more, int *nprincs,
		   const krb5_fulladdr *from, char *cname)
{
    krb5_error_code retval;
    krb5_principal *plist, *pl2;
    krb5_data tmp;

    *nprincs = 0;
    *more = FALSE;

    /*
     * Call to krb5_princ_component is normally not safe but is so
     * here only because find_alternate_tgs() is only called from
     * somewhere that has already checked the number of components in
     * the principal.
     */
    if ((retval = krb5_walk_realm_tree(kdc_context, 
		      krb5_princ_realm(kdc_context, request->server),
		      krb5_princ_component(kdc_context, request->server, 1),
				      &plist, KRB5_REALM_BRANCH_CHAR)))
	return;

    /* move to the end */
    for (pl2 = plist; *pl2; pl2++);

    /* the first entry in this array is for krbtgt/local@local, so we
       ignore it */
    while (--pl2 > plist) {
	*nprincs = 1;
	tmp = *krb5_princ_realm(kdc_context, *pl2);
	krb5_princ_set_realm(kdc_context, *pl2, 
			     krb5_princ_realm(kdc_context, tgs_server));
	retval = krb5_db_get_principal(kdc_context, *pl2, server, nprincs, more);
	krb5_princ_set_realm(kdc_context, *pl2, &tmp);
	if (retval) {
	    *nprincs = 0;
	    *more = FALSE;
	    krb5_free_realm_tree(kdc_context, plist);
	    return;
	}
	if (*more) {
	    krb5_db_free_principal(kdc_context, server, *nprincs);
	    continue;
	} else if (*nprincs == 1) {
	    /* Found it! */
	    krb5_principal tmpprinc;
	    char *sname;

	    tmp = *krb5_princ_realm(kdc_context, *pl2);
	    krb5_princ_set_realm(kdc_context, *pl2, 
				 krb5_princ_realm(kdc_context, tgs_server));
	    if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) {
		krb5_db_free_principal(kdc_context, server, *nprincs);
		krb5_princ_set_realm(kdc_context, *pl2, &tmp);
		continue;
	    }
	    krb5_princ_set_realm(kdc_context, *pl2, &tmp);

	    krb5_free_principal(kdc_context, request->server);
	    request->server = tmpprinc;
	    if (krb5_unparse_name(kdc_context, request->server, &sname)) {

		audit_krb5kdc_tgs_req_alt_tgt(
			(struct in_addr *)from->address->contents,
			(in_port_t)from->port,
			0, cname, "<unparseable>", 0);
		krb5_klog_syslog(LOG_INFO,
		       "TGS_REQ: issuing alternate <un-unparseable> TGT");
	    } else {
		limit_string(sname);
		audit_krb5kdc_tgs_req_alt_tgt(
			(struct in_addr *)from->address->contents,
			(in_port_t)from->port,
			0, cname, sname, 0);
		krb5_klog_syslog(LOG_INFO,
		       "TGS_REQ: issuing TGT %s", sname);
		free(sname);
	    }
	    return;
	}
	krb5_db_free_principal(kdc_context, server, *nprincs);
	continue;
    }

    *nprincs = 0;
    *more = FALSE;
    krb5_free_realm_tree(kdc_context, plist);
    return;
}