1 /*-------------------------------------------------------------------------
6 * Copyright (c) 2009-2023, PostgreSQL Global Development Group
8 * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
11 * contrib/passwordcheck/passwordcheck.c
13 *-------------------------------------------------------------------------
23 #include "commands/user.h"
25 #include "libpq/crypt.h"
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
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.
53 check_password(const char *username
,
54 const char *shadow_pass
,
55 PasswordType password_type
,
56 Datum validuntil_time
,
59 if (prev_check_password_hook
)
60 prev_check_password_hook(username
, shadow_pass
,
61 password_type
, validuntil_time
,
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
)
78 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
79 errmsg("password must not equal user name")));
84 * For unencrypted passwords we can perform better checks
86 const char *password
= shadow_pass
;
87 int pwdlen
= strlen(password
);
95 /* enforce minimum length */
96 if (pwdlen
< MIN_PWD_LENGTH
)
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
))
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;
119 pwd_has_nonletter
= true;
121 if (!pwd_has_letter
|| !pwd_has_nonletter
)
123 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
124 errmsg("password must contain both letters and nonletters")));
127 /* call cracklib to check password */
128 if ((reason
= FascistCheck(password
, CRACKLIB_DICTPATH
)))
130 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
131 errmsg("password is easily cracked"),
132 errdetail_log("cracklib diagnostic: %s", reason
)));
136 /* all checks passed, password is ok */
140 * Module initialization function
145 /* activate password checks when the module is loaded */
146 prev_check_password_hook
= check_password_hook
;
147 check_password_hook
= check_password
;