Fix another incorrect data type choice from commit dc2123400.
[pgsql.git] / contrib / passwordcheck / passwordcheck.c
blobae4a0abe2e3c8d61ad319350872f77ba96f41714
1 /*-------------------------------------------------------------------------
3 * passwordcheck.c
6 * Copyright (c) 2009-2023, PostgreSQL Global Development Group
8 * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
10 * IDENTIFICATION
11 * contrib/passwordcheck/passwordcheck.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include <ctype.h>
19 #ifdef USE_CRACKLIB
20 #include <crack.h>
21 #endif
23 #include "commands/user.h"
24 #include "fmgr.h"
25 #include "libpq/crypt.h"
27 PG_MODULE_MAGIC;
29 /* Saved hook value in case of unload */
30 static check_password_hook_type prev_check_password_hook = NULL;
32 /* passwords shorter than this will be rejected */
33 #define MIN_PWD_LENGTH 8
36 * check_password
38 * performs checks on an encrypted or unencrypted password
39 * ereport's if not acceptable
41 * username: name of role being created or changed
42 * password: new password (possibly already encrypted)
43 * password_type: PASSWORD_TYPE_* code, to indicate if the password is
44 * in plaintext or encrypted form.
45 * validuntil_time: password expiration time, as a timestamptz Datum
46 * validuntil_null: true if password expiration time is NULL
48 * This sample implementation doesn't pay any attention to the password
49 * expiration time, but you might wish to insist that it be non-null and
50 * not too far in the future.
52 static void
53 check_password(const char *username,
54 const char *shadow_pass,
55 PasswordType password_type,
56 Datum validuntil_time,
57 bool validuntil_null)
59 if (prev_check_password_hook)
60 prev_check_password_hook(username, shadow_pass,
61 password_type, validuntil_time,
62 validuntil_null);
64 if (password_type != PASSWORD_TYPE_PLAINTEXT)
67 * Unfortunately we cannot perform exhaustive checks on encrypted
68 * passwords - we are restricted to guessing. (Alternatively, we could
69 * insist on the password being presented non-encrypted, but that has
70 * its own security disadvantages.)
72 * We only check for username = password.
74 const char *logdetail = NULL;
76 if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
77 ereport(ERROR,
78 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79 errmsg("password must not equal user name")));
81 else
84 * For unencrypted passwords we can perform better checks
86 const char *password = shadow_pass;
87 int pwdlen = strlen(password);
88 int i;
89 bool pwd_has_letter,
90 pwd_has_nonletter;
91 #ifdef USE_CRACKLIB
92 const char *reason;
93 #endif
95 /* enforce minimum length */
96 if (pwdlen < MIN_PWD_LENGTH)
97 ereport(ERROR,
98 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
99 errmsg("password is too short")));
101 /* check if the password contains the username */
102 if (strstr(password, username))
103 ereport(ERROR,
104 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
105 errmsg("password must not contain user name")));
107 /* check if the password contains both letters and non-letters */
108 pwd_has_letter = false;
109 pwd_has_nonletter = false;
110 for (i = 0; i < pwdlen; i++)
113 * isalpha() does not work for multibyte encodings but let's
114 * consider non-ASCII characters non-letters
116 if (isalpha((unsigned char) password[i]))
117 pwd_has_letter = true;
118 else
119 pwd_has_nonletter = true;
121 if (!pwd_has_letter || !pwd_has_nonletter)
122 ereport(ERROR,
123 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124 errmsg("password must contain both letters and nonletters")));
126 #ifdef USE_CRACKLIB
127 /* call cracklib to check password */
128 if ((reason = FascistCheck(password, CRACKLIB_DICTPATH)))
129 ereport(ERROR,
130 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
131 errmsg("password is easily cracked"),
132 errdetail_log("cracklib diagnostic: %s", reason)));
133 #endif
136 /* all checks passed, password is ok */
140 * Module initialization function
142 void
143 _PG_init(void)
145 /* activate password checks when the module is loaded */
146 prev_check_password_hook = check_password_hook;
147 check_password_hook = check_password;