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" | #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_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL | ||||||
| krb5_aname_to_localname (krb5_context context, | krb5_aname_to_localname (krb5_context context, | ||||||
| 			 krb5_const_principal aname, | 			 krb5_const_principal aname, | ||||||
| 			 size_t lnsize, | 			 size_t lnsize, | ||||||
| 			 char *lname) | 			 char *lname) | ||||||
| { | { | ||||||
|  |     static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT; | ||||||
|     krb5_error_code ret; |     krb5_error_code ret; | ||||||
|     krb5_realm *lrealms, *r; |     krb5_realm *lrealms, *r; | ||||||
|  |     heim_string_t ures = NULL; | ||||||
|     int valid; |     int valid; | ||||||
|     size_t len; |     size_t len; | ||||||
|     const char *res; |     const char *res; | ||||||
|  |  | ||||||
|  |     heim_base_once_f(®_def_plugins, context, reg_def_plugins_once); | ||||||
|  |  | ||||||
|     ret = krb5_get_default_realms (context, &lrealms); |     ret = krb5_get_default_realms (context, &lrealms); | ||||||
|     if (ret) |     if (ret) | ||||||
| 	return ret; | 	return ret; | ||||||
| @@ -78,13 +334,20 @@ krb5_aname_to_localname (krb5_context context, | |||||||
| 	if (!userok) | 	if (!userok) | ||||||
| 	    return KRB5_NO_LOCALNAME; | 	    return KRB5_NO_LOCALNAME; | ||||||
|  |  | ||||||
|     } else |     } else { | ||||||
| 	return KRB5_NO_LOCALNAME; | 	ret = an2lnplugin(context, aname, &ures); | ||||||
|  | 	if (ret) | ||||||
|  | 	    return ret; | ||||||
|  | 	res = heim_string_get_utf8(ures); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     len = strlen (res); |     len = strlen (res); | ||||||
|     if (len >= lnsize) |     if (len >= lnsize) | ||||||
| 	return ERANGE; | 	return ERANGE; | ||||||
|     strlcpy (lname, res, lnsize); |     strlcpy (lname, res, lnsize); | ||||||
|  |  | ||||||
|  |     if (ures) | ||||||
|  | 	heim_release(ures); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -260,6 +260,17 @@ If set to "ignore", the framework will ignore any the server input to | |||||||
| .Xr krb5_rd_req 3, | .Xr krb5_rd_req 3, | ||||||
| this is very useful when the GSS-API server input the | this is very useful when the GSS-API server input the | ||||||
| wrong server name into the gss_accept_sec_context call. | 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 | .It Li name_canon_rules = Va rules | ||||||
| One or more name canonicalization rules.  Each rule consists of one or | One or more name canonicalization rules.  Each rule consists of one or | ||||||
| more tokens separated by colon (':').  The first token must be a rule | more tokens separated by colon (':').  The first token must be a rule | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nicolas Williams
					Nicolas Williams