#include "k5-int.h"
#include "os-proto.h"
#include <krb5/localauth_plugin.h>
#include <ctype.h>
#include "k5-regex.h"
static krb5_error_code
aname_do_match(const char *selstring, const char **contextp)
{
krb5_error_code ret;
const char *startp, *endp;
char *regstr;
regex_t re;
regmatch_t m;
if (**contextp != '(')
return 0;
startp = *contextp + 1;
endp = strchr(startp, ')');
if (endp == NULL)
return KRB5_CONFIG_BADFORMAT;
regstr = k5memdup0(startp, endp - startp, &ret);
if (regstr == NULL)
return ret;
ret = (regcomp(&re, regstr, REG_EXTENDED) == 0 &&
regexec(&re, selstring, 1, &m, 0) == 0 &&
m.rm_so == 0 && (size_t)m.rm_eo == strlen(selstring)) ? 0 :
KRB5_LNAME_NOTRANS;
regfree(&re);
free(regstr);
*contextp = endp + 1;
return ret;
}
static krb5_error_code
do_replacement(const char *regstr, const char *repl, krb5_boolean doall,
const char *instr, char **outstr)
{
struct k5buf buf;
regex_t re;
regmatch_t m;
*outstr = NULL;
if (regcomp(&re, regstr, REG_EXTENDED))
return KRB5_LNAME_NOTRANS;
k5_buf_init_dynamic(&buf);
while (regexec(&re, instr, 1, &m, 0) == 0) {
k5_buf_add_len(&buf, instr, m.rm_so);
k5_buf_add(&buf, repl);
instr += m.rm_eo;
if (!doall)
break;
}
regfree(&re);
k5_buf_add(&buf, instr);
*outstr = k5_buf_cstring(&buf);
return (*outstr == NULL) ? ENOMEM : 0;
}
static krb5_error_code
aname_replacer(const char *string, const char **contextp, char **result)
{
krb5_error_code ret = 0;
const char *cp, *ep, *tp;
char *newstr, *rule = NULL, *repl = NULL, *current = NULL;
krb5_boolean doglobal;
*result = NULL;
current = strdup(string);
if (current == NULL)
return ENOMEM;
cp = *contextp;
while (*cp != '\0') {
while (isspace((unsigned char)*cp))
cp++;
if (!(cp[0] == 's' && cp[1] == '/' && (ep = strchr(cp + 2, '/')) &&
(tp = strchr(ep + 1, '/')))) {
ret = KRB5_CONFIG_BADFORMAT;
goto cleanup;
}
free(rule);
rule = k5memdup0(cp + 2, ep - (cp + 2), &ret);
if (rule == NULL)
goto cleanup;
free(repl);
repl = k5memdup0(ep + 1, tp - (ep + 1), &ret);
if (repl == NULL)
goto cleanup;
cp = tp + 1;
doglobal = (*cp == 'g');
if (doglobal)
cp++;
ret = do_replacement(rule, repl, doglobal, current, &newstr);
if (ret)
goto cleanup;
free(current);
current = newstr;
}
*result = current;
current = NULL;
cleanup:
free(current);
free(repl);
free(rule);
return ret;
}
static krb5_error_code
aname_get_selstring(krb5_context context, krb5_const_principal aname,
const char **contextp, char **selstring_out)
{
const char *current;
char *end;
long num_comps, ind;
const krb5_data *datap;
struct k5buf selstring;
size_t nlit;
*selstring_out = NULL;
if (**contextp != '[') {
return krb5_unparse_name_flags(context, aname,
KRB5_PRINCIPAL_UNPARSE_NO_REALM,
selstring_out);
}
current = *contextp + 1;
errno = 0;
num_comps = strtol(current, &end, 10);
if (errno != 0 || num_comps < 0 || *end != ':')
return KRB5_CONFIG_BADFORMAT;
current = end;
if (num_comps != aname->length)
return KRB5_LNAME_NOTRANS;
current++;
k5_buf_init_dynamic(&selstring);
while (TRUE) {
nlit = strcspn(current, "$]");
k5_buf_add_len(&selstring, current, nlit);
current += nlit;
if (*current != '$')
break;
errno = 0;
ind = strtol(current + 1, &end, 10);
if (errno || ind > num_comps)
break;
current = end;
datap = ind > 0 ? &aname->data[ind - 1] : &aname->realm;
k5_buf_add_len(&selstring, datap->data, datap->length);
}
if (*current != ']') {
k5_buf_free(&selstring);
return KRB5_CONFIG_BADFORMAT;
}
*selstring_out = k5_buf_cstring(&selstring);
if (*selstring_out == NULL)
return ENOMEM;
*contextp = current + 1;
return 0;
}
static krb5_error_code
an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
const char *rule, krb5_const_principal aname, char **lname_out)
{
krb5_error_code ret;
const char *current;
char *selstring = NULL;
*lname_out = NULL;
if (rule == NULL)
return KRB5_CONFIG_BADFORMAT;
current = rule;
ret = aname_get_selstring(context, aname, ¤t, &selstring);
if (ret)
return ret;
if (*current == '(') {
ret = aname_do_match(selstring, ¤t);
if (ret)
goto cleanup;
}
ret = aname_replacer(selstring, ¤t, lname_out);
cleanup:
free(selstring);
return ret;
}
static void
freestr(krb5_context context, krb5_localauth_moddata data, char *str)
{
free(str);
}
krb5_error_code
localauth_rule_initvt(krb5_context context, int maj_ver, int min_ver,
krb5_plugin_vtable vtable)
{
krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
static const char *types[] = { "RULE", NULL };
vt->name = "rule";
vt->an2ln_types = types;
vt->an2ln = an2ln_rule;
vt->free_string = freestr;
return 0;
}