1 /*-------------------------------------------------------------------------
6 * Copyright (c) 2009-2014, 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/md5.h"
29 /* passwords shorter than this will be rejected */
30 #define MIN_PWD_LENGTH 8
32 extern void _PG_init(void);
37 * performs checks on an encrypted or unencrypted password
38 * ereport's if not acceptable
40 * username: name of role being created or changed
41 * password: new password (possibly already encrypted)
42 * password_type: PASSWORD_TYPE_PLAINTEXT or PASSWORD_TYPE_MD5 (there
43 * could be other encryption schemes in future)
44 * validuntil_time: password expiration time, as a timestamptz Datum
45 * validuntil_null: true if password expiration time is NULL
47 * This sample implementation doesn't pay any attention to the password
48 * expiration time, but you might wish to insist that it be non-null and
49 * not too far in the future.
52 check_password(const char *username,
55 Datum validuntil_time,
58 int namelen = strlen(username);
59 int pwdlen = strlen(password);
60 char encrypted[MD5_PASSWD_LEN + 1];
65 switch (password_type)
67 case PASSWORD_TYPE_MD5:
70 * Unfortunately we cannot perform exhaustive checks on encrypted
71 * passwords - we are restricted to guessing. (Alternatively, we
72 * could insist on the password being presented non-encrypted, but
73 * that has its own security disadvantages.)
75 * We only check for username = password.
77 if (!pg_md5_encrypt(username, username, namelen, encrypted))
78 elog(ERROR, "password encryption failed");
79 if (strcmp(password, encrypted) == 0)
81 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82 errmsg("password must not contain user name")));
85 case PASSWORD_TYPE_PLAINTEXT:
88 * For unencrypted passwords we can perform better checks
91 /* enforce minimum length */
92 if (pwdlen < MIN_PWD_LENGTH)
94 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
95 errmsg("password is too short")));
97 /* check if the password contains the username */
98 if (strstr(password, username))
100 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
101 errmsg("password must not contain user name")));
103 /* check if the password contains both letters and non-letters */
104 pwd_has_letter = false;
105 pwd_has_nonletter = false;
106 for (i = 0; i < pwdlen; i++)
109 * isalpha() does not work for multibyte encodings but let's
110 * consider non-ASCII characters non-letters
112 if (isalpha((unsigned char) password[i]))
113 pwd_has_letter = true;
115 pwd_has_nonletter = true;
117 if (!pwd_has_letter || !pwd_has_nonletter)
119 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
120 errmsg("password must contain both letters and nonletters")));
123 /* call cracklib to check password */
124 if (FascistCheck(password, CRACKLIB_DICTPATH))
126 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127 errmsg("password is easily cracked")));
132 elog(ERROR, "unrecognized password type: %d", password_type);
136 /* all checks passed, password is ok */
140 * Module initialization function
145 /* activate password checks when the module is loaded */
146 check_password_hook = check_password;