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
11 * The Board of Trustees of the Leland Stanford Junior Unversity
13 * See LICENSE for licensing terms.
17 #include <portable/system.h>
21 #include <plugin/api.h>
24 * Used to store local state. Currently, all we have is the dictionary path,
25 * which we get from kadmind rather than from krb5.conf since it's already a
32 /* The public function exported by the cracklib library. */
33 extern char *FascistCheck(const char *password, const char *dict);
37 * Initialize the module. Ensure that the dictionary file exists and is
38 * readable and store the path in the module context. Returns 0 on success,
39 * non-zero on failure. This function returns failure only if it could not
42 * The dictionary file should not include the trailing .pwd extension.
43 * Currently, we don't cope with a NULL dictionary path.
46 pwcheck_init(void **context, const char *dictionary)
52 if (dictionary == NULL)
54 length = strlen(dictionary) + strlen(".pwd") + 1;
55 path = malloc(length);
58 snprintf(path, length, "%s.pwd", dictionary);
59 if (access(path, R_OK) != 0)
61 path[strlen(path) - strlen(".pwd")] = '\0';
62 ctx = malloc(sizeof(struct context));
65 ctx->dictionary = path;
72 * Check a given password. Takes our local context, the password, the
73 * principal the password is for, and a buffer and buffer length into which to
74 * put any failure message.
77 pwcheck_check(void *context, const char *password, const char *principal,
78 char *errstr, int errstrlen)
85 struct context *ctx = context;
88 * We get the principal (in krb5_unparse_name format) from kadmind and we
89 * want to be sure that the password doesn't match the username, the
90 * username reversed, or the username with trailing digits. We therefore
91 * have to copy the string so that we can manipulate it a bit.
93 if (strcasecmp(password, principal) == 0) {
94 snprintf(errstr, errstrlen, "Password based on username");
97 user = strdup(principal);
99 snprintf(errstr, errstrlen, "Cannot allocate memory");
102 for (p = user; p[0] != '\0'; p++) {
103 if (p[0] == '\\' && p[1] != '\0') {
112 if (strlen(password) == strlen(user)) {
113 if (strcasecmp(password, user) == 0) {
115 snprintf(errstr, errstrlen, "Password based on username");
119 /* Check against the reversed username. */
120 for (i = 0, j = strlen(user) - 1; i < j; i++, j--) {
125 if (strcasecmp(password, user) == 0) {
127 snprintf(errstr, errstrlen, "Password based on username");
131 if (strlen(password) > strlen(user))
132 if (strncasecmp(password, user, strlen(user)) == 0) {
133 q = password + strlen(user);
134 while (isdigit((int) *q))
138 snprintf(errstr, errstrlen, "Password based on username");
143 result = FascistCheck(password, ctx->dictionary);
144 if (result != NULL) {
145 strlcpy(errstr, result, errstrlen);
153 * Cleanly shut down the password strength plugin. The only thing we have to
154 * do is free our context memory.
157 pwcheck_close(void *context)
159 struct context *ctx = context;
162 if (ctx->dictionary != NULL)
163 free(ctx->dictionary);