[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Password expiration patch
On Wed, 13 Sep 2006, [ISO-8859-1] Love Hörnquist Åstrand wrote:
> Hi Eric,
>
>> I have been working on a patch to allow for "pluggable" modules to be
>> loaded into kadmin (much the same way the password quality checks are done)
>> to allow for variable password lifetimes (rather than just reading a static
>> value from the config files). The patch still has the same functionality
>> (use config file value) if the modules are unconfigured, but could be used
>> to do just about anything (we have a module which queries LDAP for an
>> attribute, so that faculty/staff/students/systems people have different
>> expirations).
>>
>> Would such a patch be accepted into Heimdal, and if so, where should I send
>> it?
>
> I've not given much thought to how to do stuff like that. Viewed
> from a greater perspecitive there seams to be many things that should
> be configured depending on what "group" the user belonged too.
>
> Please send the patch to the list or a pointer to the patch if its large.
>
> When I've read the patch I can comment on what I think about it.
>
> Love
>
>
>
Patch is attached.
This is against 0.7.2, If there is a CVS head or something you would like
the patch against, please let me know.
--
Eric Sturdivant
University of Maryland
Office of Information Technology
Distributed Computing Services
(301) 405-8473
<sturdiva AT umd DOT edu>
http://www.oit.umd.edu/dcs
--- /tmp/heimdal/heimdal-0.7.2/lib/kadm5/bump_pw_expire.c 2006-09-13 09:05:29.750560808 -0400
+++ lib/kadm5/bump_pw_expire.c 2006-09-13 09:03:06.000001000 -0400
@@ -32,9 +32,227 @@
*/
#include "kadm5_locl.h"
+#include "kadm5-pwlifetime.h"
RCSID("$Id: bump_pw_expire.c,v 1.1 2000/07/24 03:47:54 assar Exp $");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+
+/* builtin default (read lifetime from config file) */
+static krb5_error_code
+krb5_config_passwd_lifetime (krb5_context context,
+ krb5_principal principal,
+ krb5_deltat *lifetime)
+{
+ *lifetime = krb5_config_get_time_default(context,
+ NULL,
+ 365 * 24 * 60 * 60,
+ "kadmin",
+ "password_lifetime",
+ NULL);
+
+ return 0;
+}
+
+
+
+/*
+ * builtin default function is just the normal
+ * "query krb5.conf" method.
+ */
+struct kadm5_pw_lifetime_func lifetime_builtin_funcs[] = {
+ { "krb5-config", krb5_config_passwd_lifetime },
+ { NULL }
+};
+struct kadm5_pw_lifetime_library builtin_library = {
+ "builtin",
+ KADM5_PASSWD_LIFETIME_VERSION_V0,
+ "Heimdal builtin",
+ lifetime_builtin_funcs
+};
+
+static struct kadm5_pw_lifetime_library **libraries = NULL;
+static int num_libraries = -1;
+
+
+#ifdef HAVE_DLOPEN
+static krb5_error_code
+add_func (krb5_context context, const char *lifetime_library)
+{
+ void *handle;
+ struct kadm5_pw_lifetime_library *l, **tmp;
+ int i;
+
+ if ( lifetime_library == NULL ) {
+ return EINVAL;
+ }
+
+ handle = dlopen (lifetime_library, RTLD_NOW);
+ if ( handle == NULL ) {
+ krb5_warnx (context, "failed to open '%s'", lifetime_library);
+ return ENOENT;
+ }
+
+ l = dlsym (handle, "kadm5_password_lifetime");
+ if ( l == NULL ) {
+ krb5_warnx (context,
+ "didn't find 'kadm5_password_lifetime' symbol "
+ "in '%s'", lifetime_library);
+ dlclose (handle);
+ return ENOENT;
+ }
+
+ if ( l->version != KADM5_PASSWD_LIFETIME_VERSION_V0 ) {
+ krb5_warnx (context,
+ "version of loaded library is %d (expected %d)",
+ l->version, KADM5_PASSWD_LIFETIME_VERSION_V0);
+ dlclose (handle);
+ return EINVAL;
+ }
+
+ for (i=0; i<num_libraries; i++) {
+ if ( strcmp (l->name, libraries[i]->name) == 0 ) {
+ break;
+ }
+ }
+
+ if ( i < num_libraries ) {
+ krb5_warnx (context, "password lifetime library '%s' (%s)"
+ "is already loaded",
+ l->name, lifetime_library);
+ dlclose (handle);
+
+ /* don't fall back to the default */
+ return 0;
+ }
+
+ tmp = realloc (libraries, (num_libraries + 1) * sizeof (*libraries));
+ if ( tmp == NULL ) {
+ krb5_warnx (context, "out of memory");
+ dlclose (handle);
+
+ /* fallback if we don't have anything */
+ if ( num_libraries ) {
+ return ENOMEM;
+ } else {
+ return 0;
+ }
+ }
+
+ libraries = tmp;
+ libraries[num_libraries] = l;
+ num_libraries++;
+
+ return 0;
+}
+#endif /* HAVE_DLOPEN */
+
+
+
+static void
+_kadm5_setup_passwd_lifetime_libraries (krb5_context context)
+{
+#ifdef HAVE_DLOPEN
+
+ char **tmp;
+ krb5_error_code ret;
+
+ /* query the config files to see if they want to use an
+ external library. (If not, we just fall back to the
+ builtin method. */
+ tmp = krb5_config_get_strings (context, NULL,
+ "password_lifetime",
+ "lifetime_libraries",
+ NULL);
+ if ( tmp == NULL ) {
+ /* fallback to the default. */
+ return ;
+ }
+
+ while (*tmp) {
+ ret = add_func (context, *tmp);
+ if ( ret ) {
+ krb5_warn (context, ret,
+ "Can't load password lifetime library '%s'", *tmp);
+
+ krb5_config_free_strings (tmp);
+
+ /* fallback to the default */
+ return ;
+ }
+ tmp++;
+ }
+#endif /* HAVE_DLOPEN */
+
+ return ;
+}
+
+
+
+/*
+ * find the given function (from config file) in the libraries
+ * we have configured.
+ */
+static struct kadm5_pw_lifetime_func *
+find_func (krb5_context context, const char *name)
+{
+ struct kadm5_pw_lifetime_func *f;
+ char *module = NULL;
+ const char *p, *func;
+ int i;
+
+ /* name is made up of "$module:$function" or "$function" */
+ p = strchr (name, ':');
+ if ( p ) {
+ func = p+1;
+ module = strndup (name, p-name);
+ if ( module == NULL ) {
+ return NULL;
+ }
+ } else {
+ func = name;
+ }
+
+ /* find it in the loaded modules first. */
+ for (i=0; i<num_libraries; i++) {
+ /* skip if we *did* specify a module, and it's not this one. */
+ if ( module && strcmp (module, libraries[i]->name) != 0 )
+ continue;
+
+ for (f=libraries[i]->funcs; f->name; f++) {
+ if ( strcmp (func, f->name) == 0 ) {
+ if ( module ) {
+ free (module);
+ }
+ return f;
+ }
+ }
+ }
+
+ /* try the builtin modules */
+ if ( module == NULL || strcmp (module, "builtin") == 0 ) {
+ for (f=builtin_library.funcs; f->name; f++) {
+ if ( strcmp (func, f->name) == 0 ) {
+ if ( module ) {
+ free (module);
+ }
+ return f;
+ }
+ }
+ }
+
+ if ( module ) {
+ free (module);
+ }
+
+ return NULL;
+}
+
+
+
/*
* extend password_expiration if it's defined
*/
@@ -43,17 +261,56 @@
_kadm5_bump_pw_expire(kadm5_server_context *context,
hdb_entry *ent)
{
+
+ if ( num_libraries == -1 ) {
+ num_libraries = 0;
+ _kadm5_setup_passwd_lifetime_libraries (context->context);
+ }
+
if (ent->pw_end != NULL) {
- time_t life;
+ char **l, **lp;
+ krb5_deltat life, least_life=0;
+ int least_set=0;
+ krb5_error_code ret;
+ struct kadm5_pw_lifetime_func *proc;
+
+ /* find what function we should be using. */
+ l = krb5_config_get_strings (context->context, NULL,
+ "password_lifetime",
+ "policies",
+ NULL);
+
+ if ( l == NULL ) {
+ /* default. */
+ ret = krb5_config_passwd_lifetime (context->context,
+ ent->principal,
+ &life);
+ if ( !ret ) {
+ *(ent->pw_end) = time(NULL) + life;
+ }
+ return 0;
+ }
+
+ /* go through the policies, and find the most restrictive one. */
+ for (lp = l; *lp; lp++) {
+ proc = find_func (context->context, *lp);
+ if ( proc == NULL ) {
+ krb5_warnx (context->context,
+ "Failed to find password lifetime function '%s'",
+ *lp);
+ continue;
+ }
- life = krb5_config_get_time_default(context->context,
- NULL,
- 365 * 24 * 60 * 60,
- "kadmin",
- "password_lifetime",
- NULL);
+ ret = (proc->func)(context->context, ent->principal, &life);
+ if ( !ret && (!least_set || life < least_life) ) {
+ least_life = life;
+ least_set = 1;
+ }
+ }
- *(ent->pw_end) = time(NULL) + life;
+ if ( least_set ) {
+ *(ent->pw_end) = time(NULL) + least_life;
+ }
}
return 0;
}
--- /tmp/heimdal/heimdal-0.7.2/lib/kadm5/Makefile.am 2006-09-13 09:05:29.763648454 -0400
+++ lib/kadm5/Makefile.am 2006-09-13 09:07:04.000001000 -0400
@@ -20,7 +20,7 @@
buildkadm5include = $(buildinclude)/kadm5
kadm5include_HEADERS = kadm5_err.h admin.h private.h \
- kadm5-protos.h kadm5-private.h
+ kadm5-protos.h kadm5-private.h kadm5-pwlifetime.h
install-build-headers:: $(kadm5include_HEADERS)
@foo='$(kadm5include_HEADERS)'; \
--- /dev/null 2006-09-13 09:08:07.000000000 -0400
+++ lib/kadm5/kadm5-pwlifetime.h 2006-09-12 11:02:12.000001000 -0400
@@ -0,0 +1,30 @@
+#include <krb5.h>
+
+#ifndef KADM5_PWLIFETIME_H
+#define KADM5_PWLIFETIME_H 1
+
+#define KADM5_PASSWD_LIFETIME_VERSION_V0 0
+
+/*
+ * function provided by the client library which can return a krb5_deltat
+ * to be used as the password lifetime bump for the given principal.
+ */
+typedef krb5_error_code
+(*kadm5_passwd_lifetime_func)(krb5_context context,
+ krb5_principal principal,
+ krb5_deltat *lifetime);
+
+struct kadm5_pw_lifetime_func {
+ char *name;
+ kadm5_passwd_lifetime_func func;
+};
+
+struct kadm5_pw_lifetime_library {
+ char *name;
+ int version;
+ char *vendor;
+ struct kadm5_pw_lifetime_func *funcs;
+};
+
+
+#endif /* KADM5_PWLIFETIME_H */