summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-09-01 16:40:22 +0000
committerGreg Hudson <ghudson@mit.edu>2010-09-01 16:40:22 +0000
commitbd399cad888d90e99da3e5787040b34da857a34e (patch)
treeaf01e6e0bd5b66a3dd3e05959f34e61623114916 /src
parent18fda42e0ef82c51228ba7cbfb3915330f0c352e (diff)
downloadkrb5-bd399cad888d90e99da3e5787040b34da857a34e.tar.gz
krb5-bd399cad888d90e99da3e5787040b34da857a34e.tar.xz
krb5-bd399cad888d90e99da3e5787040b34da857a34e.zip
Password quality pluggable interface
Merge branches/plugins2 to trunk. Adds a password quality pluggable interface described in this project page: http://k5wiki.kerberos.org/wiki/Projects/Password_quality_pluggable_interface ticket: 6765 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24284 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/config-files/krb5.conf.M56
-rw-r--r--src/include/Makefile.in1
-rw-r--r--src/include/k5-int.h3
-rw-r--r--src/include/krb5/pwqual_plugin.h109
-rw-r--r--src/lib/kadm5/kadm_err.et1
-rw-r--r--src/lib/kadm5/server_internal.h55
-rw-r--r--src/lib/kadm5/srv/Makefile.in22
-rw-r--r--src/lib/kadm5/srv/libkadm5srv_mit.exports3
-rw-r--r--src/lib/kadm5/srv/pwqual.c115
-rw-r--r--src/lib/kadm5/srv/pwqual_dict.c254
-rw-r--r--src/lib/kadm5/srv/pwqual_empty.c61
-rw-r--r--src/lib/kadm5/srv/pwqual_hesiod.c133
-rw-r--r--src/lib/kadm5/srv/pwqual_princ.c75
-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.c246
-rw-r--r--src/lib/kadm5/srv/svr_principal.c6
-rw-r--r--src/lib/krb5/krb/Makefile.in2
-rw-r--r--src/lib/krb5/krb/plugin.c1
19 files changed, 988 insertions, 367 deletions
diff --git a/src/config-files/krb5.conf.M b/src/config-files/krb5.conf.M
index db3305f59..2995aa2be 100644
--- a/src/config-files/krb5.conf.M
+++ b/src/config-files/krb5.conf.M
@@ -110,6 +110,9 @@ Contains default values for database specific parameters.
.IP [dbmodules]
Contains database specific parameters used by the database library.
+
+.ip [plugins]
+Contains plugin module registration and filtering parameters.
.PP
Each of these sections will be covered in more details in the following
sections.
@@ -682,6 +685,59 @@ is whitespace-separated. The LDAP server is specified by a LDAP URI.
.IP ldap_conns_per_server
This LDAP specific tag indicates the number of connections to be maintained per
LDAP server.
+
+.SH PLUGINS SECTION
+
+Tags in the [plugins] section can be used to register dynamic plugin
+modules and to turn modules on and off. Not every krb5 pluggable
+interface uses the [plugins] section; the ones that do are documented
+here.
+
+.PP
+Each pluggable interface corresponds to a subsection of [plugins].
+All subsections support the same tags:
+
+.IP module
+This tag may have multiple values. Each value is a string of the form
+"modulename:pathname", which causes the shared object located at
+pathname to be registered as a dynamic module named modulename for the
+pluggable interface. If pathname is not an absolute path, it will be
+treated as relative to the "krb5/plugins" subdirectory of the krb5
+library directory.
+
+.IP enable_only
+This tag may have multiple values. If there are values for this tag,
+then only the named modules will be enabled for the pluggable
+interface.
+
+.IP disable
+This tag may have multiple values. If there are values for this tag,
+then the named modules will be disabled for the pluggable interface.
+
+.PP
+The following subsections are currently supported within the [plugins]
+section:
+
+.SS pwqual interface
+
+The pwqual subsection controls modules for the password quality
+interface, which is used to reject weak passwords when passwords are
+changed. In addition to any registered dynamic modules, the following
+built-in modules exist (and may be disabled with the disable tag):
+
+.IP dict
+Checks against the realm dictionary file
+
+.IP empty
+Rejects empty passwords
+
+.IP hesiod
+Checks against user information stored in Hesiod (only if Kerberos was
+built with Hesiod support)
+
+.IP princ
+Checks against components of the principal name
+
.SH FILES
/etc/krb5.conf
.SH SEE ALSO
diff --git a/src/include/Makefile.in b/src/include/Makefile.in
index 930712d18..5c178a24a 100644
--- a/src/include/Makefile.in
+++ b/src/include/Makefile.in
@@ -138,6 +138,7 @@ install-headers-unix install:: krb5/krb5.h profile.h
$(INSTALL_DATA) krb5/krb5.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)krb5.h
$(INSTALL_DATA) $(srcdir)/krb5/locate_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)locate_plugin.h
$(INSTALL_DATA) $(srcdir)/krb5/plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)plugin.h
+ $(INSTALL_DATA) $(srcdir)/krb5/pwqual_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)pwqual_plugin.h
$(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
$(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index ec701c1df..4b2fdd25d 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1516,7 +1516,8 @@ struct plugin_interface {
/* A list of plugin interface IDs. Make sure to increment
* PLUGIN_NUM_INTERFACES when a new interface is added. */
-#define PLUGIN_NUM_INTERFACES 0
+#define PLUGIN_INTERFACE_PWQUAL 0
+#define PLUGIN_NUM_INTERFACES 1
/* Retrieve the plugin module of type interface_id and name modname,
* storing the result into module. */
diff --git a/src/include/krb5/pwqual_plugin.h b/src/include/krb5/pwqual_plugin.h
new file mode 100644
index 000000000..403bb1152
--- /dev/null
+++ b/src/include/krb5/pwqual_plugin.h
@@ -0,0 +1,109 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * prototype/prototype.h
+ *
+ * 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.
+ *
+ *
+ * Declarations for password quality plugin module implementors.
+ *
+ * The password quality pluggable interface currently has only one supported
+ * major version, which is 1. Major version 1 has a current minor version
+ * number of 1.
+ *
+ * Password quality plugin modules should define a function named
+ * pwqual_<modulename>_initvt, matching the signature:
+ *
+ * krb5_error_code
+ * pwqual_modname_initvt(krb5_context context, int maj_ver, int min_ver,
+ * krb5_plugin_vtable vtable);
+ *
+ * The initvt function should:
+ *
+ * - Check that the supplied maj_ver number is supported by the module, or
+ * return KRB5_PLUGIN_VER_NOTSUPP if it is not.
+ *
+ * - Cast the vtable pointer as appropriate for maj_ver:
+ * maj_ver == 1: Cast to krb5_pwqual_vtable
+ *
+ * - Initialize the methods of the vtable, stopping as appropriate for the
+ * supplied min_ver. Optional methods may be left uninitialized.
+ *
+ * Memory for the vtable is allocated by the caller, not by the module.
+ */
+
+#ifndef KRB5_PWQUAL_PLUGIN_H
+#define KRB5_PWQUAL_PLUGIN_H
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+#include <kadm5/admin.h>
+
+/* An abstract type for password quality module data. */
+typedef struct krb5_pwqual_moddata_st *krb5_pwqual_moddata;
+
+/*** Method type declarations ***/
+
+/* Optional: Initialize module data. dictfile is the realm's configured
+ * dictionary filename. */
+typedef krb5_error_code
+(*krb5_pwqual_open_fn)(krb5_context context, const char *dict_file,
+ krb5_pwqual_moddata *data);
+
+/*
+ * Mandatory: Check a password for the principal princ, which has an associated
+ * password policy named policy_name (or no associated policy if policy_name is
+ * NULL). The parameter languages, if not NULL, contains a null-terminated
+ * list of client-specified language tags as defined in RFC 5646. The method
+ * should return one of the following errors if the password fails quality
+ * standards:
+ *
+ * - KADM5_PASS_Q_TOOSHORT: password should be longer
+ * - KADM5_PASS_Q_CLASS: password must have more character classes
+ * - KADM5_PASS_Q_DICT: password contains dictionary words
+ * - KADM5_PASS_Q_GENERIC: unspecified quality failure
+ *
+ * The module should also set an extended error message with
+ * krb5_set_error_message(). The message may be localized according to one of
+ * the language tags in languages.
+ */
+typedef krb5_error_code
+(*krb5_pwqual_check_fn)(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, const char *policy_name,
+ krb5_principal princ, const char **languages);
+
+/* Optional: Release resources used by module data. */
+typedef void
+(*krb5_pwqual_close_fn)(krb5_context context, krb5_pwqual_moddata data);
+
+/*** vtable declarations **/
+
+/* Password quality plugin vtable for major version 1. */
+typedef struct krb5_pwqual_vtable_st {
+ krb5_pwqual_open_fn open;
+ krb5_pwqual_check_fn check;
+ krb5_pwqual_close_fn close;
+ /* Minor version 1 ends here. */
+} *krb5_pwqual_vtable;
+
+#endif /* KRB5_PWQUAL_PLUGIN_H */
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index a6086b111..5530ccafa 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -61,4 +61,5 @@ error_code KADM5_SETKEY3_ETYPE_MISMATCH, "Mismatched enctypes for setkey3"
error_code KADM5_MISSING_KRB5_CONF_PARAMS, "Missing parameters in krb5.conf required for kadmin client"
error_code KADM5_XDR_FAILURE, "XDR encoding error"
error_code KADM5_CANT_RESOLVE, "Cannot resolve network address for admin server in requested realm"
+error_code KADM5_PASS_Q_GENERIC, "Unspecified password quality failure"
end
diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h
index cc589fad2..076c18fc0 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,46 @@ 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 all available password quality plugin modules, bind each module to the
+ * realm's dictionary file, 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,
+ const char *dict_file);
+
+/* Release a handle list allocated by k5_pwqual_load. */
+void
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles);
+
+/* Check a password using a password quality plugin module. */
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, const char *policy_name,
+ krb5_principal princ);
+
+/*** initvt functions for built-in password quality modules ***/
+
+/* The dict module checks passwords against the realm's dictionary. */
+krb5_error_code
+pwqual_dict_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The empty module rejects empty passwords (even with no password policy). */
+krb5_error_code
+pwqual_empty_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The hesiod module checks passwords against GECOS fields from Hesiod passwd
+ * information (only if the tree was built with Hesiod support). */
+krb5_error_code
+pwqual_hesiod_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+/* The princ module checks passwords against principal components. */
+krb5_error_code
+pwqual_princ_initvt(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 c7e0fac9b..a513035d5 100644
--- a/src/lib/kadm5/srv/Makefile.in
+++ b/src/lib/kadm5/srv/Makefile.in
@@ -27,36 +27,48 @@ 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_empty.c \
+ $(srcdir)/pwqual_hesiod.c \
+ $(srcdir)/pwqual_princ.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_empty.$(OBJEXT) \
+ pwqual_hesiod.$(OBJEXT) \
+ pwqual_princ.$(OBJEXT) \
+ 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_empty.o \
+ pwqual_hesiod.o \
+ pwqual_princ.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 6da95bd7c..345957a13 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 000000000..646bfcb3c
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual.c
@@ -0,0 +1,115 @@
+/* -*- 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,
+ const char *dict_file)
+{
+ krb5_error_code ret;
+ krb5_plugin_initvt_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, initialize its vtable, and bind the
+ * dictionary filename. */
+ 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) { /* Failed vtable init is non-fatal. */
+ free(handle);
+ continue;
+ }
+ handle->data = NULL;
+ if (handle->vt.open != NULL) {
+ ret = handle->vt.open(context, dict_file, &handle->data);
+ if (ret != 0) /* Failed dictionary binding is fatal. */
+ goto cleanup;
+ }
+ list[count++] = handle;
+ list[count] = NULL;
+ handle = NULL;
+ }
+ list[count] = NULL;
+
+ *handles = list;
+ list = NULL;
+
+cleanup:
+ free(handle);
+ k5_plugin_free_modules(context, modules);
+ k5_pwqual_free_handles(context, list);
+ return ret;
+}
+
+void
+k5_pwqual_free_handles(krb5_context context, pwqual_handle *handles)
+{
+ pwqual_handle *hp, handle;
+
+ if (handles == NULL)
+ return;
+ for (hp = handles; *hp != NULL; hp++) {
+ handle = *hp;
+ if (handle->vt.close != NULL)
+ handle->vt.close(context, handle->data);
+ }
+ free(handles);
+}
+
+krb5_error_code
+k5_pwqual_check(krb5_context context, pwqual_handle handle,
+ const char *password, const char *policy_name,
+ krb5_principal princ)
+{
+ return handle->vt.check(context, handle->data, password, policy_name,
+ princ, NULL);
+}
diff --git a/src/lib/kadm5/srv/pwqual_dict.c b/src/lib/kadm5/srv/pwqual_dict.c
new file mode 100644
index 000000000..2df9a8b94
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_dict.c
@@ -0,0 +1,254 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_dict.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.
+ *
+ * Dictionary initialization and lookup code is (see top-level NOTICE file for
+ * license):
+ *
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ *
+ * Password quality module to look up passwords within the realm dictionary.
+ */
+
+#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, const char *policy_name,
+ krb5_principal princ, const char **languages)
+{
+ dict_moddata dict = (dict_moddata)data;
+
+ /* Don't check the dictionary for principals with no password policy. */
+ if (policy_name == 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;
+
+ 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_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ 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_empty.c b/src/lib/kadm5/srv/pwqual_empty.c
new file mode 100644
index 000000000..df3505aaf
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_empty.c
@@ -0,0 +1,61 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_empty.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 reject empty passwords.
+ */
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include "server_internal.h"
+
+static krb5_error_code
+empty_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, const char *policy_name,
+ krb5_principal princ, const char **languages)
+{
+ /* Unlike other built-in modules, this one operates even for principals
+ * with no password policy. */
+ if (*password == '\0') {
+ krb5_set_error_message(context, KADM5_PASS_Q_TOOSHORT,
+ "Empty passwords are not allowed");
+ return KADM5_PASS_Q_TOOSHORT;
+ }
+ return 0;
+}
+
+krb5_error_code
+pwqual_empty_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->check = empty_check;
+ return 0;
+}
diff --git a/src/lib/kadm5/srv/pwqual_hesiod.c b/src/lib/kadm5/srv/pwqual_hesiod.c
new file mode 100644
index 000000000..993992d19
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_hesiod.c
@@ -0,0 +1,133 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_hesiod.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 check passwords against GECOS fields of Hesiod
+ * passwd information, if the tree is compiled with Hesiod support.
+ */
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include "server_internal.h"
+#include <ctype.h>
+
+#ifdef HESIOD
+#include <pwd.h>
+
+static char *
+reverse(char *str, char *newstr, size_t newstr_size)
+{
+ char *p, *q;
+ size_t i;
+
+ i = strlen(str);
+ if (i >= newstr_size)
+ i = newstr_size - 1;
+ p = str + i - 1;
+ q = newstr;
+ q[i] = '\0';
+ for (; i > 0; i--)
+ *q++ = *p--;
+
+ return newstr;
+}
+
+static int
+str_check_gecos(char *gecos, const char *pwstr)
+{
+ char *cp, *ncp, *tcp, revbuf[80];
+
+ 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, revbuf, sizeof(revbuf));
+ if (!strcasecmp(pwstr, tcp))
+ return 1;
+ cp = ncp;
+ } else
+ break;
+ }
+ return 0;
+}
+#endif /* HESIOD */
+
+static krb5_error_code
+hesiod_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, const char *policy_name,
+ krb5_principal princ, const char **languages)
+{
+#ifdef HESIOD
+ extern struct passwd *hes_getpwnam();
+ struct passwd *ent;
+ int i, n;
+ const char *cp;
+
+ /* Don't check for principals with no password policy. */
+ if (policy_name == NULL)
+ return 0;
+
+ n = krb5_princ_size(handle->context, princ);
+ for (i = 0; i < n; i++) {
+ ent = hes_getpwnam(cp);
+ if (ent && ent->pw_gecos && str_check_gecos(ent->pw_gecos, password)) {
+ krb5_set_error_message(context, KADM5_PASS_Q_DICT,
+ "Password maynot match user information.");
+ return KADM5_PASS_Q_DICT;
+ }
+ }
+#endif /* HESIOD */
+ return 0;
+}
+
+krb5_error_code
+pwqual_hesiod_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->check = hesiod_check;
+ return 0;
+}
diff --git a/src/lib/kadm5/srv/pwqual_princ.c b/src/lib/kadm5/srv/pwqual_princ.c
new file mode 100644
index 000000000..dfe5f2033
--- /dev/null
+++ b/src/lib/kadm5/srv/pwqual_princ.c
@@ -0,0 +1,75 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/kadm5/srv/pwqual_princ.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 check passwords against principal components.
+ */
+
+#include "k5-platform.h"
+#include <krb5/pwqual_plugin.h>
+#include "server_internal.h"
+
+static krb5_error_code
+princ_check(krb5_context context, krb5_pwqual_moddata data,
+ const char *password, const char *policy_name,
+ krb5_principal princ, const char **languages)
+{
+ int i, n;
+ char *cp;
+
+ /* Don't check for principals with no password policy. */
+ if (policy_name == NULL)
+ return 0;
+
+ /* 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) {
+ krb5_set_error_message(context, KADM5_PASS_Q_DICT,
+ "Password may not match principal name");
+ return KADM5_PASS_Q_DICT;
+ }
+ }
+
+ return 0;
+}
+
+krb5_error_code
+pwqual_princ_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_pwqual_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (krb5_pwqual_vtable)vtable;
+ vt->check = princ_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 81cc5f997..000000000
--- 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 557ef0ad4..9ebc13eea 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 1faeb86b1..93369f000 100644
--- a/src/lib/kadm5/srv/server_misc.c
+++ b/src/lib/kadm5/srv/server_misc.c
@@ -1,22 +1,38 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
+ * 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.
+ *
+ * check_against_policy code is originally (see top-level NOTICE file for
+ * license):
+ *
* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
*
- * $Header$
*/
-#if !defined(lint) && !defined(__CODECENTER__)
-static char *rcsid = "$Header$";
-#endif
-
#include "k5-int.h"
#include <kdb.h>
#include <ctype.h>
#include <pwd.h>
-
-/* for strcasecmp */
-#include <string.h>
-
#include "server_internal.h"
kadm5_ret_t
@@ -37,147 +53,99 @@ 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;
+kadm5_ret_t
+init_pwqual(kadm5_server_handle_t handle)
{
- 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);
+ krb5_error_code ret;
+ pwqual_handle *list;
+ const char *dict_file = NULL;
+
+ /* Register the built-in password quality modules. */
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "dict", pwqual_dict_initvt);
+ if (ret != 0)
+ return ret;
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "empty", pwqual_empty_initvt);
+ if (ret != 0)
+ return ret;
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "hesiod", pwqual_hesiod_initvt);
+ if (ret != 0)
+ return ret;
+ ret = k5_plugin_register(handle->context, PLUGIN_INTERFACE_PWQUAL,
+ "princ", pwqual_princ_initvt);
+ if (ret != 0)
+ return ret;
+
+ /* Load all available password quality modules. */
+ if (handle->params.mask & KADM5_CONFIG_DICT_FILE)
+ dict_file = handle->params.dict_file;
+ ret = k5_pwqual_load(handle->context, &list, dict_file);
+ if (ret != 0)
+ return ret;
+
+ handle->qual_handles = list;
+ return 0;
}
-#endif /* HESIOD */
-#if 0
-static int
-lower(str)
- char *str;
+/* Check that a password meets the quality constraints given in pol. */
+static kadm5_ret_t
+check_against_policy(kadm5_server_handle_t handle, const char *password,
+ kadm5_policy_ent_t pol)
{
- register char *cp;
- int effect=0;
-
- for (cp = str; *cp; cp++) {
- if (isupper(*cp)) {
- *cp = tolower(*cp);
- effect++;
- }
+ int hasupper = 0, haslower = 0, hasdigit = 0, haspunct = 0, hasspec = 0;
+ int c, nclasses;
+
+ /* Check against the policy's minimum length. */
+ if (strlen(password) < (size_t)pol->pw_min_length)
+ return KADM5_PASS_Q_TOOSHORT;
+
+ /* Check against the policy's minimum number of character classes. */
+ while ((c = (unsigned char)*password++) != '\0') {
+ if (islower(c))
+ haslower = 1;
+ else if (isupper(c))
+ hasupper = 1;
+ else if (isdigit(c))
+ hasdigit = 1;
+ else if (ispunct(c))
+ haspunct = 1;
+ else
+ hasspec = 1;
}
- return(effect);
+ nclasses = hasupper + haslower + hasdigit + haspunct + hasspec;
+ if (nclasses < pol->pw_min_classes)
+ return KADM5_PASS_Q_CLASS;
+ return KADM5_OK;
}
-#endif
-#ifdef HESIOD
-static int
-str_check_gecos(gecos, pwstr)
- char *gecos;
- char *pwstr;
+/* Check a password against all available password quality plugin modules
+ * and against policy. */
+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;
+ const char *polname = (policy == NULL) ? NULL : policy->policy;
+
+ if (policy != NULL) {
+ ret = check_against_policy(handle, password, policy);
+ if (ret != 0)
+ return ret;
+ }
+ for (h = handle->qual_handles; *h != NULL; h++) {
+ ret = k5_pwqual_check(handle->context, *h, password, polname, 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
-
- 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;
+ 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 6b14d3ba6..dc1640643 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,
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 4f3081428..0737a2e53 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -353,7 +353,7 @@ T_PAC_OBJS= t_pac.o pac.o pac_sign.o copy_data.o
T_PRINC_OBJS= t_princ.o parse.o unparse.o
-T_ETYPES_OBJS= t_etypes.o init_ctx.o plugin.o etype_list.o
+T_ETYPES_OBJS= t_etypes.o init_ctx.o etype_list.o plugin.o
t_walk_rtree: $(T_WALK_RTREE_OBJS) $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o t_walk_rtree $(T_WALK_RTREE_OBJS) $(KRB5_BASE_LIBS)
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c
index d868f3319..277da2472 100644
--- a/src/lib/krb5/krb/plugin.c
+++ b/src/lib/krb5/krb/plugin.c
@@ -31,6 +31,7 @@
#include "k5-int.h"
const char *interface_names[PLUGIN_NUM_INTERFACES] = {
+ "pwqual"
};
/* Return the context's interface structure for id, or NULL if invalid. */