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:
@@ -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(®_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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user