]> granicus.if.org Git - postgresql/blob - contrib/passwordcheck/passwordcheck.c
Adjust blank lines around PG_MODULE_MAGIC defines, for consistency
[postgresql] / contrib / passwordcheck / passwordcheck.c
1 /*-------------------------------------------------------------------------
2  *
3  * passwordcheck.c
4  *
5  *
6  * Copyright (c) 2009-2014, PostgreSQL Global Development Group
7  *
8  * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
9  *
10  * IDENTIFICATION
11  *        contrib/passwordcheck/passwordcheck.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #ifdef USE_CRACKLIB
20 #include <crack.h>
21 #endif
22
23 #include "commands/user.h"
24 #include "fmgr.h"
25 #include "libpq/md5.h"
26
27 PG_MODULE_MAGIC;
28
29 /* passwords shorter than this will be rejected */
30 #define MIN_PWD_LENGTH 8
31
32 extern void _PG_init(void);
33
34 /*
35  * check_password
36  *
37  * performs checks on an encrypted or unencrypted password
38  * ereport's if not acceptable
39  *
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
46  *
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.
50  */
51 static void
52 check_password(const char *username,
53                            const char *password,
54                            int password_type,
55                            Datum validuntil_time,
56                            bool validuntil_null)
57 {
58         int                     namelen = strlen(username);
59         int                     pwdlen = strlen(password);
60         char            encrypted[MD5_PASSWD_LEN + 1];
61         int                     i;
62         bool            pwd_has_letter,
63                                 pwd_has_nonletter;
64
65         switch (password_type)
66         {
67                 case PASSWORD_TYPE_MD5:
68
69                         /*
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.)
74                          *
75                          * We only check for username = password.
76                          */
77                         if (!pg_md5_encrypt(username, username, namelen, encrypted))
78                                 elog(ERROR, "password encryption failed");
79                         if (strcmp(password, encrypted) == 0)
80                                 ereport(ERROR,
81                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82                                                  errmsg("password must not contain user name")));
83                         break;
84
85                 case PASSWORD_TYPE_PLAINTEXT:
86
87                         /*
88                          * For unencrypted passwords we can perform better checks
89                          */
90
91                         /* enforce minimum length */
92                         if (pwdlen < MIN_PWD_LENGTH)
93                                 ereport(ERROR,
94                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
95                                                  errmsg("password is too short")));
96
97                         /* check if the password contains the username */
98                         if (strstr(password, username))
99                                 ereport(ERROR,
100                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
101                                                  errmsg("password must not contain user name")));
102
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++)
107                         {
108                                 /*
109                                  * isalpha() does not work for multibyte encodings but let's
110                                  * consider non-ASCII characters non-letters
111                                  */
112                                 if (isalpha((unsigned char) password[i]))
113                                         pwd_has_letter = true;
114                                 else
115                                         pwd_has_nonletter = true;
116                         }
117                         if (!pwd_has_letter || !pwd_has_nonletter)
118                                 ereport(ERROR,
119                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
120                                 errmsg("password must contain both letters and nonletters")));
121
122 #ifdef USE_CRACKLIB
123                         /* call cracklib to check password */
124                         if (FascistCheck(password, CRACKLIB_DICTPATH))
125                                 ereport(ERROR,
126                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127                                                  errmsg("password is easily cracked")));
128 #endif
129                         break;
130
131                 default:
132                         elog(ERROR, "unrecognized password type: %d", password_type);
133                         break;
134         }
135
136         /* all checks passed, password is ok */
137 }
138
139 /*
140  * Module initialization function
141  */
142 void
143 _PG_init(void)
144 {
145         /* activate password checks when the module is loaded */
146         check_password_hook = check_password;
147 }