Exemplo n.º 1
0
/*
 * Log to a cell.  If the cell has already been logged to, return without
 * doing anything.  Otherwise, log to it and mark that it has been logged
 * to.
 */
static int auth_to_cell(krb5_context context, char *user, char *cell, char *realm)
{
    int status = 0;
    char username[BUFSIZ];	/* To hold client username structure */
    afs_int32 viceId;		/* AFS uid of user */

    char name[ANAME_SZ];	/* Name of afs key */
    char primary_instance[INST_SZ];	/* Instance of afs key */
    char secondary_instance[INST_SZ];	/* Backup instance to try */
    int try_secondary = 0;		/* Flag to indicate if we try second */
    char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
    char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
    char local_cell[MAXCELLCHARS+1];
    char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
    static char lastcell[MAXCELLCHARS+1] = { 0 };
    static char confname[512] = { 0 };
    krb5_creds *v5cred = NULL;
    struct ktc_principal aserver;
    struct ktc_principal aclient;
    struct ktc_token atoken, btoken;
    int afssetpag = 0, uid = -1;
    struct passwd *pwd;

    memset(name, 0, sizeof(name));
    memset(primary_instance, 0, sizeof(primary_instance));
    memset(secondary_instance, 0, sizeof(secondary_instance));
    memset(realm_of_user, 0, sizeof(realm_of_user));
    memset(realm_of_cell, 0, sizeof(realm_of_cell));
    syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog starting: user %s uid %d", user, getuid());
    if (confname[0] == '\0') {
        strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
        confname[sizeof(confname) - 2] = '\0';
    }

    /* NULL or empty cell returns information on local cell */
    if ((status = get_cellconfig(cell, &ak_cellconfig,
                                 local_cell, linkedcell))) {
        syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_cellconfig returns %d", status);
        return(status);
    }

    strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
    cell_to_use[MAXCELLCHARS] = 0;

    /*
     * Find out which realm we're supposed to authenticate to.  If one
     * is not included, use the kerberos realm found in the credentials
     * cache.
     */

    if (realm && realm[0]) {
        strcpy(realm_of_cell, realm);
    }
    else {
        char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, FALSE);

        if (!afs_realm) {
            syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
            return AFSCONF_FAILURE;
        }

        strcpy(realm_of_cell, afs_realm);
    }

    /* We use the afs.<cellname> convention here...
     *
     * Doug Engert's original code had principals of the form:
     *
     * "afsx/cell@realm"
     *
     * in the KDC, so the name wouldn't conflict with DFS.  Since we're
     * not using DFS, I changed it just to look for the following
     * principals:
     *
     * afs/<cell>@<realm>
     * afs@<realm>
     *
     * Because people are transitioning from afs@realm to afs/cell,
     * we configure things so that if the first one isn't found, we
     * try the second one.  You can select which one you prefer with
     * a configure option.
     */

    strcpy(name, AFSKEY);

    if (1 || strcasecmp(cell_to_use, realm_of_cell) != 0) {
        strncpy(primary_instance, cell_to_use, sizeof(primary_instance));
        primary_instance[sizeof(primary_instance)-1] = '\0';
        if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
            try_secondary = 1;
            secondary_instance[0] = '\0';
        }
    } else {
        primary_instance[0] = '\0';
        try_secondary = 1;
        strncpy(secondary_instance, cell_to_use,
                sizeof(secondary_instance));
        secondary_instance[sizeof(secondary_instance)-1] = '\0';
    }

    /*
     * Extract the session key from the ticket file and hand-frob an
     * afs style authenticator.
     */

    /*
     * Try to obtain AFS tickets.  Because there are two valid service
     * names, we will try both, but trying the more specific first.
     *
     *	afs/<cell>@<realm> i.e. allow for single name with "."
     * 	afs@<realm>
     */

    status = get_credv5(context, user, name, primary_instance, realm_of_cell,
                        &v5cred);

    if ((status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
            status == KRB5KRB_ERR_GENERIC) && !realm_of_cell[0]) {
        char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, TRUE);

        if (!afs_realm) {
            syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
            return AFSCONF_FAILURE;
        }

        strcpy(realm_of_cell, afs_realm);

        if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
            try_secondary = 1;
            secondary_instance[0] = '\0';
        }

        status = get_credv5(context, user, name, primary_instance,
                            realm_of_cell, &v5cred);
    }
    if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
            status == KRB5KRB_ERR_GENERIC) {
        if (try_secondary)
            status = get_credv5(context, user, name, secondary_instance,
                                realm_of_cell, &v5cred);
    }

    if (status) {
        syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_credv5 returns %d", status);
        return status;
    }

    strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
    strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
    strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);

    /*
     * The default is to use rxkad2b, which means we put in a full
     * V5 ticket.  If the user specifies -524, we talk to the
     * 524 ticket converter.
     */

    {
        char *p;
        int len;

        len = min(get_princ_len(context, v5cred->client, 0),
                  second_comp(context, v5cred->client) ?
                  MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1);
        strncpy(username, get_princ_str(context, v5cred->client, 0), len);
        username[len] = '\0';

        if (second_comp(context, v5cred->client)) {
            strcat(username, ".");
            p = username + strlen(username);
            len = min(get_princ_len(context, v5cred->client, 1),
                      MAXKTCNAMELEN - strlen(username) - 1);
            strncpy(p, get_princ_str(context, v5cred->client, 1), len);
            p[len] = '\0';
        }

        memset(&atoken, 0, sizeof(atoken));
        atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
        atoken.startTime = v5cred->times.starttime;;
        atoken.endTime = v5cred->times.endtime;
        memcpy(&atoken.sessionKey, get_cred_keydata(v5cred),
               get_cred_keylen(v5cred));
        atoken.ticketLen = v5cred->ticket.length;
        memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
    }

    if ((status = get_user_realm(context, realm_of_user))) {
        syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_user_realm returns %d", status);
        return KRB5_REALM_UNKNOWN;
    }
    if (strcmp(realm_of_user, realm_of_cell)) {
        strcat(username, "@");
        strcat(username, realm_of_user);
    }

    strcpy(lastcell, aserver.cell);

    /*
     * This is a crock, but it is Transarc's crock, so
     * we have to play along in order to get the
     * functionality.  The way the afs id is stored is
     * as a string in the username field of the token.
     * Contrary to what you may think by looking at
     * the code for tokens, this hack (AFS ID %d) will
     * not work if you change %d to something else.
     */

#if 0
    /* This actually crashes long-running daemons */
    if (!pr_Initialize (0, confname, aserver.cell))
        status = pr_SNameToId (username, &viceId);
    if ((status == 0) && (viceId != ANONYMOUSID))
        sprintf (username, "AFS ID %d", (int) viceId);
#else
    /*
     * This actually only works assuming that your uid and pts space match
     * and probably this works only for the local cell anyway.
     */

    if ((uid = getuid()) == 0) {
        if ((pwd = getpwnam(user)) == NULL) {
            syslog(LOG_AUTH|LOG_ERR, "LAM aklog: getpwnam %s failed", user);
            return AUTH_FAILURE;
        }
    }

    /* Don't do first-time registration. Handle only the simple case */
    if ((status == 0) && (viceId != ANONYMOUSID))
        sprintf (username, "AFS ID %d", ((uid==0)?(int)pwd->pw_uid:(int)uid));
#endif

    /* Reset the "aclient" structure before we call ktc_SetToken.
     * This structure was first set by the ktc_GetToken call when
     * we were comparing whether identical tokens already existed.
     */
    strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
    strcpy(aclient.instance, "");
    strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);

#ifndef AFS_AIX51_ENV
    /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before
     * this routine, it will not add the token. It is not clear what
     * is going on here! So we will do the following operation.
     * On AIX 5 this kills our parent. So we won't.
     */
    write(2,"",0); /* dummy write */
#endif
    afssetpag = (getpagvalue("afs") > 0) ? 1 : 0;
    if (uid == 0) {
        struct sigaction newAction, origAction;
        pid_t cid, pcid;
        int wstatus;

        sigemptyset(&newAction.sa_mask);
        newAction.sa_handler = SIG_DFL;
        newAction.sa_flags = 0;
        status = sigaction(SIGCHLD, &newAction, &origAction);
        if (status) {
            syslog(LOG_AUTH|LOG_ERR, "LAM aklog: sigaction returned %d", status);
            return AUTH_FAILURE;
        }
        syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: in daemon? forking to set tokens");
        cid = fork();
        if (cid <= 0) {
            syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog child: setting tokens");
            setuid(pwd->pw_uid);
            status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
            if (status != 0)
                syslog(LOG_AUTH|LOG_ERR, "LAM aklog child: set tokens, returning %d", status);
            exit((status == 0)?0:255);
        } else {
            do {
                pcid = waitpid(cid, &wstatus, 0);
            } while ((pcid == -1) && (errno == EINTR));
            if ((pcid == cid) && WIFEXITED(wstatus))
                status = WEXITSTATUS(wstatus);
            else
                status = -1;
        }
        syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: collected child status %d", status);
        sigaction(SIGCHLD, &origAction, NULL);
    } else {
        status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
    }
    if (status != 0)
        syslog(LOG_AUTH|LOG_ERR, "LAM aklog: set tokens returned %d", status);
    else
        syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: set tokens, pag %d", getpagvalue("afs"));
    return(status);
}
Exemplo n.º 2
0
static int
CommandProc(struct cmd_syndesc *as, void *arock)
{
    krb5_principal princ = 0;
    char *cell, *pname, **hrealms, *service;
    char service_temp[MAXKTCREALMLEN + 20];
    krb5_creds incred[1], mcred[1], *outcred = 0, *afscred;
    krb5_ccache cc = 0;
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
    krb5_get_init_creds_opt *gic_opts;
#else
    krb5_get_init_creds_opt gic_opts[1];
#endif
    char *tofree = NULL, *outname;
    int code;
    char *what;
    int i, dosetpag, evil, noprdb, id;
#ifdef AFS_RXK5
    int authtype;
#endif
    krb5_data enc_part[1];
    krb5_prompter_fct pf = NULL;
    char *pass = 0;
    void *pa = 0;
    struct kp_arg klog_arg[1];

    char passwd[BUFSIZ];
    struct afsconf_cell cellconfig[1];

    static char rn[] = "klog";	/*Routine name */
    static int Pipe = 0;	/* reading from a pipe */
    static int Silent = 0;	/* Don't want error messages */

    int writeTicketFile = 0;	/* write ticket file to /tmp */

    service = 0;
    memset(incred, 0, sizeof *incred);
    /* blow away command line arguments */
    for (i = 1; i < zero_argc; i++)
	memset(zero_argv[i], 0, strlen(zero_argv[i]));
    zero_argc = 0;
    memset(klog_arg, 0, sizeof *klog_arg);

    /* first determine quiet flag based on -silent switch */
    Silent = (as->parms[aSILENT].items ? 1 : 0);

    if (Silent) {
	afs_set_com_err_hook(silent_errors);
    }

    if ((code = krb5_init_context(&k5context))) {
	afs_com_err(rn, code, "while initializing Kerberos 5 library");
	KLOGEXIT(code);
    }
    if ((code = rx_Init(0))) {
	afs_com_err(rn, code, "while initializing rx");
	KLOGEXIT(code);
    }
    initialize_U_error_table();
    /*initialize_krb5_error_table();*/
    initialize_RXK_error_table();
    initialize_KTC_error_table();
    initialize_ACFG_error_table();
    /* initialize_rx_error_table(); */
    if (!(tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
	afs_com_err(rn, 0, "can't get afs configuration (afsconf_Open(%s))",
	    AFSDIR_CLIENT_ETC_DIRPATH);
	KLOGEXIT(1);
    }

    /*
     * Enable DES enctypes, which are currently still required for AFS.
     * krb5_allow_weak_crypto is MIT Kerberos 1.8.  krb5_enctype_enable is
     * Heimdal.
     */
#if defined(HAVE_KRB5_ENCTYPE_ENABLE)
    i = krb5_enctype_valid(k5context, ETYPE_DES_CBC_CRC);
    if (i)
        krb5_enctype_enable(k5context, ETYPE_DES_CBC_CRC);
#elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
    krb5_allow_weak_crypto(k5context, 1);
#endif

    /* Parse remaining arguments. */

    dosetpag = !! as->parms[aSETPAG].items;
    Pipe = !! as->parms[aPIPE].items;
    writeTicketFile = !! as->parms[aTMP].items;
    noprdb = !! as->parms[aNOPRDB].items;
    evil = (always_evil&1) || !! as->parms[aUNWRAP].items;

#ifdef AFS_RXK5
    authtype = 0;
    if (as->parms[aK5].items)
	authtype |= FORCE_RXK5;
    if (as->parms[aK4].items)
	authtype |= FORCE_RXKAD;
    if (!authtype)
	authtype |= env_afs_rxk5_default();
#endif

    cell = as->parms[aCELL].items ? as->parms[aCELL].items->data : 0;
    if ((code = afsconf_GetCellInfo(tdir, cell, "afsprot", cellconfig))) {
	if (cell)
	    afs_com_err(rn, code, "Can't get cell information for '%s'", cell);
	else
	    afs_com_err(rn, code, "Can't get determine local cell!");
	KLOGEXIT(code);
    }

    if (as->parms[aKRBREALM].items) {
	code = krb5_set_default_realm(k5context,
		as->parms[aKRBREALM].items->data);
	if (code) {
	    afs_com_err(rn, code, "Can't make <%s> the default realm",
		as->parms[aKRBREALM].items->data);
	    KLOGEXIT(code);
	}
    }
    else if ((code = krb5_get_host_realm(k5context, cellconfig->hostName[0], &hrealms))) {
	afs_com_err(rn, code, "Can't get realm for host <%s> in cell <%s>\n",
		cellconfig->hostName[0], cellconfig->name);
	KLOGEXIT(code);
    } else {
	if (hrealms && *hrealms) {
	    code = krb5_set_default_realm(k5context,
		    *hrealms);
	    if (code) {
		afs_com_err(rn, code, "Can't make <%s> the default realm",
		    *hrealms);
		KLOGEXIT(code);
	    }
	}
	if (hrealms) krb5_free_host_realm(k5context, hrealms);
    }

    id = getuid();
    if (as->parms[aPRINCIPAL].items) {
	pname = as->parms[aPRINCIPAL].items->data;
    } else {
	/* No explicit name provided: use Unix uid. */
	struct passwd *pw;
	pw = getpwuid(id);
	if (pw == 0) {
	    afs_com_err(rn, 0,
		"Can't figure out your name from your user id (%d).", id);
	    if (!Silent)
		fprintf(stderr, "%s: Try providing the user name.\n", rn);
	    KLOGEXIT(1);
	}
	pname = pw->pw_name;
    }
    code = krb5_parse_name(k5context, pname, &princ);
    if (code) {
	afs_com_err(rn, code, "Can't parse principal <%s>", pname);
	KLOGEXIT(code);
    }

    if (as->parms[aPASSWORD].items) {
	/*
	 * Current argument is the desired password string.  Remember it in
	 * our local buffer, and zero out the argument string - anyone can
	 * see it there with ps!
	 */
	strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
	memset(as->parms[aPASSWORD].items->data, 0,
	       strlen(as->parms[aPASSWORD].items->data));
	pass = passwd;
    }

    /* Get the password if it wasn't provided. */
    if (!pass) {
	if (Pipe) {
	    strncpy(passwd, getpipepass(), sizeof(passwd));
	    pass = passwd;
	} else {
	    pf = klog_prompter;
	    pa = klog_arg;
	}
    }

    service = 0;
#ifdef AFS_RXK5
    if (authtype & FORCE_RXK5) {
	tofree = get_afs_krb5_svc_princ(cellconfig);
	snprintf(service_temp, sizeof service_temp, "%s", tofree);
    } else
#endif
    snprintf (service_temp, sizeof service_temp, "afs/%s", cellconfig->name);

    klog_arg->pp = &pass;
    klog_arg->pstore = passwd;
    klog_arg->allocated = sizeof(passwd);
    /* XXX should allow k5 to prompt in most cases -- what about expired pw?*/
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
    code = krb5_get_init_creds_opt_alloc(k5context, &gic_opts);
    if (code) {
	afs_com_err(rn, code, "Can't allocate get_init_creds options");
	KLOGEXIT(code);
    }
#else
    krb5_get_init_creds_opt_init(gic_opts);
#endif

    for (;;) {
        code = krb5_get_init_creds_password(k5context,
	    incred,
	    princ,
	    pass,
	    pf,	/* prompter */
	    pa,	/* data */
	    0,	/* start_time */
	    0,	/* in_tkt_service */
	    gic_opts);
	if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
            break;
    }
    memset(passwd, 0, sizeof(passwd));
    if (code) {
	char *r = 0;
	if (krb5_get_default_realm(k5context, &r))
	    r = 0;
	if (r)
	    afs_com_err(rn, code, "Unable to authenticate in realm %s", r);
	else
	    afs_com_err(rn, code, "Unable to authenticate to use cell %s",
		cellconfig->name);
	if (r) free(r);
	KLOGEXIT(code);
    }

    for (;;writeTicketFile = 0) {
        if (writeTicketFile) {
            what = "getting default ccache";
            code = krb5_cc_default(k5context, &cc);
        } else {
            what = "krb5_cc_resolve";
            code = krb5_cc_resolve(k5context, "MEMORY:core", &cc);
            if (code) goto Failed;
        }
        what = "initializing ccache";
        code = krb5_cc_initialize(k5context, cc, princ);
        if (code) goto Failed;
        what = "writing Kerberos ticket file";
        code = krb5_cc_store_cred(k5context, cc, incred);
        if (code) goto Failed;
        if (writeTicketFile)
            fprintf(stderr,
                    "Wrote ticket file to %s\n",
                    krb5_cc_get_name(k5context, cc));
        break;
      Failed:
        if (code)
            afs_com_err(rn, code, "%s", what);
        if (writeTicketFile) {
            if (cc) {
                krb5_cc_close(k5context, cc);
                cc = 0;
            }
            continue;
        }
        KLOGEXIT(code);
    }

    for (service = service_temp;;service = "afs") {
        memset(mcred, 0, sizeof *mcred);
        mcred->client = princ;
        code = krb5_parse_name(k5context, service, &mcred->server);
        if (code) {
            afs_com_err(rn, code, "Unable to parse service <%s>\n", service);
            KLOGEXIT(code);
        }
        if (tofree) { free(tofree); tofree = 0; }
        if (!(code = krb5_unparse_name(k5context, mcred->server, &outname)))
            tofree = outname;
        else outname = service;
        code = krb5_get_credentials(k5context, 0, cc, mcred, &outcred);
        krb5_free_principal(k5context, mcred->server);
        if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || service != service_temp) break;
#ifdef AFS_RXK5
        if (authtype & FORCE_RXK5)
            break;
#endif
    }
    afscred = outcred;

    if (code) {
	afs_com_err(rn, code, "Unable to get credentials to use %s", outname);
	KLOGEXIT(code);
    }

#ifdef AFS_RXK5
    if (authtype & FORCE_RXK5) {
	struct ktc_principal aserver[1];
	int viceid = 555;

	memset(aserver, 0, sizeof *aserver);
	strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
	code = ktc_SetK5Token(k5context, aserver, afscred, viceid, dosetpag);
	if (code) {
	    afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
		cellconfig->name);
	    KLOGEXIT(1);
	}
    } else
#endif
    {
	struct ktc_principal aserver[1], aclient[1];
	struct ktc_token atoken[1];

	memset(atoken, 0, sizeof *atoken);
	if (evil) {
	    size_t elen = enc_part->length;
	    atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY;
	    if (afs_krb5_skip_ticket_wrapper(afscred->ticket.data,
			afscred->ticket.length, (char **) &enc_part->data,
			&elen)) {
		afs_com_err(rn, 0, "Can't unwrap %s AFS credential",
		    cellconfig->name);
		KLOGEXIT(1);
	    }
	} else {
	    atoken->kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
	    *enc_part = afscred->ticket;
	}
	atoken->startTime = afscred->times.starttime;
	atoken->endTime = afscred->times.endtime;
	if (tkt_DeriveDesKey(get_creds_enctype(afscred),
			     get_cred_keydata(afscred),
			     get_cred_keylen(afscred), &atoken->sessionKey)) {
	    afs_com_err(rn, 0,
			"Cannot derive DES key from enctype %i of length %u",
			get_creds_enctype(afscred),
			(unsigned)get_cred_keylen(afscred));
	    KLOGEXIT(1);
	}
	memcpy(atoken->ticket, enc_part->data,
	    atoken->ticketLen = enc_part->length);
	memset(aserver, 0, sizeof *aserver);
	strncpy(aserver->name, "afs", 4);
	strncpy(aserver->cell, cellconfig->name, MAXKTCREALMLEN-1);
	memset(aclient, 0, sizeof *aclient);
	i = realm_len(k5context, afscred->client);
	if (i > MAXKTCREALMLEN-1) i = MAXKTCREALMLEN-1;
	memcpy(aclient->cell, realm_data(k5context, afscred->client), i);
	if (!noprdb) {
	    int viceid = 0;
	    k5_to_k4_name(k5context, afscred->client, aclient);
	    code = whoami(atoken, cellconfig, aclient, &viceid);
	    if (code) {
		afs_com_err(rn, code, "Can't get your viceid for cell %s", cellconfig->name);
		*aclient->name = 0;
	    } else
		snprintf(aclient->name, MAXKTCNAMELEN-1, "AFS ID %d", viceid);
	}
	if (!*aclient->name)
	    k5_to_k4_name(k5context, afscred->client, aclient);
	code = ktc_SetToken(aserver, atoken, aclient, dosetpag);
	if (code) {
	    afs_com_err(rn, code, "Unable to store tokens for cell %s\n",
		cellconfig->name);
	    KLOGEXIT(1);
	}
    }

    krb5_free_principal(k5context, princ);
    krb5_free_cred_contents(k5context, incred);
    if (outcred) krb5_free_creds(k5context, outcred);
    if (cc)
	krb5_cc_close(k5context, cc);
    if (tofree) free(tofree);

    return 0;
}