From: Greg Hudson Date: Wed, 1 Sep 2010 16:40:22 +0000 (+0000) Subject: Password quality pluggable interface X-Git-Tag: krb5-1.9-beta1~114 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=bd399cad888d90e99da3e5787040b34da857a34e;p=krb5.git 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 --- diff --git a/doc/admin.texinfo b/doc/admin.texinfo index 5da912768..fd1be4279 100644 --- a/doc/admin.texinfo +++ b/doc/admin.texinfo @@ -410,6 +410,7 @@ salt. The supported values for salts are as follows. * capaths:: * dbdefaults:: * dbmodules:: +* plugins:: * pkinit client options:: * Sample krb5.conf File:: @end menu @@ -1042,7 +1043,7 @@ This LDAP specific tag indicates the list of LDAP servers that the Kerberos serv This LDAP specific tag indicates the number of connections to be maintained per LDAP server. This value is used if the number of connections per LDAP server are not mentioned in the configuration section under [dbmodules]. The default value is 5. @end table -@node dbmodules, pkinit client options, dbdefaults, krb5.conf +@node dbmodules, plugins, dbdefaults, krb5.conf @subsection [dbmodules] Contains database specific parameters used by the database library. Each tag in the [dbmodules] section of the file names a configuration section for database specific parameters that can be referred to by a realm. The value of the tag is a subsection where the relations in that subsection define the database specific parameters. @@ -1090,7 +1091,65 @@ This LDAP specific tags indicates the number of connections to be maintained per @end table -@node pkinit client options, Sample krb5.conf File, dbmodules, krb5.conf +@node plugins, pkinit client options, dbmodules, krb5.conf + +@menu +* pwqual interface:: +@end menu + +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. + +Each pluggable interface corresponds to a subsection of [plugins]. +All subsections support the same tags: + +@table @b +@itemx 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. + +@itemx 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. + +@itemx 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. +@end table + +The following subsections are currently supported within the [plugins] +section: + +@node pwqual interface, , plugins, plugins + +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): + +@table @b +@itemx dict +Checks against the realm dictionary file + +@itemx empty +Rejects empty passwords + +@itemx hesiod +Checks against user information stored in Hesiod (only if Kerberos was +built with Hesiod support) + +@itemx princ +Checks against components of the principal name +@end table + +@node pkinit client options, Sample krb5.conf File, plugins, krb5.conf @subsection pkinit options @menu diff --git a/doc/krb5conf.texinfo b/doc/krb5conf.texinfo index 911435061..0b4b2d437 100644 --- a/doc/krb5conf.texinfo +++ b/doc/krb5conf.texinfo @@ -89,6 +89,10 @@ client to determine the intermediate realms which may be used in cross-realm authentication. It is also used by the end-service when checking the transited field for trusted intermediate realms. +@itemx plugins +Contains tags to register dynamic plugin modules and to turn modules on +and off. + @ignore this doesn't seem to be used @itemx kdc diff --git a/pwqual_combo/Makefile b/pwqual_combo/Makefile new file mode 100644 index 000000000..ee7905b35 --- /dev/null +++ b/pwqual_combo/Makefile @@ -0,0 +1,14 @@ +# Dummy non-portable build system for sample password quality plugin. + +INCLUDES = -I/path/to/krb5/prefix/include + +all: pwqual_combo.so + +pwqual_combo.so: combo.so + gcc -shared -fPIC -o $@ combo.so + +combo.so: combo.c + gcc -fPIC -DSHARED $(INCLUDES) -g -c combo.c -o $@ + +clean: + rm -f combo.so pwqual_combo.so diff --git a/pwqual_combo/combo.c b/pwqual_combo/combo.c new file mode 100644 index 000000000..1a52ef483 --- /dev/null +++ b/pwqual_combo/combo.c @@ -0,0 +1,187 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * 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. + * + * + * Sample password quality plugin which checks for dictionary word combos + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct combo_moddata_st { + char **word_list; /* list of word pointers */ + char *word_block; /* actual word data */ + size_t word_count; /* number of words */ +} *combo_moddata; + +static int +word_compare(const void *s1, const void *s2) +{ + return (strcasecmp(*(const char **)s1, *(const char **)s2)); +} + +static krb5_error_code +init_dict(combo_moddata dict, const char *dict_file) +{ + int fd; + size_t len, i; + char *p, *t; + struct stat sb; + + if (dict_file == NULL) + return 0; + if ((fd = open(dict_file, O_RDONLY)) == -1) + return (errno == ENOENT) ? 0 : errno; + 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 0; +} + +static void +destroy_dict(combo_moddata dict) +{ + if (dict == NULL) + return; + free(dict->word_list); + free(dict->word_block); + free(dict); + return; +} + +static krb5_error_code +combo_open(krb5_context context, const char *dict_file, + krb5_pwqual_moddata *data) +{ + krb5_error_code ret; + combo_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; +} + +static krb5_error_code +combo_check(krb5_context context, krb5_pwqual_moddata data, + const char *password, const char *policy_name, + krb5_principal princ, const char **languages) +{ + combo_moddata dict = (combo_moddata)data; + size_t i, j, len, pwlen; + const char *remainder; + + if (dict->word_list == NULL) + return 0; + + pwlen = strlen(password); + for (i = 0; i < dict->word_count; i++) { + len = strlen(dict->word_list[i]); + if (len >= pwlen) + continue; + if (strncasecmp(password, dict->word_list[i], len) != 0) + continue; + remainder = password + len; + for (i = 0; i < dict->word_count; i++) { + if (strcasecmp(remainder, dict->word_list[i]) == 0) { + krb5_set_error_message(context, KADM5_PASS_Q_DICT, + "Password may not be a pair of " + "dictionary words"); + return KADM5_PASS_Q_DICT; + } + } + } + + return 0; +} + +static void +combo_close(krb5_context context, krb5_pwqual_moddata data) +{ + destroy_dict((combo_moddata)data); +} + +krb5_error_code +pwqual_combo_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 = combo_open; + vt->check = combo_check; + vt->close = combo_close; + return 0; +} 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__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 +#include +#include + +/* 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 #include #include +#include #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 + +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 +#include +#include +#include +#include +#include "adm_proto.h" +#include +#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 + * 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 + * 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 + * 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 +#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 +#include "server_internal.h" +#include + +#ifdef HESIOD +#include + +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 +#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_MEMORY_H -#include -#endif -#include "adm_proto.h" -#include -#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 - * 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 - * 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. - * 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 - * 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 #include #include - -/* for strcasecmp */ -#include - #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. */