#include "k5-int.h"
#include "int-proto.h"
struct hstate {
char *str;
size_t len;
char *tail;
char *dot;
};
static krb5_error_code
rtree_capath_tree(krb5_context context,
const krb5_data *client,
const krb5_data *server,
char **vals,
krb5_principal **tree);
static krb5_error_code
rtree_capath_vals(krb5_context context,
const krb5_data *client,
const krb5_data *server,
char ***vals);
static krb5_error_code
rtree_hier_tree(krb5_context context,
const krb5_data *client,
const krb5_data *server,
krb5_principal **rettree,
int sep);
static krb5_error_code
rtree_hier_realms(krb5_context context,
const krb5_data *client,
const krb5_data *server,
krb5_data **realms,
size_t *nrealms,
int sep);
static void
free_realmlist(krb5_context context,
krb5_data *realms,
size_t nrealms);
static krb5_error_code
rtree_hier_tweens(krb5_context context,
struct hstate *realm,
krb5_data **tweens,
size_t *ntweens,
int dotail,
int sep);
static void
adjtail(struct hstate *c, struct hstate *s, int sep);
static void
comtail(struct hstate *c, struct hstate *s, int sep);
krb5_error_code
krb5_walk_realm_tree( krb5_context context,
const krb5_data *client,
const krb5_data *server,
krb5_principal **tree,
int realm_sep)
{
krb5_error_code retval = 0;
char **capvals;
if (client->data == NULL || server->data == NULL)
return KRB5_NO_TKT_IN_RLM;
if (data_eq(*client, *server))
return KRB5_NO_TKT_IN_RLM;
retval = rtree_capath_vals(context, client, server, &capvals);
if (retval)
return retval;
if (capvals != NULL) {
retval = rtree_capath_tree(context, client, server, capvals, tree);
return retval;
}
retval = rtree_hier_tree(context, client, server, tree, realm_sep);
return retval;
}
krb5_error_code
k5_client_realm_path(krb5_context context, const krb5_data *client,
const krb5_data *server, krb5_data **rpath_out)
{
krb5_error_code retval;
char **capvals = NULL;
size_t i;
krb5_data *rpath = NULL, d;
retval = rtree_capath_vals(context, client, server, &capvals);
if (retval)
return retval;
if (capvals != NULL && capvals[0] != NULL && *capvals[0] == '.') {
profile_free_list(capvals);
capvals = NULL;
}
for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
rpath = calloc(i + 3, sizeof(*rpath));
if (rpath == NULL)
return ENOMEM;
retval = krb5int_copy_data_contents(context, client, &rpath[0]);
if (retval)
goto cleanup;
for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
d = make_data(capvals[i], strcspn(capvals[i], "\t "));
retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
if (retval)
goto cleanup;
}
retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
if (retval)
goto cleanup;
rpath[i + 2] = empty_data();
*rpath_out = rpath;
rpath = NULL;
cleanup:
profile_free_list(capvals);
krb5int_free_data_list(context, rpath);
return retval;
}
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;
pprinc = &tree[0];
retval = krb5int_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 = krb5int_tgtname(context, &dstrealm, &srcrealm, pprinc++);
if (retval) goto error;
srcrealm = dstrealm;
}
retval = krb5int_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(context, *--pprinc);
*pprinc = NULL;
}
free(tree);
}
return retval;
}
static krb5_error_code
rtree_capath_vals(krb5_context context,
const krb5_data *client,
const krb5_data *server,
char ***vals)
{
krb5_error_code retval = 0;
char *clientz = NULL, *serverz = NULL;
const char *key[4];
*vals = NULL;
clientz = k5memdup0(client->data, client->length, &retval);
if (clientz == NULL)
goto error;
serverz = k5memdup0(server->data, server->length, &retval);
if (serverz == NULL)
goto error;
key[0] = "capaths";
key[1] = clientz;
key[2] = serverz;
key[3] = NULL;
retval = profile_get_values(context->profile, key, vals);
switch (retval) {
case PROF_NO_SECTION:
case PROF_NO_RELATION:
retval = 0;
break;
default:
break;
}
error:
free(clientz);
free(serverz);
return retval;
}
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 = krb5int_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;
}
static krb5_error_code
rtree_hier_realms(krb5_context context,
const krb5_data *client,
const krb5_data *server,
krb5_data **realms,
size_t *nrealms,
int sep)
{
krb5_error_code retval;
struct hstate c, s;
krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp;
size_t nctween, nstween;
*realms = NULL;
*nrealms = 0;
r = rp = NULL;
c.str = client->data;
c.len = client->length;
c.dot = c.tail = NULL;
s.str = server->data;
s.len = server->length;
s.dot = s.tail = NULL;
comtail(&c, &s, sep);
adjtail(&c, &s, sep);
retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
if (retval) goto error;
retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
if (retval) goto error;
rp = r = calloc(nctween + nstween, sizeof(krb5_data));
if (r == NULL) {
retval = ENOMEM;
goto error;
}
for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
retval = krb5int_copy_data_contents(context, twp, rp);
if (retval) goto error;
rp++;
}
for (twp = &stweens[nstween]; twp-- > stweens;) {
retval = krb5int_copy_data_contents(context, twp, rp);
if (retval) goto error;
rp++;
}
error:
free(ctweens);
free(stweens);
if (retval) {
free_realmlist(context, r, rp - r);
return retval;
}
*realms = r;
*nrealms = rp - r;
return 0;
}
static void
free_realmlist(krb5_context context,
krb5_data *realms,
size_t nrealms)
{
size_t i;
for (i = 0; i < nrealms; i++)
krb5_free_data_contents(context, &realms[i]);
free(realms);
}
static krb5_error_code
rtree_hier_tweens(krb5_context context,
struct hstate *realm,
krb5_data **tweens,
size_t *ntweens,
int dotail,
int sep)
{
char *p, *r, *rtail, *lp;
size_t rlen, n;
krb5_data *tws, *ntws;
r = realm->str;
rlen = realm->len;
rtail = realm->tail;
*tweens = ntws = tws = NULL;
*ntweens = n = 0;
for (lp = p = r; p < &r[rlen]; p++) {
if (*p != sep && &p[1] != &r[rlen])
continue;
if (lp == rtail && !dotail)
break;
ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
if (ntws == NULL) {
free(tws);
return ENOMEM;
}
tws = ntws;
tws[n].data = lp;
tws[n].length = &r[rlen] - lp;
n++;
if (lp == rtail)
break;
lp = &p[1];
}
*tweens = tws;
*ntweens = n;
return 0;
}
static void
adjtail(struct hstate *c, struct hstate *s, int sep)
{
int cfull, sfull;
char *cp, *sp;
cp = c->tail;
sp = s->tail;
if (cp == NULL || sp == NULL)
return;
cfull = (cp == c->str || cp[-1] == sep);
sfull = (sp == s->str || sp[-1] == sep);
if (cfull && sfull) {
return;
} else if (c->dot != NULL && s->dot != NULL) {
cp = c->dot + 1;
sp = s->dot + 1;
if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
cp = sp = NULL;
}
} else {
cp = sp = NULL;
}
c->tail = cp;
s->tail = sp;
}
static void
comtail(struct hstate *c, struct hstate *s, int sep)
{
char *cp, *sp, *cdot, *sdot;
if (c->len == 0 || s->len == 0)
return;
cdot = sdot = NULL;
cp = &c->str[c->len];
sp = &s->str[s->len];
while (cp > c->str && sp > s->str) {
if (*--cp != *--sp) {
cp++;
sp++;
break;
}
if (*cp == sep) {
cdot = cp;
sdot = sp;
}
}
if (cp == &c->str[c->len])
return;
c->tail = cp;
s->tail = sp;
c->dot = cdot;
s->dot = sdot;
}
void
krb5_free_realm_tree(krb5_context context, krb5_principal *realms)
{
krb5_principal *nrealms = realms;
if (realms == NULL)
return;
while (*nrealms) {
krb5_free_principal(context, *nrealms);
nrealms++;
}
free(realms);
}