155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kafs_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: common.c 15461 2005-06-16 22:52:33Z lha $");
3755682Smarkm
3855682Smarkm#define AUTH_SUPERUSER "afs"
3955682Smarkm
4055682Smarkm/*
4155682Smarkm * Here only ASCII characters are relevant.
4255682Smarkm */
4355682Smarkm
4455682Smarkm#define IsAsciiLower(c) ('a' <= (c) && (c) <= 'z')
4555682Smarkm
4655682Smarkm#define ToAsciiUpper(c) ((c) - 'a' + 'A')
4755682Smarkm
48120945Snectarstatic void (*kafs_verbose)(void *, const char *);
49120945Snectarstatic void *kafs_verbose_ctx;
50120945Snectar
51120945Snectarvoid
52120945Snectar_kafs_foldup(char *a, const char *b)
5355682Smarkm{
5455682Smarkm  for (; *b; a++, b++)
5555682Smarkm    if (IsAsciiLower(*b))
5655682Smarkm      *a = ToAsciiUpper(*b);
5755682Smarkm    else
5855682Smarkm      *a = *b;
5955682Smarkm  *a = '\0';
6055682Smarkm}
6155682Smarkm
62120945Snectarvoid
63120945Snectarkafs_set_verbose(void (*f)(void *, const char *), void *ctx)
64120945Snectar{
65120945Snectar    if (f) {
66120945Snectar	kafs_verbose = f;
67120945Snectar	kafs_verbose_ctx = ctx;
68120945Snectar    }
69120945Snectar}
70120945Snectar
7155682Smarkmint
72120945Snectarkafs_settoken_rxkad(const char *cell, struct ClearToken *ct,
73120945Snectar		    void *ticket, size_t ticket_len)
7455682Smarkm{
7555682Smarkm    struct ViceIoctl parms;
76120945Snectar    char buf[2048], *t;
7755682Smarkm    int32_t sizeof_x;
7855682Smarkm
7955682Smarkm    t = buf;
8055682Smarkm    /*
8155682Smarkm     * length of secret token followed by secret token
8255682Smarkm     */
83120945Snectar    sizeof_x = ticket_len;
8455682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
8555682Smarkm    t += sizeof(sizeof_x);
86120945Snectar    memcpy(t, ticket, sizeof_x);
8755682Smarkm    t += sizeof_x;
8855682Smarkm    /*
8955682Smarkm     * length of clear token followed by clear token
9055682Smarkm     */
91120945Snectar    sizeof_x = sizeof(*ct);
9255682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
9355682Smarkm    t += sizeof(sizeof_x);
94120945Snectar    memcpy(t, ct, sizeof_x);
9555682Smarkm    t += sizeof_x;
9655682Smarkm
9755682Smarkm    /*
9855682Smarkm     * do *not* mark as primary cell
9955682Smarkm     */
10055682Smarkm    sizeof_x = 0;
10155682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
10255682Smarkm    t += sizeof(sizeof_x);
10355682Smarkm    /*
10455682Smarkm     * follow with cell name
10555682Smarkm     */
10655682Smarkm    sizeof_x = strlen(cell) + 1;
10755682Smarkm    memcpy(t, cell, sizeof_x);
10855682Smarkm    t += sizeof_x;
10955682Smarkm
11055682Smarkm    /*
11155682Smarkm     * Build argument block
11255682Smarkm     */
11355682Smarkm    parms.in = buf;
11455682Smarkm    parms.in_size = t - buf;
11555682Smarkm    parms.out = 0;
11655682Smarkm    parms.out_size = 0;
117120945Snectar
118120945Snectar    return k_pioctl(0, VIOCSETTOK, &parms, 0);
11955682Smarkm}
12055682Smarkm
121120945Snectarvoid
122120945Snectar_kafs_fixup_viceid(struct ClearToken *ct, uid_t uid)
123120945Snectar{
124120945Snectar#define ODD(x) ((x) & 1)
125120945Snectar    /* According to Transarc conventions ViceId is valid iff
126120945Snectar     * (EndTimestamp - BeginTimestamp) is odd. By decrementing EndTime
127120945Snectar     * the transformations:
128120945Snectar     *
129120945Snectar     * (issue_date, life) -> (StartTime, EndTime) -> (issue_date, life)
130120945Snectar     * preserves the original values.
131120945Snectar     */
132120945Snectar    if (uid != 0)		/* valid ViceId */
133120945Snectar    {
134120945Snectar	if (!ODD(ct->EndTimestamp - ct->BeginTimestamp))
135120945Snectar	    ct->EndTimestamp--;
136120945Snectar    }
137120945Snectar    else			/* not valid ViceId */
138120945Snectar    {
139120945Snectar	if (ODD(ct->EndTimestamp - ct->BeginTimestamp))
140120945Snectar	    ct->EndTimestamp--;
141120945Snectar    }
142120945Snectar}
143120945Snectar
144120945Snectar
145120945Snectarint
146120945Snectar_kafs_v4_to_kt(CREDENTIALS *c, uid_t uid, struct kafs_token *kt)
147120945Snectar{
148120945Snectar    kt->ticket = NULL;
149120945Snectar
150120945Snectar    if (c->ticket_st.length > MAX_KTXT_LEN)
151120945Snectar	return EINVAL;
152120945Snectar
153120945Snectar    kt->ticket = malloc(c->ticket_st.length);
154120945Snectar    if (kt->ticket == NULL)
155120945Snectar	return ENOMEM;
156120945Snectar    kt->ticket_len = c->ticket_st.length;
157120945Snectar    memcpy(kt->ticket, c->ticket_st.dat, kt->ticket_len);
158120945Snectar
159120945Snectar    /*
160120945Snectar     * Build a struct ClearToken
161120945Snectar     */
162120945Snectar    kt->ct.AuthHandle = c->kvno;
163120945Snectar    memcpy (kt->ct.HandShakeKey, c->session, sizeof(c->session));
164120945Snectar    kt->ct.ViceId = uid;
165120945Snectar    kt->ct.BeginTimestamp = c->issue_date;
166120945Snectar    kt->ct.EndTimestamp = krb_life_to_time(c->issue_date, c->lifetime);
167120945Snectar
168120945Snectar    _kafs_fixup_viceid(&kt->ct, uid);
169120945Snectar
170120945Snectar    return 0;
171120945Snectar}
172120945Snectar
17355682Smarkm/* Try to get a db-server for an AFS cell from a AFSDB record */
17455682Smarkm
17555682Smarkmstatic int
17655682Smarkmdns_find_cell(const char *cell, char *dbserver, size_t len)
17755682Smarkm{
17855682Smarkm    struct dns_reply *r;
17955682Smarkm    int ok = -1;
18055682Smarkm    r = dns_lookup(cell, "afsdb");
18155682Smarkm    if(r){
18255682Smarkm	struct resource_record *rr = r->head;
18355682Smarkm	while(rr){
18455682Smarkm	    if(rr->type == T_AFSDB && rr->u.afsdb->preference == 1){
18555682Smarkm		strlcpy(dbserver,
18655682Smarkm				rr->u.afsdb->domain,
18755682Smarkm				len);
18855682Smarkm		ok = 0;
18955682Smarkm		break;
19055682Smarkm	    }
19155682Smarkm	    rr = rr->next;
19255682Smarkm	}
19355682Smarkm	dns_free_data(r);
19455682Smarkm    }
19555682Smarkm    return ok;
19655682Smarkm}
19755682Smarkm
19855682Smarkm
19955682Smarkm/*
20055682Smarkm * Try to find the cells we should try to klog to in "file".
20155682Smarkm */
20255682Smarkmstatic void
203178825Sdfrfind_cells(const char *file, char ***cells, int *idx)
20455682Smarkm{
20555682Smarkm    FILE *f;
20655682Smarkm    char cell[64];
20755682Smarkm    int i;
208178825Sdfr    int ind = *idx;
20955682Smarkm
21055682Smarkm    f = fopen(file, "r");
21155682Smarkm    if (f == NULL)
21255682Smarkm	return;
21355682Smarkm    while (fgets(cell, sizeof(cell), f)) {
21455682Smarkm	char *t;
21555682Smarkm	t = cell + strlen(cell);
21655682Smarkm	for (; t >= cell; t--)
21755682Smarkm	  if (*t == '\n' || *t == '\t' || *t == ' ')
21855682Smarkm	    *t = 0;
21955682Smarkm	if (cell[0] == '\0' || cell[0] == '#')
22055682Smarkm	    continue;
22155682Smarkm	for(i = 0; i < ind; i++)
22255682Smarkm	    if(strcmp((*cells)[i], cell) == 0)
22355682Smarkm		break;
22455682Smarkm	if(i == ind){
22555682Smarkm	    char **tmp;
22655682Smarkm
22755682Smarkm	    tmp = realloc(*cells, (ind + 1) * sizeof(**cells));
22855682Smarkm	    if (tmp == NULL)
22955682Smarkm		break;
23055682Smarkm	    *cells = tmp;
23155682Smarkm	    (*cells)[ind] = strdup(cell);
23255682Smarkm	    if ((*cells)[ind] == NULL)
23355682Smarkm		break;
23455682Smarkm	    ++ind;
23555682Smarkm	}
23655682Smarkm    }
23755682Smarkm    fclose(f);
238178825Sdfr    *idx = ind;
23955682Smarkm}
24055682Smarkm
24155682Smarkm/*
24255682Smarkm * Get tokens for all cells[]
24355682Smarkm */
24455682Smarkmstatic int
245178825Sdfrafslog_cells(struct kafs_data *data, char **cells, int max, uid_t uid,
24655682Smarkm	     const char *homedir)
24755682Smarkm{
24855682Smarkm    int ret = 0;
24955682Smarkm    int i;
25055682Smarkm    for (i = 0; i < max; i++) {
25155682Smarkm        int er = (*data->afslog_uid)(data, cells[i], 0, uid, homedir);
25255682Smarkm	if (er)
25355682Smarkm	    ret = er;
25455682Smarkm    }
25555682Smarkm    return ret;
25655682Smarkm}
25755682Smarkm
25855682Smarkmint
259178825Sdfr_kafs_afslog_all_local_cells(struct kafs_data *data,
260178825Sdfr			     uid_t uid, const char *homedir)
26155682Smarkm{
26255682Smarkm    int ret;
26355682Smarkm    char **cells = NULL;
264178825Sdfr    int idx = 0;
26555682Smarkm
26655682Smarkm    if (homedir == NULL)
26755682Smarkm	homedir = getenv("HOME");
26855682Smarkm    if (homedir != NULL) {
26955682Smarkm	char home[MaxPathLen];
27055682Smarkm	snprintf(home, sizeof(home), "%s/.TheseCells", homedir);
271178825Sdfr	find_cells(home, &cells, &idx);
27255682Smarkm    }
273178825Sdfr    find_cells(_PATH_THESECELLS, &cells, &idx);
274178825Sdfr    find_cells(_PATH_THISCELL, &cells, &idx);
275178825Sdfr    find_cells(_PATH_ARLA_THESECELLS, &cells, &idx);
276178825Sdfr    find_cells(_PATH_ARLA_THISCELL, &cells, &idx);
277178825Sdfr    find_cells(_PATH_OPENAFS_DEBIAN_THESECELLS, &cells, &idx);
278178825Sdfr    find_cells(_PATH_OPENAFS_DEBIAN_THISCELL, &cells, &idx);
279178825Sdfr    find_cells(_PATH_OPENAFS_MACOSX_THESECELLS, &cells, &idx);
280178825Sdfr    find_cells(_PATH_OPENAFS_MACOSX_THISCELL, &cells, &idx);
281178825Sdfr    find_cells(_PATH_ARLA_DEBIAN_THESECELLS, &cells, &idx);
282178825Sdfr    find_cells(_PATH_ARLA_DEBIAN_THISCELL, &cells, &idx);
283178825Sdfr    find_cells(_PATH_ARLA_OPENBSD_THESECELLS, &cells, &idx);
284178825Sdfr    find_cells(_PATH_ARLA_OPENBSD_THISCELL, &cells, &idx);
28555682Smarkm
286178825Sdfr    ret = afslog_cells(data, cells, idx, uid, homedir);
287178825Sdfr    while(idx > 0)
288178825Sdfr	free(cells[--idx]);
28955682Smarkm    free(cells);
29055682Smarkm    return ret;
29155682Smarkm}
29255682Smarkm
29355682Smarkm
29490926Snectarstatic int
295178825Sdfrfile_find_cell(struct kafs_data *data,
296178825Sdfr	       const char *cell, char **realm, int exact)
29755682Smarkm{
29855682Smarkm    FILE *F;
29955682Smarkm    char buf[1024];
30055682Smarkm    char *p;
30155682Smarkm    int ret = -1;
30255682Smarkm
30355682Smarkm    if ((F = fopen(_PATH_CELLSERVDB, "r"))
30490926Snectar	|| (F = fopen(_PATH_ARLA_CELLSERVDB, "r"))
30590926Snectar	|| (F = fopen(_PATH_OPENAFS_DEBIAN_CELLSERVDB, "r"))
306178825Sdfr	|| (F = fopen(_PATH_OPENAFS_MACOSX_CELLSERVDB, "r"))
30790926Snectar	|| (F = fopen(_PATH_ARLA_DEBIAN_CELLSERVDB, "r"))) {
30855682Smarkm	while (fgets(buf, sizeof(buf), F)) {
30990926Snectar	    int cmp;
31090926Snectar
31155682Smarkm	    if (buf[0] != '>')
31255682Smarkm		continue; /* Not a cell name line, try next line */
31390926Snectar	    p = buf;
31490926Snectar	    strsep(&p, " \t\n#");
31590926Snectar
31690926Snectar	    if (exact)
31790926Snectar		cmp = strcmp(buf + 1, cell);
31890926Snectar	    else
31990926Snectar		cmp = strncmp(buf + 1, cell, strlen(cell));
32090926Snectar
32190926Snectar	    if (cmp == 0) {
32255682Smarkm		/*
32355682Smarkm		 * We found the cell name we're looking for.
32455682Smarkm		 * Read next line on the form ip-address '#' hostname
32555682Smarkm		 */
32655682Smarkm		if (fgets(buf, sizeof(buf), F) == NULL)
32755682Smarkm		    break;	/* Read failed, give up */
32855682Smarkm		p = strchr(buf, '#');
32955682Smarkm		if (p == NULL)
33055682Smarkm		    break;	/* No '#', give up */
33155682Smarkm		p++;
33255682Smarkm		if (buf[strlen(buf) - 1] == '\n')
33355682Smarkm		    buf[strlen(buf) - 1] = '\0';
33455682Smarkm		*realm = (*data->get_realm)(data, p);
33555682Smarkm		if (*realm && **realm != '\0')
33655682Smarkm		    ret = 0;
33755682Smarkm		break;		/* Won't try any more */
33855682Smarkm	    }
33955682Smarkm	}
34055682Smarkm	fclose(F);
34155682Smarkm    }
34290926Snectar    return ret;
34390926Snectar}
34490926Snectar
345178825Sdfr/* Find the realm associated with cell. Do this by opening CellServDB
346178825Sdfr   file and getting the realm-of-host for the first VL-server for the
347178825Sdfr   cell.
34890926Snectar
34990926Snectar   This does not work when the VL-server is living in one realm, but
35090926Snectar   the cell it is serving is living in another realm.
35190926Snectar
35290926Snectar   Return 0 on success, -1 otherwise.
35390926Snectar   */
35490926Snectar
35590926Snectarint
356178825Sdfr_kafs_realm_of_cell(struct kafs_data *data,
357178825Sdfr		    const char *cell, char **realm)
35890926Snectar{
35990926Snectar    char buf[1024];
36090926Snectar    int ret;
36190926Snectar
36290926Snectar    ret = file_find_cell(data, cell, realm, 1);
36390926Snectar    if (ret == 0)
36490926Snectar	return ret;
36590926Snectar    if (dns_find_cell(cell, buf, sizeof(buf)) == 0) {
36690926Snectar	*realm = (*data->get_realm)(data, buf);
36755682Smarkm	if(*realm != NULL)
36890926Snectar	    return 0;
36955682Smarkm    }
37090926Snectar    return file_find_cell(data, cell, realm, 0);
37155682Smarkm}
37255682Smarkm
373120945Snectarstatic int
374178825Sdfr_kafs_try_get_cred(struct kafs_data *data, const char *user, const char *cell,
375120945Snectar		   const char *realm, uid_t uid, struct kafs_token *kt)
376120945Snectar{
377120945Snectar    int ret;
378120945Snectar
379120945Snectar    ret = (*data->get_cred)(data, user, cell, realm, uid, kt);
380120945Snectar    if (kafs_verbose) {
381120945Snectar	char *str;
382120945Snectar	asprintf(&str, "%s tried afs%s%s@%s -> %d",
383120945Snectar		 data->name, cell[0] == '\0' ? "" : "/",
384120945Snectar		 cell, realm, ret);
385120945Snectar	(*kafs_verbose)(kafs_verbose_ctx, str);
386120945Snectar	free(str);
387120945Snectar    }
388120945Snectar
389120945Snectar    return ret;
390120945Snectar}
391120945Snectar
392120945Snectar
39355682Smarkmint
394178825Sdfr_kafs_get_cred(struct kafs_data *data,
395120945Snectar	       const char *cell,
396120945Snectar	       const char *realm_hint,
397120945Snectar	       const char *realm,
398120945Snectar	       uid_t uid,
399120945Snectar	       struct kafs_token *kt)
40055682Smarkm{
40155682Smarkm    int ret = -1;
40255682Smarkm    char *vl_realm;
40355682Smarkm    char CELL[64];
40455682Smarkm
405178825Sdfr    /* We're about to find the realm that holds the key for afs in
40655682Smarkm     * the specified cell. The problem is that null-instance
40755682Smarkm     * afs-principals are common and that hitting the wrong realm might
40855682Smarkm     * yield the wrong afs key. The following assumptions were made.
40955682Smarkm     *
41055682Smarkm     * Any realm passed to us is preferred.
41155682Smarkm     *
41255682Smarkm     * If there is a realm with the same name as the cell, it is most
41355682Smarkm     * likely the correct realm to talk to.
41455682Smarkm     *
41555682Smarkm     * In most (maybe even all) cases the database servers of the cell
41655682Smarkm     * will live in the realm we are looking for.
41755682Smarkm     *
41855682Smarkm     * Try the local realm, but if the previous cases fail, this is
41955682Smarkm     * really a long shot.
42055682Smarkm     *
42155682Smarkm     */
42255682Smarkm
42355682Smarkm    /* comments on the ordering of these tests */
42455682Smarkm
42555682Smarkm    /* If the user passes a realm, she probably knows something we don't
426102644Snectar     * know and we should try afs@realm_hint.
42755682Smarkm     */
42855682Smarkm
42955682Smarkm    if (realm_hint) {
430120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
431120945Snectar				 cell, realm_hint, uid, kt);
43255682Smarkm	if (ret == 0) return 0;
433120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
434120945Snectar				 "", realm_hint, uid, kt);
43555682Smarkm	if (ret == 0) return 0;
43655682Smarkm    }
43755682Smarkm
438120945Snectar    _kafs_foldup(CELL, cell);
43955682Smarkm
44055682Smarkm    /*
44155682Smarkm     * If cell == realm we don't need no cross-cell authentication.
44255682Smarkm     * Try afs@REALM.
44355682Smarkm     */
44455682Smarkm    if (strcmp(CELL, realm) == 0) {
445120945Snectar        ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
446120945Snectar				 "", realm, uid, kt);
44755682Smarkm	if (ret == 0) return 0;
44855682Smarkm	/* Try afs.cell@REALM below. */
44955682Smarkm    }
45055682Smarkm
45155682Smarkm    /*
45255682Smarkm     * If the AFS servers have a file /usr/afs/etc/krb.conf containing
45355682Smarkm     * REALM we still don't have to resort to cross-cell authentication.
45455682Smarkm     * Try afs.cell@REALM.
45555682Smarkm     */
456120945Snectar    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
457120945Snectar			     cell, realm, uid, kt);
45855682Smarkm    if (ret == 0) return 0;
45955682Smarkm
46055682Smarkm    /*
46155682Smarkm     * We failed to get ``first class tickets'' for afs,
46255682Smarkm     * fall back to cross-cell authentication.
46355682Smarkm     * Try afs@CELL.
46455682Smarkm     * Try afs.cell@CELL.
46555682Smarkm     */
466120945Snectar    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
467120945Snectar			     "", CELL, uid, kt);
46855682Smarkm    if (ret == 0) return 0;
469120945Snectar    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
470120945Snectar			     cell, CELL, uid, kt);
47155682Smarkm    if (ret == 0) return 0;
47255682Smarkm
47355682Smarkm    /*
47455682Smarkm     * Perhaps the cell doesn't correspond to any realm?
47555682Smarkm     * Use realm of first volume location DB server.
47655682Smarkm     * Try afs.cell@VL_REALM.
47755682Smarkm     * Try afs@VL_REALM???
47855682Smarkm     */
47955682Smarkm    if (_kafs_realm_of_cell(data, cell, &vl_realm) == 0
48055682Smarkm	&& strcmp(vl_realm, realm) != 0
48155682Smarkm	&& strcmp(vl_realm, CELL) != 0) {
482120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
483120945Snectar				 cell, vl_realm, uid, kt);
48455682Smarkm	if (ret)
485120945Snectar	    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
486120945Snectar				     "", vl_realm, uid, kt);
48755682Smarkm	free(vl_realm);
48855682Smarkm	if (ret == 0) return 0;
48955682Smarkm    }
49055682Smarkm
49155682Smarkm    return ret;
49255682Smarkm}
493