155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kafs_locl.h"
3555682Smarkm
3655682Smarkm#define AUTH_SUPERUSER "afs"
3755682Smarkm
3855682Smarkm/*
3955682Smarkm * Here only ASCII characters are relevant.
4055682Smarkm */
4155682Smarkm
4255682Smarkm#define IsAsciiLower(c) ('a' <= (c) && (c) <= 'z')
4355682Smarkm
4455682Smarkm#define ToAsciiUpper(c) ((c) - 'a' + 'A')
4555682Smarkm
46120945Snectarstatic void (*kafs_verbose)(void *, const char *);
47120945Snectarstatic void *kafs_verbose_ctx;
48120945Snectar
49120945Snectarvoid
50120945Snectar_kafs_foldup(char *a, const char *b)
5155682Smarkm{
5255682Smarkm  for (; *b; a++, b++)
5355682Smarkm    if (IsAsciiLower(*b))
5455682Smarkm      *a = ToAsciiUpper(*b);
5555682Smarkm    else
5655682Smarkm      *a = *b;
5755682Smarkm  *a = '\0';
5855682Smarkm}
5955682Smarkm
60120945Snectarvoid
61120945Snectarkafs_set_verbose(void (*f)(void *, const char *), void *ctx)
62120945Snectar{
63120945Snectar    if (f) {
64120945Snectar	kafs_verbose = f;
65120945Snectar	kafs_verbose_ctx = ctx;
66120945Snectar    }
67120945Snectar}
68120945Snectar
6955682Smarkmint
70120945Snectarkafs_settoken_rxkad(const char *cell, struct ClearToken *ct,
71120945Snectar		    void *ticket, size_t ticket_len)
7255682Smarkm{
7355682Smarkm    struct ViceIoctl parms;
74120945Snectar    char buf[2048], *t;
7555682Smarkm    int32_t sizeof_x;
76233294Sstas
7755682Smarkm    t = buf;
7855682Smarkm    /*
7955682Smarkm     * length of secret token followed by secret token
8055682Smarkm     */
81120945Snectar    sizeof_x = ticket_len;
8255682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
8355682Smarkm    t += sizeof(sizeof_x);
84120945Snectar    memcpy(t, ticket, sizeof_x);
8555682Smarkm    t += sizeof_x;
8655682Smarkm    /*
8755682Smarkm     * length of clear token followed by clear token
8855682Smarkm     */
89120945Snectar    sizeof_x = sizeof(*ct);
9055682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
9155682Smarkm    t += sizeof(sizeof_x);
92120945Snectar    memcpy(t, ct, sizeof_x);
9355682Smarkm    t += sizeof_x;
9455682Smarkm
9555682Smarkm    /*
9655682Smarkm     * do *not* mark as primary cell
9755682Smarkm     */
9855682Smarkm    sizeof_x = 0;
9955682Smarkm    memcpy(t, &sizeof_x, sizeof(sizeof_x));
10055682Smarkm    t += sizeof(sizeof_x);
10155682Smarkm    /*
10255682Smarkm     * follow with cell name
10355682Smarkm     */
10455682Smarkm    sizeof_x = strlen(cell) + 1;
10555682Smarkm    memcpy(t, cell, sizeof_x);
10655682Smarkm    t += sizeof_x;
10755682Smarkm
10855682Smarkm    /*
10955682Smarkm     * Build argument block
11055682Smarkm     */
11155682Smarkm    parms.in = buf;
11255682Smarkm    parms.in_size = t - buf;
11355682Smarkm    parms.out = 0;
11455682Smarkm    parms.out_size = 0;
115120945Snectar
116120945Snectar    return k_pioctl(0, VIOCSETTOK, &parms, 0);
11755682Smarkm}
11855682Smarkm
119120945Snectarvoid
120120945Snectar_kafs_fixup_viceid(struct ClearToken *ct, uid_t uid)
121120945Snectar{
122120945Snectar#define ODD(x) ((x) & 1)
123120945Snectar    /* According to Transarc conventions ViceId is valid iff
124120945Snectar     * (EndTimestamp - BeginTimestamp) is odd. By decrementing EndTime
125120945Snectar     * the transformations:
126120945Snectar     *
127120945Snectar     * (issue_date, life) -> (StartTime, EndTime) -> (issue_date, life)
128120945Snectar     * preserves the original values.
129120945Snectar     */
130120945Snectar    if (uid != 0)		/* valid ViceId */
131120945Snectar    {
132120945Snectar	if (!ODD(ct->EndTimestamp - ct->BeginTimestamp))
133120945Snectar	    ct->EndTimestamp--;
134120945Snectar    }
135120945Snectar    else			/* not valid ViceId */
136120945Snectar    {
137120945Snectar	if (ODD(ct->EndTimestamp - ct->BeginTimestamp))
138120945Snectar	    ct->EndTimestamp--;
139120945Snectar    }
140120945Snectar}
141120945Snectar
14255682Smarkm/* Try to get a db-server for an AFS cell from a AFSDB record */
14355682Smarkm
14455682Smarkmstatic int
14555682Smarkmdns_find_cell(const char *cell, char *dbserver, size_t len)
14655682Smarkm{
147233294Sstas    struct rk_dns_reply *r;
14855682Smarkm    int ok = -1;
149233294Sstas    r = rk_dns_lookup(cell, "afsdb");
15055682Smarkm    if(r){
151233294Sstas	struct rk_resource_record *rr = r->head;
15255682Smarkm	while(rr){
153233294Sstas	    if(rr->type == rk_ns_t_afsdb && rr->u.afsdb->preference == 1){
15455682Smarkm		strlcpy(dbserver,
15555682Smarkm				rr->u.afsdb->domain,
15655682Smarkm				len);
15755682Smarkm		ok = 0;
15855682Smarkm		break;
15955682Smarkm	    }
16055682Smarkm	    rr = rr->next;
16155682Smarkm	}
162233294Sstas	rk_dns_free_data(r);
16355682Smarkm    }
16455682Smarkm    return ok;
16555682Smarkm}
16655682Smarkm
16755682Smarkm
16855682Smarkm/*
16955682Smarkm * Try to find the cells we should try to klog to in "file".
17055682Smarkm */
17155682Smarkmstatic void
172178825Sdfrfind_cells(const char *file, char ***cells, int *idx)
17355682Smarkm{
17455682Smarkm    FILE *f;
17555682Smarkm    char cell[64];
17655682Smarkm    int i;
177178825Sdfr    int ind = *idx;
17855682Smarkm
17955682Smarkm    f = fopen(file, "r");
18055682Smarkm    if (f == NULL)
18155682Smarkm	return;
18255682Smarkm    while (fgets(cell, sizeof(cell), f)) {
18355682Smarkm	char *t;
18455682Smarkm	t = cell + strlen(cell);
18555682Smarkm	for (; t >= cell; t--)
18655682Smarkm	  if (*t == '\n' || *t == '\t' || *t == ' ')
18755682Smarkm	    *t = 0;
18855682Smarkm	if (cell[0] == '\0' || cell[0] == '#')
18955682Smarkm	    continue;
19055682Smarkm	for(i = 0; i < ind; i++)
19155682Smarkm	    if(strcmp((*cells)[i], cell) == 0)
19255682Smarkm		break;
19355682Smarkm	if(i == ind){
19455682Smarkm	    char **tmp;
19555682Smarkm
19655682Smarkm	    tmp = realloc(*cells, (ind + 1) * sizeof(**cells));
19755682Smarkm	    if (tmp == NULL)
19855682Smarkm		break;
19955682Smarkm	    *cells = tmp;
20055682Smarkm	    (*cells)[ind] = strdup(cell);
20155682Smarkm	    if ((*cells)[ind] == NULL)
20255682Smarkm		break;
20355682Smarkm	    ++ind;
20455682Smarkm	}
20555682Smarkm    }
20655682Smarkm    fclose(f);
207178825Sdfr    *idx = ind;
20855682Smarkm}
20955682Smarkm
21055682Smarkm/*
21155682Smarkm * Get tokens for all cells[]
21255682Smarkm */
21355682Smarkmstatic int
214178825Sdfrafslog_cells(struct kafs_data *data, char **cells, int max, uid_t uid,
21555682Smarkm	     const char *homedir)
21655682Smarkm{
21755682Smarkm    int ret = 0;
21855682Smarkm    int i;
21955682Smarkm    for (i = 0; i < max; i++) {
22055682Smarkm        int er = (*data->afslog_uid)(data, cells[i], 0, uid, homedir);
22155682Smarkm	if (er)
22255682Smarkm	    ret = er;
22355682Smarkm    }
22455682Smarkm    return ret;
22555682Smarkm}
22655682Smarkm
22755682Smarkmint
228178825Sdfr_kafs_afslog_all_local_cells(struct kafs_data *data,
229178825Sdfr			     uid_t uid, const char *homedir)
23055682Smarkm{
23155682Smarkm    int ret;
23255682Smarkm    char **cells = NULL;
233178825Sdfr    int idx = 0;
23455682Smarkm
23555682Smarkm    if (homedir == NULL)
23655682Smarkm	homedir = getenv("HOME");
23755682Smarkm    if (homedir != NULL) {
23855682Smarkm	char home[MaxPathLen];
23955682Smarkm	snprintf(home, sizeof(home), "%s/.TheseCells", homedir);
240178825Sdfr	find_cells(home, &cells, &idx);
24155682Smarkm    }
242178825Sdfr    find_cells(_PATH_THESECELLS, &cells, &idx);
243178825Sdfr    find_cells(_PATH_THISCELL, &cells, &idx);
244178825Sdfr    find_cells(_PATH_ARLA_THESECELLS, &cells, &idx);
245178825Sdfr    find_cells(_PATH_ARLA_THISCELL, &cells, &idx);
246178825Sdfr    find_cells(_PATH_OPENAFS_DEBIAN_THESECELLS, &cells, &idx);
247178825Sdfr    find_cells(_PATH_OPENAFS_DEBIAN_THISCELL, &cells, &idx);
248178825Sdfr    find_cells(_PATH_OPENAFS_MACOSX_THESECELLS, &cells, &idx);
249178825Sdfr    find_cells(_PATH_OPENAFS_MACOSX_THISCELL, &cells, &idx);
250178825Sdfr    find_cells(_PATH_ARLA_DEBIAN_THESECELLS, &cells, &idx);
251178825Sdfr    find_cells(_PATH_ARLA_DEBIAN_THISCELL, &cells, &idx);
252178825Sdfr    find_cells(_PATH_ARLA_OPENBSD_THESECELLS, &cells, &idx);
253178825Sdfr    find_cells(_PATH_ARLA_OPENBSD_THISCELL, &cells, &idx);
254233294Sstas
255178825Sdfr    ret = afslog_cells(data, cells, idx, uid, homedir);
256178825Sdfr    while(idx > 0)
257178825Sdfr	free(cells[--idx]);
25855682Smarkm    free(cells);
25955682Smarkm    return ret;
26055682Smarkm}
26155682Smarkm
26255682Smarkm
26390926Snectarstatic int
264233294Sstasfile_find_cell(struct kafs_data *data,
265178825Sdfr	       const char *cell, char **realm, int exact)
26655682Smarkm{
26755682Smarkm    FILE *F;
26855682Smarkm    char buf[1024];
26955682Smarkm    char *p;
27055682Smarkm    int ret = -1;
27155682Smarkm
27255682Smarkm    if ((F = fopen(_PATH_CELLSERVDB, "r"))
27390926Snectar	|| (F = fopen(_PATH_ARLA_CELLSERVDB, "r"))
27490926Snectar	|| (F = fopen(_PATH_OPENAFS_DEBIAN_CELLSERVDB, "r"))
275178825Sdfr	|| (F = fopen(_PATH_OPENAFS_MACOSX_CELLSERVDB, "r"))
27690926Snectar	|| (F = fopen(_PATH_ARLA_DEBIAN_CELLSERVDB, "r"))) {
27755682Smarkm	while (fgets(buf, sizeof(buf), F)) {
27890926Snectar	    int cmp;
27990926Snectar
28055682Smarkm	    if (buf[0] != '>')
28155682Smarkm		continue; /* Not a cell name line, try next line */
28290926Snectar	    p = buf;
28390926Snectar	    strsep(&p, " \t\n#");
28490926Snectar
28590926Snectar	    if (exact)
28690926Snectar		cmp = strcmp(buf + 1, cell);
28790926Snectar	    else
28890926Snectar		cmp = strncmp(buf + 1, cell, strlen(cell));
28990926Snectar
29090926Snectar	    if (cmp == 0) {
29155682Smarkm		/*
29255682Smarkm		 * We found the cell name we're looking for.
29355682Smarkm		 * Read next line on the form ip-address '#' hostname
29455682Smarkm		 */
29555682Smarkm		if (fgets(buf, sizeof(buf), F) == NULL)
29655682Smarkm		    break;	/* Read failed, give up */
29755682Smarkm		p = strchr(buf, '#');
29855682Smarkm		if (p == NULL)
29955682Smarkm		    break;	/* No '#', give up */
30055682Smarkm		p++;
30155682Smarkm		if (buf[strlen(buf) - 1] == '\n')
30255682Smarkm		    buf[strlen(buf) - 1] = '\0';
30355682Smarkm		*realm = (*data->get_realm)(data, p);
30455682Smarkm		if (*realm && **realm != '\0')
30555682Smarkm		    ret = 0;
30655682Smarkm		break;		/* Won't try any more */
30755682Smarkm	    }
30855682Smarkm	}
30955682Smarkm	fclose(F);
31055682Smarkm    }
31190926Snectar    return ret;
31290926Snectar}
31390926Snectar
314178825Sdfr/* Find the realm associated with cell. Do this by opening CellServDB
315178825Sdfr   file and getting the realm-of-host for the first VL-server for the
316178825Sdfr   cell.
31790926Snectar
31890926Snectar   This does not work when the VL-server is living in one realm, but
31990926Snectar   the cell it is serving is living in another realm.
32090926Snectar
32190926Snectar   Return 0 on success, -1 otherwise.
32290926Snectar   */
32390926Snectar
32490926Snectarint
325178825Sdfr_kafs_realm_of_cell(struct kafs_data *data,
326178825Sdfr		    const char *cell, char **realm)
32790926Snectar{
32890926Snectar    char buf[1024];
32990926Snectar    int ret;
33090926Snectar
33190926Snectar    ret = file_find_cell(data, cell, realm, 1);
33290926Snectar    if (ret == 0)
33390926Snectar	return ret;
33490926Snectar    if (dns_find_cell(cell, buf, sizeof(buf)) == 0) {
33590926Snectar	*realm = (*data->get_realm)(data, buf);
33655682Smarkm	if(*realm != NULL)
33790926Snectar	    return 0;
33855682Smarkm    }
33990926Snectar    return file_find_cell(data, cell, realm, 0);
34055682Smarkm}
34155682Smarkm
342120945Snectarstatic int
343178825Sdfr_kafs_try_get_cred(struct kafs_data *data, const char *user, const char *cell,
344120945Snectar		   const char *realm, uid_t uid, struct kafs_token *kt)
345120945Snectar{
346120945Snectar    int ret;
347120945Snectar
348120945Snectar    ret = (*data->get_cred)(data, user, cell, realm, uid, kt);
349120945Snectar    if (kafs_verbose) {
350233294Sstas	const char *estr = (*data->get_error)(data, ret);
351120945Snectar	char *str;
352233294Sstas	asprintf(&str, "%s tried afs%s%s@%s -> %s (%d)",
353233294Sstas		 data->name, cell ? "/" : "",
354233294Sstas		 cell ? cell : "", realm, estr ? estr : "unknown", ret);
355120945Snectar	(*kafs_verbose)(kafs_verbose_ctx, str);
356233294Sstas	if (estr)
357233294Sstas	    (*data->free_error)(data, estr);
358120945Snectar	free(str);
359120945Snectar    }
360120945Snectar
361120945Snectar    return ret;
362120945Snectar}
363120945Snectar
364120945Snectar
36555682Smarkmint
366178825Sdfr_kafs_get_cred(struct kafs_data *data,
367233294Sstas	       const char *cell,
368120945Snectar	       const char *realm_hint,
369120945Snectar	       const char *realm,
370120945Snectar	       uid_t uid,
371120945Snectar	       struct kafs_token *kt)
37255682Smarkm{
37355682Smarkm    int ret = -1;
37455682Smarkm    char *vl_realm;
37555682Smarkm    char CELL[64];
37655682Smarkm
377178825Sdfr    /* We're about to find the realm that holds the key for afs in
37855682Smarkm     * the specified cell. The problem is that null-instance
37955682Smarkm     * afs-principals are common and that hitting the wrong realm might
38055682Smarkm     * yield the wrong afs key. The following assumptions were made.
38155682Smarkm     *
38255682Smarkm     * Any realm passed to us is preferred.
38355682Smarkm     *
38455682Smarkm     * If there is a realm with the same name as the cell, it is most
38555682Smarkm     * likely the correct realm to talk to.
38655682Smarkm     *
38755682Smarkm     * In most (maybe even all) cases the database servers of the cell
38855682Smarkm     * will live in the realm we are looking for.
38955682Smarkm     *
39055682Smarkm     * Try the local realm, but if the previous cases fail, this is
39155682Smarkm     * really a long shot.
39255682Smarkm     *
39355682Smarkm     */
394233294Sstas
39555682Smarkm    /* comments on the ordering of these tests */
39655682Smarkm
39755682Smarkm    /* If the user passes a realm, she probably knows something we don't
398102644Snectar     * know and we should try afs@realm_hint.
39955682Smarkm     */
400233294Sstas
40155682Smarkm    if (realm_hint) {
402120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
403120945Snectar				 cell, realm_hint, uid, kt);
40455682Smarkm	if (ret == 0) return 0;
405120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
406233294Sstas				 NULL, realm_hint, uid, kt);
40755682Smarkm	if (ret == 0) return 0;
40855682Smarkm    }
40955682Smarkm
410120945Snectar    _kafs_foldup(CELL, cell);
41155682Smarkm
41255682Smarkm    /*
413233294Sstas     * If the AFS servers have a file /usr/afs/etc/krb.conf containing
414233294Sstas     * REALM we still don't have to resort to cross-cell authentication.
415233294Sstas     * Try afs.cell@REALM.
416233294Sstas     */
417233294Sstas    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
418233294Sstas			     cell, realm, uid, kt);
419233294Sstas    if (ret == 0) return 0;
420233294Sstas
421233294Sstas    /*
42255682Smarkm     * If cell == realm we don't need no cross-cell authentication.
42355682Smarkm     * Try afs@REALM.
42455682Smarkm     */
42555682Smarkm    if (strcmp(CELL, realm) == 0) {
426120945Snectar        ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
427233294Sstas				 NULL, realm, uid, kt);
42855682Smarkm	if (ret == 0) return 0;
42955682Smarkm    }
43055682Smarkm
43155682Smarkm    /*
43255682Smarkm     * We failed to get ``first class tickets'' for afs,
43355682Smarkm     * fall back to cross-cell authentication.
43455682Smarkm     * Try afs@CELL.
43555682Smarkm     * Try afs.cell@CELL.
43655682Smarkm     */
437120945Snectar    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
438233294Sstas			     NULL, CELL, uid, kt);
43955682Smarkm    if (ret == 0) return 0;
440233294Sstas    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
441120945Snectar			     cell, CELL, uid, kt);
44255682Smarkm    if (ret == 0) return 0;
44355682Smarkm
44455682Smarkm    /*
44555682Smarkm     * Perhaps the cell doesn't correspond to any realm?
44655682Smarkm     * Use realm of first volume location DB server.
44755682Smarkm     * Try afs.cell@VL_REALM.
44855682Smarkm     * Try afs@VL_REALM???
44955682Smarkm     */
45055682Smarkm    if (_kafs_realm_of_cell(data, cell, &vl_realm) == 0
45155682Smarkm	&& strcmp(vl_realm, realm) != 0
45255682Smarkm	&& strcmp(vl_realm, CELL) != 0) {
453120945Snectar	ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
454120945Snectar				 cell, vl_realm, uid, kt);
45555682Smarkm	if (ret)
456120945Snectar	    ret = _kafs_try_get_cred(data, AUTH_SUPERUSER,
457233294Sstas				     NULL, vl_realm, uid, kt);
45855682Smarkm	free(vl_realm);
45955682Smarkm	if (ret == 0) return 0;
46055682Smarkm    }
46155682Smarkm
46255682Smarkm    return ret;
46355682Smarkm}
464