aboutsummaryrefslogtreecommitdiff
path: root/src/lib/kadm5
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-07-22 03:13:38 +0000
committerGreg Hudson <ghudson@mit.edu>2010-07-22 03:13:38 +0000
commit9e1b4bc8d254fd6671a94d2a8a5c84650754fce3 (patch)
tree81c86ecb849fb18b3ae2cda4f4fe3eb3bcbad871 /src/lib/kadm5
parentc5e940f6d4d6737bf0ed778a72dbfc6dfb9640dc (diff)
downloadkrb5-9e1b4bc8d254fd6671a94d2a8a5c84650754fce3.zip
krb5-9e1b4bc8d254fd6671a94d2a8a5c84650754fce3.tar.gz
krb5-9e1b4bc8d254fd6671a94d2a8a5c84650754fce3.tar.bz2
Proof of concept code for a candidate plugin framework
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/plugins2@24203 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/kadm5')
-rw-r--r--src/lib/kadm5/server_internal.h53
-rw-r--r--src/lib/kadm5/srv/Makefile.in16
-rw-r--r--src/lib/kadm5/srv/libkadm5srv_mit.exports3
-rw-r--r--src/lib/kadm5/srv/pwqual.c114
-rw-r--r--src/lib/kadm5/srv/pwqual_dict.c242
-rw-r--r--src/lib/kadm5/srv/pwqual_policy.c81
-rw-r--r--src/lib/kadm5/srv/server_dict.c208
-rw-r--r--src/lib/kadm5/srv/server_init.c4
-rw-r--r--src/lib/kadm5/srv/server_misc.c187
-rw-r--r--src/lib/kadm5/srv/svr_principal.c6
10 files changed, 553 insertions, 361 deletions
diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h
index cc589fa..52f71e9 100644
--- a/src/lib/kadm5/server_internal.h
+++ b/src/lib/kadm5/server_internal.h
@@ -22,6 +22,7 @@
#include <errno.h>
#include <kdb.h>
#include <kadm5/admin.h>
+#include <krb5/plugin.h>
#include "admin_internal.h"
/*
@@ -33,6 +34,9 @@
*/
#define INITIAL_HIST_KVNO 2
+/* A pwqual_handle represents a password quality plugin module. */
+typedef struct pwqual_handle_st *pwqual_handle;
+
typedef struct _kadm5_server_handle_t {
krb5_ui_4 magic_number;
krb5_ui_4 struct_version;
@@ -42,6 +46,7 @@ typedef struct _kadm5_server_handle_t {
kadm5_config_params params;
struct _kadm5_server_handle_t *lhandle;
char **db_args;
+ pwqual_handle *qual_handles;
} kadm5_server_handle_rec, *kadm5_server_handle_t;
#define OSA_ADB_PRINC_VERSION_1 0x12345C01
@@ -65,8 +70,7 @@ typedef struct _osa_princ_ent_t {
kadm5_ret_t adb_policy_init(kadm5_server_handle_t handle);
kadm5_ret_t adb_policy_close(kadm5_server_handle_t handle);
kadm5_ret_t passwd_check(kadm5_server_handle_t handle,
- char *pass, int use_policy,
- kadm5_policy_ent_t policy,
+ const char *pass, kadm5_policy_ent_t policy,
krb5_principal principal);
kadm5_ret_t principal_exists(krb5_principal principal);
krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
@@ -90,9 +94,8 @@ krb5_error_code kdb_iter_entry(kadm5_server_handle_t handle,
void (*iter_fct)(void *, krb5_principal),
void *data);
-int init_dict(kadm5_config_params *);
-int find_word(const char *word);
-void destroy_dict(void);
+kadm5_ret_t init_pwqual(kadm5_server_handle_t handle);
+void destroy_pwqual(kadm5_server_handle_t handle);
/* XXX this ought to be in libkrb5.a, but isn't */
kadm5_ret_t krb5_copy_key_data_contents(krb5_context context,
@@ -153,4 +156,44 @@ bool_t xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp);
void
osa_free_princ_ent(osa_princ_ent_t val);
+/*** Password quality plugin consumer interface ***/
+
+/* Load the available password quality plugins and store the result into
+ * *handles. Free the result with k5_pwqual_free_handles. */
+krb5_error_code
+k5_pwqual_load(krb5_context context, pwqual_handle **handles);
+
+/* Release a handle list allocated by k5_pwqual_load. All modules must have
+ * been closed by the caller. */
+krb5_error_code
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles);
+
+/* Initialize a password quality plugin, possibly using the realm's configured
+ * dictionary filename. */
+krb5_error_code
+k5_pwqual_open(krb5_context context, pwqual_handle handle,
+ const char *dict_file);
+
+/* Check a password using a password quality plugin. */
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ);
+
+/* Release the memory used by a password quality plugin. */
+void
+k5_pwqual_close(krb5_context context, pwqual_handle handle);
+
+/*** Init functions for built-in password quality modules ***/
+
+/* The dict module checks passwords against the realm's dictionary. */
+krb5_error_code
+pwqual_dict_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The policy module enforces password policy constraints. */
+krb5_error_code
+pwqual_policy_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
#endif /* __KADM5_SERVER_INTERNAL_H__ */
diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in
index c7e0fac..6000d73 100644
--- a/src/lib/kadm5/srv/Makefile.in
+++ b/src/lib/kadm5/srv/Makefile.in
@@ -27,36 +27,42 @@ SHLIB_DIRS=-L$(TOPLIBD)
SHLIB_RDIRS=$(KRB5_LIBDIR)
RELDIR=kadm5/srv
-SRCS = $(srcdir)/svr_policy.c \
+SRCS = $(srcdir)/pwqual.c \
+ $(srcdir)/pwqual_dict.c \
+ $(srcdir)/pwqual_policy.c \
+ $(srcdir)/svr_policy.c \
$(srcdir)/svr_principal.c \
$(srcdir)/server_acl.c \
$(srcdir)/server_kdb.c \
$(srcdir)/server_misc.c \
$(srcdir)/server_init.c \
- $(srcdir)/server_dict.c \
$(srcdir)/svr_iters.c \
$(srcdir)/svr_chpass_util.c \
$(srcdir)/adb_xdr.c
-OBJS = svr_policy.$(OBJEXT) \
+OBJS = pwqual.$(OBJEXT) \
+ pwqual_dict.$(OBJEXT) \
+ pwqual_policy.$(OBJECT) \
+ svr_policy.$(OBJEXT) \
svr_principal.$(OBJEXT) \
server_acl.$(OBJEXT) \
server_kdb.$(OBJEXT) \
server_misc.$(OBJEXT) \
server_init.$(OBJEXT) \
- server_dict.$(OBJEXT) \
svr_iters.$(OBJEXT) \
svr_chpass_util.$(OBJEXT) \
adb_xdr.$(OBJEXT)
STLIBOBJS = \
+ pwqual.o \
+ pwqual_dict.o \
+ pwqual_policy.o \
svr_policy.o \
svr_principal.o \
server_acl.o \
server_kdb.o \
server_misc.o \
server_init.o \
- server_dict.o \
svr_iters.o \
svr_chpass_util.o \
adb_xdr.o
diff --git a/src/lib/kadm5/srv/libkadm5srv_mit.exports b/src/lib/kadm5/srv/libkadm5srv_mit.exports
index 6da95bd..345957a 100644
--- a/src/lib/kadm5/srv/libkadm5srv_mit.exports
+++ b/src/lib/kadm5/srv/libkadm5srv_mit.exports
@@ -7,10 +7,7 @@ kadm5int_acl_impose_restrictions
kadm5int_acl_init
adb_policy_close
adb_policy_init
-destroy_dict
-find_word
hist_princ
-init_dict
kadm5_set_use_password_server
kadm5_chpass_principal
kadm5_chpass_principal_3
diff --git a/src/lib/kadm5/srv/pwqual.c b/src/lib/kadm5/srv/pwqual.c
new file mode 100644
index 0000000..4452376
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual.c
@@ -0,0 +1,114 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Consumer interface for password quality plugins
+ */
+
+#include "k5-int.h"
+#include "server_internal.h"
+#include <krb5/pwqual_plugin.h>
+
+struct pwqual_handle_st {
+ struct krb5_pwqual_vtable_st vt;
+ krb5_pwqual_moddata data;
+};
+
+krb5_error_code
+k5_pwqual_load(krb5_context context, pwqual_handle **handles)
+{
+ krb5_error_code ret;
+ krb5_plugin_init_fn *modules = NULL, *mod;
+ size_t count;
+ pwqual_handle *list = NULL, handle = NULL;
+
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_PWQUAL, &modules);
+ if (ret != 0)
+ goto cleanup;
+
+ /* Allocate a large enough list of handles. */
+ for (count = 0; modules[count] != NULL; count++);
+ list = k5alloc((count + 1) * sizeof(*list), &ret);
+ if (list == NULL)
+ goto cleanup;
+
+ /* For each module, allocate a handle and initialize its vtable. Skip
+ * modules which don't successfully initialize. */
+ count = 0;
+ for (mod = modules; *mod != NULL; mod++) {
+ handle = k5alloc(sizeof(*handle), &ret);
+ if (handle == NULL)
+ goto cleanup;
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
+ if (ret == 0)
+ list[count++] = handle;
+ else
+ free(handle);
+ }
+
+ *handles = list;
+ list = NULL;
+
+cleanup:
+ k5_plugin_free_modules(context, modules);
+ k5_pwqual_free_handles(context, list);
+ return ret;
+}
+
+krb5_error_code
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles)
+{
+ /* It's the caller's responsibility to close each handle, so all of the
+ * module data should be freed by now, leaving only the list itself. */
+ free(handles);
+}
+
+krb5_error_code
+k5_pwqual_open(krb5_context context, pwqual_handle handle,
+ const char *dict_file)
+{
+ if (handle->data != NULL)
+ return EINVAL;
+ if (handle->vt.open == NULL)
+ return 0;
+ return handle->vt.open(context, dict_file, &handle->data);
+}
+
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ return handle->vt.check(context, handle->data, password, policy, princ);
+}
+
+void
+k5_pwqual_close(krb5_context context, pwqual_handle handle)
+{
+ if (handle->vt.close)
+ handle->vt.close(context, handle->data);
+ handle->data = NULL;
+}
diff --git a/src/lib/kadm5/srv/pwqual_dict.c b/src/lib/kadm5/srv/pwqual_dict.c
new file mode 100644
index 0000000..60bc5ff
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_dict.c
@@ -0,0 +1,242 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <kadm5/admin.h>
+#include "adm_proto.h"
+#include <syslog.h>
+#include "server_internal.h"
+
+typedef struct dict_moddata_st {
+ char **word_list; /* list of word pointers */
+ char *word_block; /* actual word data */
+ unsigned int word_count; /* number of words */
+} *dict_moddata;
+
+
+/*
+ * Function: word_compare
+ *
+ * Purpose: compare two words in the dictionary.
+ *
+ * Arguments:
+ * w1 (input) pointer to first word
+ * w2 (input) pointer to second word
+ * <return value> result of strcmp
+ *
+ * Requires:
+ * w1 and w2 to point to valid memory
+ *
+ */
+
+static int
+word_compare(const void *s1, const void *s2)
+{
+ return (strcasecmp(*(const char **)s1, *(const char **)s2));
+}
+
+/*
+ * Function: init-dict
+ *
+ * Purpose: Initialize in memory word dictionary
+ *
+ * Arguments:
+ * none
+ * <return value> KADM5_OK on success errno on failure;
+ * (but success on ENOENT)
+ *
+ * Requires:
+ * If WORDFILE exists, it must contain a list of words,
+ * one word per-line.
+ *
+ * Effects:
+ * If WORDFILE exists, it is read into memory sorted for future
+ * use. If it does not exist, it syslogs an error message and returns
+ * success.
+ *
+ * Modifies:
+ * word_list to point to a chunck of allocated memory containing
+ * pointers to words
+ * word_block to contain the dictionary.
+ *
+ */
+
+static int
+init_dict(dict_moddata dict, const char *dict_file)
+{
+ int fd;
+ size_t len, i;
+ char *p, *t;
+ struct stat sb;
+
+ if (dict_file == NULL) {
+ krb5_klog_syslog(LOG_INFO, "No dictionary file specified, continuing "
+ "without one.");
+ return KADM5_OK;
+ }
+ if ((fd = open(dict_file, O_RDONLY)) == -1) {
+ if (errno == ENOENT) {
+ krb5_klog_syslog(LOG_ERR,
+ "WARNING! Cannot find dictionary file %s, "
+ "continuing without one.", dict_file);
+ return KADM5_OK;
+ } else
+ return errno;
+ }
+ set_cloexec_fd(fd);
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return errno;
+ }
+ if ((dict->word_block = malloc(sb.st_size + 1)) == NULL)
+ return ENOMEM;
+ if (read(fd, dict->word_block, sb.st_size) != sb.st_size)
+ return errno;
+ (void) close(fd);
+ dict->word_block[sb.st_size] = '\0';
+
+ p = dict->word_block;
+ len = sb.st_size;
+ while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
+ *t = '\0';
+ len -= t - p + 1;
+ p = t + 1;
+ dict->word_count++;
+ }
+ if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL)
+ return ENOMEM;
+ p = dict->word_block;
+ for (i = 0; i < dict->word_count; i++) {
+ dict->word_list[i] = p;
+ p += strlen(p) + 1;
+ }
+ qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare);
+ return KADM5_OK;
+}
+
+/*
+ * Function: destroy_dict
+ *
+ * Purpose: destroy in-core copy of dictionary.
+ *
+ * Arguments:
+ * none
+ * <return value> none
+ * Requires:
+ * nothing
+ * Effects:
+ * frees up memory occupied by word_list and word_block
+ * sets count back to 0, and resets the pointers to NULL
+ *
+ * Modifies:
+ * word_list, word_block, and word_count.
+ *
+ */
+
+static void
+destroy_dict(dict_moddata dict)
+{
+ if (dict == NULL)
+ return;
+ free(dict->word_list);
+ free(dict->word_block);
+ free(dict);
+ return;
+}
+
+/* Implement the password quality open method by reading in dict_file. */
+static krb5_error_code
+dict_open(krb5_context context, const char *dict_file,
+ krb5_pwqual_moddata *data)
+{
+ krb5_error_code ret;
+ dict_moddata dict;
+
+ *data = NULL;
+
+ /* Allocate and initialize a dictionary structure. */
+ dict = malloc(sizeof(*dict));
+ if (dict == NULL)
+ return ENOMEM;
+ dict->word_list = NULL;
+ dict->word_block = NULL;
+ dict->word_count = 0;
+
+ /* Fill in the dictionary structure with data from dict_file. */
+ ret = init_dict(dict, dict_file);
+ if (ret != 0) {
+ destroy_dict(dict);
+ return ret;
+ }
+
+ *data = (krb5_pwqual_moddata)dict;
+ return 0;
+}
+
+/* Implement the password quality check method by checking the password
+ * against the dictionary, as well as against principal components. */
+static krb5_error_code
+dict_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ dict_moddata dict = (dict_moddata)data;
+ int i, n;
+ char *cp;
+
+ /* Don't check the dictionary for principals with no password policy. */
+ if (policy == NULL)
+ return 0;
+
+ /* Check against words in the dictionary if we successfully loaded one. */
+ if (dict->word_list != NULL &&
+ bsearch(&password, dict->word_list, dict->word_count, sizeof(char *),
+ word_compare) != NULL)
+ return KADM5_PASS_Q_DICT;
+
+ /* Check against components of the principal. */
+ n = krb5_princ_size(handle->context, princ);
+ cp = krb5_princ_realm(handle->context, princ)->data;
+ if (strcasecmp(cp, password) == 0)
+ return KADM5_PASS_Q_DICT;
+ for (i = 0; i < n; i++) {
+ cp = krb5_princ_component(handle->context, princ, i)->data;
+ if (strcasecmp(cp, password) == 0)
+ return KADM5_PASS_Q_DICT;
+ }
+ return 0;
+}
+
+/* Implement the password quality close method. */
+static void
+dict_close(krb5_context context, krb5_pwqual_moddata data)
+{
+ destroy_dict((dict_moddata)data);
+}
+
+krb5_error_code
+pwqual_dict_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return EINVAL; /* XXX create error code */
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->open = dict_open;
+ vt->check = dict_check;
+ vt->close = dict_close;
+ return 0;
+}
diff --git a/src/lib/kadm5/srv/pwqual_policy.c b/src/lib/kadm5/srv/pwqual_policy.c
new file mode 100644
index 0000000..978744d
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_policy.c
@@ -0,0 +1,81 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_policy.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * Password quality module to enforce password policy
+ */
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include <kadm5/admin.h>
+#include <ctype.h>
+#include "server_internal.h"
+
+/* Implement the password quality check module. */
+static krb5_error_code
+policy_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, kadm5_policy_ent_t policy,
+ krb5_principal princ)
+{
+ int nupper = 0, nlower = 0, ndigit = 0, npunct = 0, nspec = 0;
+ const char *s;
+ unsigned char c;
+
+ if (policy == NULL)
+ return (*password == '\0') ? KADM5_PASS_Q_TOOSHORT : 0;
+
+ if(strlen(password) < (size_t)policy->pw_min_length)
+ return KADM5_PASS_Q_TOOSHORT;
+ s = password;
+ while ((c = (unsigned char)*s++)) {
+ if (islower(c))
+ nlower = 1;
+ else if (isupper(c))
+ nupper = 1;
+ else if (isdigit(c))
+ ndigit = 1;
+ else if (ispunct(c))
+ npunct = 1;
+ else
+ nspec = 1;
+ }
+ if ((nupper + nlower + ndigit + npunct + nspec) < policy->pw_min_classes)
+ return KADM5_PASS_Q_CLASS;
+ return 0;
+}
+
+krb5_error_code
+pwqual_policy_init(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return EINVAL; /* XXX create error code */
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->check = policy_check;
+ return 0;
+}
diff --git a/src/lib/kadm5/srv/server_dict.c b/src/lib/kadm5/srv/server_dict.c
deleted file mode 100644
index 81cc5f9..0000000
--- a/src/lib/kadm5/srv/server_dict.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
- *
- * $Header$
- */
-
-#if !defined(lint) && !defined(__CODECENTER__)
-static char *rcsid = "$Header$";
-#endif
-
-#include <sys/types.h>
-#include <sys/file.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <kadm5/admin.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
-#endif
-#include "adm_proto.h"
-#include <syslog.h>
-#include "server_internal.h"
-#include "k5-platform.h"
-
-static char **word_list = NULL; /* list of word pointers */
-static char *word_block = NULL; /* actual word data */
-static unsigned int word_count = 0; /* number of words */
-
-
-/*
- * Function: word_compare
- *
- * Purpose: compare two words in the dictionary.
- *
- * Arguments:
- * w1 (input) pointer to first word
- * w2 (input) pointer to second word
- * <return value> result of strcmp
- *
- * Requires:
- * w1 and w2 to point to valid memory
- *
- */
-
-static int
-word_compare(const void *s1, const void *s2)
-{
- return (strcasecmp(*(const char **)s1, *(const char **)s2));
-}
-
-/*
- * Function: init-dict
- *
- * Purpose: Initialize in memory word dictionary
- *
- * Arguments:
- * none
- * <return value> KADM5_OK on success errno on failure;
- * (but success on ENOENT)
- *
- * Requires:
- * If WORDFILE exists, it must contain a list of words,
- * one word per-line.
- *
- * Effects:
- * If WORDFILE exists, it is read into memory sorted for future
- * use. If it does not exist, it syslogs an error message and returns
- * success.
- *
- * Modifies:
- * word_list to point to a chunck of allocated memory containing
- * pointers to words
- * word_block to contain the dictionary.
- *
- */
-
-int init_dict(kadm5_config_params *params)
-{
- int fd,
- len,
- i;
- char *p,
- *t;
- struct stat sb;
-
- if(word_list != NULL && word_block != NULL)
- return KADM5_OK;
- if (! (params->mask & KADM5_CONFIG_DICT_FILE)) {
- krb5_klog_syslog(LOG_INFO, "No dictionary file specified, continuing "
- "without one.");
- return KADM5_OK;
- }
- if ((fd = open(params->dict_file, O_RDONLY)) == -1) {
- if (errno == ENOENT) {
- krb5_klog_syslog(LOG_ERR,
- "WARNING! Cannot find dictionary file %s, "
- "continuing without one.", params->dict_file);
- return KADM5_OK;
- } else
- return errno;
- }
- set_cloexec_fd(fd);
- if (fstat(fd, &sb) == -1) {
- close(fd);
- return errno;
- }
- if ((word_block = (char *) malloc(sb.st_size + 1)) == NULL)
- return ENOMEM;
- if (read(fd, word_block, sb.st_size) != sb.st_size)
- return errno;
- (void) close(fd);
- word_block[sb.st_size] = '\0';
-
- p = word_block;
- len = sb.st_size;
- while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
- *t = '\0';
- len -= t - p + 1;
- p = t + 1;
- word_count++;
- }
- if ((word_list = (char **) malloc(word_count * sizeof(char *))) == NULL)
- return ENOMEM;
- p = word_block;
- for (i = 0; i < word_count; i++) {
- word_list[i] = p;
- p += strlen(p) + 1;
- }
- qsort(word_list, word_count, sizeof(char *), word_compare);
- return KADM5_OK;
-}
-
-/*
- * Function: find_word
- *
- * Purpose: See if the specified word exists in the in-core dictionary
- *
- * Arguments:
- * word (input) word to search for.
- * <return value> WORD_NOT_FOUND if not in dictionary,
- * KADM5_OK if if found word
- * errno if init needs to be called and returns an
- * error
- *
- * Requires:
- * word to be a null terminated string.
- * That word_list and word_block besetup
- *
- * Effects:
- * finds word in dictionary.
- * Modifies:
- * nothing.
- *
- */
-
-int
-find_word(const char *word)
-{
- char **value;
-
- if(word_list == NULL || word_block == NULL)
- return WORD_NOT_FOUND;
- if ((value = (char **) bsearch(&word, word_list, word_count, sizeof(char *),
- word_compare)) == NULL)
- return WORD_NOT_FOUND;
- else
- return KADM5_OK;
-}
-
-/*
- * Function: destroy_dict
- *
- * Purpose: destroy in-core copy of dictionary.
- *
- * Arguments:
- * none
- * <return value> none
- * Requires:
- * nothing
- * Effects:
- * frees up memory occupied by word_list and word_block
- * sets count back to 0, and resets the pointers to NULL
- *
- * Modifies:
- * word_list, word_block, and word_count.
- *
- */
-
-void
-destroy_dict(void)
-{
- if(word_list) {
- free(word_list);
- word_list = NULL;
- }
- if(word_block) {
- free(word_block);
- word_block = NULL;
- }
- if(word_count)
- word_count = 0;
- return;
-}
diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c
index 557ef0a..9ebc13e 100644
--- a/src/lib/kadm5/srv/server_init.c
+++ b/src/lib/kadm5/srv/server_init.c
@@ -317,7 +317,7 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass,
return ret;
}
- ret = init_dict(&handle->params);
+ ret = init_pwqual(handle);
if (ret) {
krb5_db_fini(handle->context);
krb5_free_principal(handle->context, handle->current_caller);
@@ -337,7 +337,7 @@ kadm5_ret_t kadm5_destroy(void *server_handle)
CHECK_HANDLE(server_handle);
- destroy_dict();
+ destroy_pwqual(handle);
adb_policy_close(handle);
krb5_db_fini(handle->context);
diff --git a/src/lib/kadm5/srv/server_misc.c b/src/lib/kadm5/srv/server_misc.c
index 1faeb86..38c0607 100644
--- a/src/lib/kadm5/srv/server_misc.c
+++ b/src/lib/kadm5/srv/server_misc.c
@@ -13,10 +13,6 @@ static char *rcsid = "$Header$";
#include <kdb.h>
#include <ctype.h>
#include <pwd.h>
-
-/* for strcasecmp */
-#include <string.h>
-
#include "server_internal.h"
kadm5_ret_t
@@ -37,147 +33,68 @@ adb_policy_close(kadm5_server_handle_t handle)
return KADM5_OK;
}
-#ifdef HESIOD
-/* stolen from v4sever/kadm_funcs.c */
-static char *
-reverse(str)
- char *str;
-{
- static char newstr[80];
- char *p, *q;
- int i;
-
- i = strlen(str);
- if (i >= sizeof(newstr))
- i = sizeof(newstr)-1;
- p = str+i-1;
- q = newstr;
- q[i]='\0';
- for(; i > 0; i--)
- *q++ = *p--;
-
- return(newstr);
-}
-#endif /* HESIOD */
-
-#if 0
-static int
-lower(str)
- char *str;
+kadm5_ret_t
+init_pwqual(kadm5_server_handle_t handle)
{
- register char *cp;
- int effect=0;
-
- for (cp = str; *cp; cp++) {
- if (isupper(*cp)) {
- *cp = tolower(*cp);
- effect++;
+ krb5_error_code ret;
+ pwqual_handle *list, *h;
+ const char *dict_file = NULL;
+
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "dict", pwqual_dict_init);
+ if (ret != 0)
+ return ret;
+
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "policy", pwqual_policy_init);
+ if (ret != 0)
+ return ret;
+
+ ret = k5_pwqual_load(handle->context, &list);
+ if (ret != 0)
+ return ret;
+
+ if (handle->params.mask & KADM5_CONFIG_DICT_FILE)
+ dict_file = handle->params.dict_file;
+
+ for (h = list; *h != NULL; h++) {
+ ret = k5_pwqual_open(handle->context, *h, dict_file);
+ if (ret != 0) {
+ /* Close any previously opened modules and error out. */
+ for (; h > list; h--)
+ k5_pwqual_close(handle->context, *(h - 1));
+ k5_pwqual_free_handles(handle->context, list);
+ return ret;
}
}
- return(effect);
+
+ handle->qual_handles = list;
+ return 0;
}
-#endif
-#ifdef HESIOD
-static int
-str_check_gecos(gecos, pwstr)
- char *gecos;
- char *pwstr;
+/* Check a password against all available password quality plugin modules. */
+kadm5_ret_t
+passwd_check(kadm5_server_handle_t handle, const char *password,
+ kadm5_policy_ent_t policy, krb5_principal princ)
{
- char *cp, *ncp, *tcp;
-
- for (cp = gecos; *cp; ) {
- /* Skip past punctuation */
- for (; *cp; cp++)
- if (isalnum(*cp))
- break;
- /* Skip to the end of the word */
- for (ncp = cp; *ncp; ncp++)
- if (!isalnum(*ncp) && *ncp != '\'')
- break;
- /* Delimit end of word */
- if (*ncp)
- *ncp++ = '\0';
- /* Check word to see if it's the password */
- if (*cp) {
- if (!strcasecmp(pwstr, cp))
- return 1;
- tcp = reverse(cp);
- if (!strcasecmp(pwstr, tcp))
- return 1;
- cp = ncp;
- } else
- break;
+ krb5_error_code ret;
+ pwqual_handle *h;
+
+ for (h = handle->qual_handles; *h != NULL; h++) {
+ ret = k5_pwqual_check(handle->context, *h, password, policy, princ);
+ if (ret != 0)
+ return ret;
}
return 0;
}
-#endif /* HESIOD */
-/* some of this is stolen from gatekeeper ... */
-kadm5_ret_t
-passwd_check(kadm5_server_handle_t handle,
- char *password, int use_policy, kadm5_policy_ent_t pol,
- krb5_principal principal)
+void
+destroy_pwqual(kadm5_server_handle_t handle)
{
- int nupper = 0,
- nlower = 0,
- ndigit = 0,
- npunct = 0,
- nspec = 0;
- char c, *s, *cp;
-#ifdef HESIOD
- extern struct passwd *hes_getpwnam();
- struct passwd *ent;
-#endif
+ pwqual_handle *h;
- if(use_policy) {
- if(strlen(password) < pol->pw_min_length)
- return KADM5_PASS_Q_TOOSHORT;
- s = password;
- while ((c = *s++)) {
- if (islower((unsigned char) c)) {
- nlower = 1;
- continue;
- }
- else if (isupper((unsigned char) c)) {
- nupper = 1;
- continue;
- } else if (isdigit((unsigned char) c)) {
- ndigit = 1;
- continue;
- } else if (ispunct((unsigned char) c)) {
- npunct = 1;
- continue;
- } else {
- nspec = 1;
- continue;
- }
- }
- if ((nupper + nlower + ndigit + npunct + nspec) < pol->pw_min_classes)
- return KADM5_PASS_Q_CLASS;
- if((find_word(password) == KADM5_OK))
- return KADM5_PASS_Q_DICT;
- else {
- int i, n = krb5_princ_size(handle->context, principal);
- cp = krb5_princ_realm(handle->context, principal)->data;
- if (strcasecmp(cp, password) == 0)
- return KADM5_PASS_Q_DICT;
- for (i = 0; i < n ; i++) {
- cp = krb5_princ_component(handle->context, principal, i)->data;
- if (strcasecmp(cp, password) == 0)
- return KADM5_PASS_Q_DICT;
-#ifdef HESIOD
- ent = hes_getpwnam(cp);
- if (ent && ent->pw_gecos)
- if (str_check_gecos(ent->pw_gecos, password))
- return KADM5_PASS_Q_DICT; /* XXX new error code? */
-#endif
- }
- return KADM5_OK;
- }
- } else {
- if (strlen(password) < 1)
- return KADM5_PASS_Q_TOOSHORT;
- }
- return KADM5_OK;
+ for (h = handle->qual_handles; *h != NULL; h++)
+ k5_pwqual_close(handle->context, *h);
+ k5_pwqual_free_handles(handle->context, handle->qual_handles);
+ handle->qual_handles = NULL;
}
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index 6b14d3b..dc16406 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -292,7 +292,7 @@ kadm5_create_principal_3(void *server_handle,
have_polent = TRUE;
}
if (password) {
- ret = passwd_check(handle, password, have_polent, &polent,
+ ret = passwd_check(handle, password, have_polent ? &polent : NULL,
entry->principal);
if (ret)
goto cleanup;
@@ -1341,8 +1341,8 @@ kadm5_chpass_principal_3(void *server_handle,
have_pol = 1;
}
- if ((ret = passwd_check(handle, password, adb.aux_attributes &
- KADM5_POLICY, &pol, principal)))
+ if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
+ principal)))
goto done;
ret = krb5_dbe_find_act_mkey(handle->context, master_keylist,