2 * The public APIs of the password strength checking kadmind plugin.
4 * Provides the public pwcheck_init, pwcheck_check, and pwcheck_close APIs for
7 * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
8 * on behalf of Stanford University.
9 * Extensive modifications by Russ Allbery <rra@stanford.edu>
10 * Copyright 2006, 2007, 2009, 2012, 2013
11 * The Board of Trustees of the Leland Stanford Junior Unversity
13 * See LICENSE for licensing terms.
17 #include <portable/kadmin.h>
18 #include <portable/krb5.h>
19 #include <portable/system.h>
24 #include <plugin/api.h>
26 /* Heimdal doesn't define KADM5_PASS_Q_GENERIC. */
27 #ifndef KADM5_PASS_Q_GENERIC
28 # define KADM5_PASS_Q_GENERIC KADM5_PASS_Q_DICT
32 * Used to store local state. Currently, all we have is the dictionary path,
33 * which we get from kadmind rather than from krb5.conf since it's already a
40 /* The public function exported by the cracklib library. */
41 extern char *FascistCheck(const char *password, const char *dict);
45 * Initialize the module. Ensure that the dictionary file exists and is
46 * readable and store the path in the module context. Returns 0 on success,
47 * non-zero on failure. This function returns failure only if it could not
50 * The dictionary file should not include the trailing .pwd extension.
51 * Currently, we don't cope with a NULL dictionary path.
54 pwcheck_init(void **context, const char *dictionary)
60 if (dictionary == NULL)
61 return KADM5_MISSING_CONF_PARAMS;
62 length = strlen(dictionary) + strlen(".pwd") + 1;
63 path = malloc(length);
66 snprintf(path, length, "%s.pwd", dictionary);
67 if (access(path, R_OK) != 0)
69 path[strlen(path) - strlen(".pwd")] = '\0';
70 ctx = malloc(sizeof(struct context));
73 ctx->dictionary = path;
80 * Check a given password. Takes our local context, the password, the
81 * principal the password is for, and a buffer and buffer length into which to
82 * put any failure message.
85 pwcheck_check(void *context, const char *password, const char *principal,
86 char *errstr, int errstrlen)
94 struct context *ctx = context;
97 * We get the principal (in krb5_unparse_name format) from kadmind and we
98 * want to be sure that the password doesn't match the username, the
99 * username reversed, or the username with trailing digits. We therefore
100 * have to copy the string so that we can manipulate it a bit.
102 if (strcasecmp(password, principal) == 0) {
103 snprintf(errstr, errstrlen, "Password based on username");
104 return KADM5_PASS_Q_GENERIC;
106 user = strdup(principal);
109 snprintf(errstr, errstrlen, "Cannot allocate memory");
112 for (p = user; p[0] != '\0'; p++) {
113 if (p[0] == '\\' && p[1] != '\0') {
122 if (strlen(password) == strlen(user)) {
123 if (strcasecmp(password, user) == 0) {
125 snprintf(errstr, errstrlen, "Password based on username");
126 return KADM5_PASS_Q_GENERIC;
129 /* Check against the reversed username. */
130 for (i = 0, j = strlen(user) - 1; i < j; i++, j--) {
135 if (strcasecmp(password, user) == 0) {
137 snprintf(errstr, errstrlen, "Password based on username");
138 return KADM5_PASS_Q_GENERIC;
141 if (strlen(password) > strlen(user))
142 if (strncasecmp(password, user, strlen(user)) == 0) {
143 q = password + strlen(user);
144 while (isdigit((int) *q))
148 snprintf(errstr, errstrlen, "Password based on username");
149 return KADM5_PASS_Q_GENERIC;
153 result = FascistCheck(password, ctx->dictionary);
154 if (result != NULL) {
155 strlcpy(errstr, result, errstrlen);
156 return KADM5_PASS_Q_GENERIC;
163 * Cleanly shut down the password strength plugin. The only thing we have to
164 * do is free our context memory.
167 pwcheck_close(void *context)
169 struct context *ctx = context;
172 if (ctx->dictionary != NULL)
173 free(ctx->dictionary);