Initial aname2lname plugin patch based on code from Love

Included is a default plugin that searches a sorted text file where
    every line is of the form:
	<unparsed-principal>[<whitespace><username>]
    If the username is missing in a matching line then an error is
    returned.  If a matching line is not found then the next plugin will
    be allowed to run, if any.
This commit is contained in:
Nicolas Williams
2011-10-25 00:43:41 -05:00
parent d0abcebf80
commit aea02876e7
2 changed files with 276 additions and 2 deletions

View File

@@ -33,18 +33,274 @@
#include "krb5_locl.h"
#define KRB5_PLUGIN_AN2LN "an2ln"
#define KRB5_PLUGIN_AN2LN_VERSION_0 0
typedef krb5_error_code (*set_result_f)(void *, const char *);
typedef struct krb5plugin_an2ln_ftable_desc {
int minor_version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
krb5_error_code (*an2ln)(void *, krb5_context, krb5_const_principal, set_result_f, void *);
} krb5plugin_an2ln_ftable;
/* Default plugin follows */
static krb5_error_code
an2ln_def_plug_init(krb5_context context, void **ctx)
{
*ctx = NULL;
return 0;
}
static void
an2ln_def_plug_fini(void *ctx)
{
}
/* Find a non-quoted new-line */
static char *
find_line(char *buf, size_t i, size_t right)
{
for (; i < right; i++) {
/* Seek a two non-quote char sequence */
if (buf[i] != '\\' && (i + 1) < right && buf[i + 1] != '\\') {
/* Seek a non-quoted new-line */
for (i += 1; i < right; i++) {
if (buf[i] == '\n')
break;
if (buf[i] == '\\' && (i + 1) < right && buf[i + 1] != '\n')
i++; /* skip quoted char */
}
break;
}
}
if (buf[i] == '\n' && (i + 1) < right)
return &buf[i + 1];
return NULL;
}
static krb5_error_code
an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
krb5_const_principal princ,
set_result_f set_res_f, void *set_res_ctx)
{
krb5_error_code ret;
const char *an2ln_db_fname;
char *fdata = NULL;
char *unparsed = NULL;
char *cp;
char *p;
char *u;
int fd = -1;
int cmp;
size_t sz, l, r, i, k;
struct stat st;
an2ln_db_fname = krb5_config_get_string(context, NULL, "libdefaults",
"aname2lname-text-db", NULL);
if (an2ln_db_fname)
return KRB5_PLUGIN_NO_HANDLE;
ret = krb5_unparse_name(context, princ, &unparsed);
if (ret)
return ret;
fd = open(an2ln_db_fname, O_RDONLY);
if (fd == -1) {
ret = KRB5_PLUGIN_NO_HANDLE;
goto cleanup;
}
if (fstat(fd, &st) == -1 || st.st_size == 0) {
ret = KRB5_PLUGIN_NO_HANDLE;
goto cleanup;
}
/*
* This is a dead-simple DB, so simple that we read the whole file
* in and do the search in memory. This means that in 32-bit
* processes we can't handle large files. But this should not be a
* large file anyways, else use another plugin.
*/
sz = (size_t)st.st_size;
if (st.st_size != (off_t)sz) {
ret = E2BIG;
goto cleanup;
}
fdata = malloc(sz + 1);
if (fdata == NULL) {
ret = krb5_enomem(context);
goto cleanup;
}
if (read(fd, fdata, sz) < sz) {
krb5_set_error_message(context, errno, "read: reading aname2lname DB");
ret = errno;
goto cleanup;
}
fdata[sz] = '\0';
close(fd);
fd = -1;
/* Binary search; file should be sorted */
for (l = 0, r = sz, i = sz >> 1; i > l && i < r; ) {
heim_assert(i > 0 && i < sz, "invalid aname2lname db index");
/* fdata[i] is likely in the middle of a line; find the next line */
cp = find_line(fdata, i, r);
if (cp == NULL) {
/*
* No new line found to the right; search to the left then
* (this isn't optimal, but it's simple)
*/
r = i;
i = (r - l) >> 1;
}
i = cp - fdata;
heim_assert(i > l && i < r, "invalid aname2lname db index");
/* Got a line; check it */
/* Search for and split on unquoted whitespace */
for (p = &fdata[i], u = NULL, k = i; k < r; k++) {
if (fdata[k] == '\\') {
k++;
continue;
}
/* The one concession to CRLF here */
if (fdata[k] == '\r' || fdata[k] == '\n') {
fdata[k] = '\0';
break;
}
if (isspace(fdata[k])) {
fdata[k] = '\0';
for (; k < r; k++) {
if (fdata[k] == '\\') {
k++;
continue;
}
if (fdata[k] == '\n')
fdata[k] = '\0';
while (isspace(fdata[k]))
k++;
break;
}
u = &fdata[k];
break;
}
}
cmp = strcmp(p, unparsed);
if (cmp < 0) {
/* search left */
r = i;
i = (r - l) >> 1;
} else if (cmp > 0) {
/* search right */
l = i;
i = (r - l) >> 1;
} else {
/* match! */
if (u == NULL)
ret = KRB5_NO_LOCALNAME;
else
ret = set_res_f(set_res_ctx, u);
break;
}
}
cleanup:
if (fd != -1)
close(fd);
free(unparsed);
free(fdata);
return ret;
}
krb5plugin_an2ln_ftable an2ln_def_plug = {
0,
an2ln_def_plug_init,
an2ln_def_plug_fini,
an2ln_def_plug_an2ln,
};
struct plctx {
krb5_const_principal aname;
heim_string_t luser;
};
static krb5_error_code KRB5_LIB_CALL
set_res(void *userctx, const char *res)
{
struct plctx *plctx = userctx;
plctx->luser = heim_string_create(res);
if (plctx->luser == NULL)
return ENOMEM;
return 0;
}
static krb5_error_code KRB5_LIB_CALL
plcallback(krb5_context context,
const void *plug, void *plugctx, void *userctx)
{
const krb5plugin_an2ln_ftable *locate = plug;
struct plctx *plctx = userctx;
if (plctx->luser)
return 0;
return locate->an2ln(plugctx, context, plctx->aname, set_res, plctx);
}
static krb5_error_code
an2lnplugin(krb5_context context, krb5_const_principal aname, heim_string_t *ures)
{
struct plctx ctx;
ctx.aname = aname;
ctx.luser = NULL;
_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN,
KRB5_PLUGIN_AN2LN_VERSION_0,
0, &ctx, plcallback);
if (ctx.luser == NULL)
return KRB5_NO_LOCALNAME;
*ures = ctx.luser;
return 0;
}
static void
reg_def_plugins_once(void *ctx)
{
krb5_error_code ret;
krb5_context context = ctx;
ret = krb5_plugin_register(context, PLUGIN_TYPE_FUNC,
KRB5_PLUGIN_AN2LN, &an2ln_def_plug);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_aname_to_localname (krb5_context context,
krb5_const_principal aname,
size_t lnsize,
char *lname)
{
static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
krb5_error_code ret;
krb5_realm *lrealms, *r;
heim_string_t ures = NULL;
int valid;
size_t len;
const char *res;
heim_base_once_f(&reg_def_plugins, context, reg_def_plugins_once);
ret = krb5_get_default_realms (context, &lrealms);
if (ret)
return ret;
@@ -78,13 +334,20 @@ krb5_aname_to_localname (krb5_context context,
if (!userok)
return KRB5_NO_LOCALNAME;
} else
return KRB5_NO_LOCALNAME;
} else {
ret = an2lnplugin(context, aname, &ures);
if (ret)
return ret;
res = heim_string_get_utf8(ures);
}
len = strlen (res);
if (len >= lnsize)
return ERANGE;
strlcpy (lname, res, lnsize);
if (ures)
heim_release(ures);
return 0;
}

View File

@@ -260,6 +260,17 @@ If set to "ignore", the framework will ignore any the server input to
.Xr krb5_rd_req 3,
this is very useful when the GSS-API server input the
wrong server name into the gss_accept_sec_context call.
.It Li aname2lname-text-db = Va filename
The named file must be a sorted (in increasing order) text file where
every line consists of an unparsed principal name optionally followed by
whitespace and a username. The aname2lname function will do a binary
search on this file, if configured, looking for lines that match the
given principal name, and if found the given username will be used, or,
if the username is missing, an error will be returned. If the file
doesn't exist, or if no matching line is found then other plugins will
be allowed to run. Note: large
.Va aname2lname-text-db
files are not supported on small memory systems/processes.
.It Li name_canon_rules = Va rules
One or more name canonicalization rules. Each rule consists of one or
more tokens separated by colon (':'). The first token must be a rule