PostgreSQL Source Code  git master
passwordcheck.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * passwordcheck.c
4  *
5  *
6  * Copyright (c) 2009-2020, 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/crypt.h"
26 
28 
29 /* Saved hook value in case of unload */
31 
32 /* passwords shorter than this will be rejected */
33 #define MIN_PWD_LENGTH 8
34 
35 extern void _PG_init(void);
36 extern void _PG_fini(void);
37 
38 /*
39  * check_password
40  *
41  * performs checks on an encrypted or unencrypted password
42  * ereport's if not acceptable
43  *
44  * username: name of role being created or changed
45  * password: new password (possibly already encrypted)
46  * password_type: PASSWORD_TYPE_* code, to indicate if the password is
47  * in plaintext or encrypted form.
48  * validuntil_time: password expiration time, as a timestamptz Datum
49  * validuntil_null: true if password expiration time is NULL
50  *
51  * This sample implementation doesn't pay any attention to the password
52  * expiration time, but you might wish to insist that it be non-null and
53  * not too far in the future.
54  */
55 static void
57  const char *shadow_pass,
58  PasswordType password_type,
59  Datum validuntil_time,
60  bool validuntil_null)
61 {
63  prev_check_password_hook(username, shadow_pass,
64  password_type, validuntil_time,
65  validuntil_null);
66 
67  if (password_type != PASSWORD_TYPE_PLAINTEXT)
68  {
69  /*
70  * Unfortunately we cannot perform exhaustive checks on encrypted
71  * passwords - we are restricted to guessing. (Alternatively, we could
72  * insist on the password being presented non-encrypted, but that has
73  * its own security disadvantages.)
74  *
75  * We only check for username = password.
76  */
77  char *logdetail;
78 
79  if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
80  ereport(ERROR,
81  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82  errmsg("password must not equal user name")));
83  }
84  else
85  {
86  /*
87  * For unencrypted passwords we can perform better checks
88  */
89  const char *password = shadow_pass;
90  int pwdlen = strlen(password);
91  int i;
92  bool pwd_has_letter,
93  pwd_has_nonletter;
94 #ifdef USE_CRACKLIB
95  const char *reason;
96 #endif
97 
98  /* enforce minimum length */
99  if (pwdlen < MIN_PWD_LENGTH)
100  ereport(ERROR,
101  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
102  errmsg("password is too short")));
103 
104  /* check if the password contains the username */
105  if (strstr(password, username))
106  ereport(ERROR,
107  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
108  errmsg("password must not contain user name")));
109 
110  /* check if the password contains both letters and non-letters */
111  pwd_has_letter = false;
112  pwd_has_nonletter = false;
113  for (i = 0; i < pwdlen; i++)
114  {
115  /*
116  * isalpha() does not work for multibyte encodings but let's
117  * consider non-ASCII characters non-letters
118  */
119  if (isalpha((unsigned char) password[i]))
120  pwd_has_letter = true;
121  else
122  pwd_has_nonletter = true;
123  }
124  if (!pwd_has_letter || !pwd_has_nonletter)
125  ereport(ERROR,
126  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127  errmsg("password must contain both letters and nonletters")));
128 
129 #ifdef USE_CRACKLIB
130  /* call cracklib to check password */
131  if ((reason = FascistCheck(password, CRACKLIB_DICTPATH)))
132  ereport(ERROR,
133  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
134  errmsg("password is easily cracked"),
135  errdetail_log("cracklib diagnostic: %s", reason)));
136 #endif
137  }
138 
139  /* all checks passed, password is ok */
140 }
141 
142 /*
143  * Module initialization function
144  */
145 void
146 _PG_init(void)
147 {
148  /* activate password checks when the module is loaded */
151 }
152 
153 /*
154  * Module unload function
155  */
156 void
157 _PG_fini(void)
158 {
159  /* uninstall hook */
161 }
static check_password_hook_type prev_check_password_hook
Definition: passwordcheck.c:30
PG_MODULE_MAGIC
Definition: passwordcheck.c:27
static void check_password(const char *username, const char *shadow_pass, PasswordType password_type, Datum validuntil_time, bool validuntil_null)
Definition: passwordcheck.c:56
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, char **logdetail)
Definition: crypt.c:222
int errcode(int sqlerrcode)
Definition: elog.c:691
void(* check_password_hook_type)(const char *username, const char *shadow_pass, PasswordType password_type, Datum validuntil_time, bool validuntil_null)
Definition: user.h:23
#define ERROR
Definition: elog.h:43
int errdetail_log(const char *fmt,...)
Definition: elog.c:1083
static char * password
Definition: streamutil.c:53
#define STATUS_OK
Definition: c.h:1166
uintptr_t Datum
Definition: postgres.h:367
PasswordType
Definition: crypt.h:27
static char * username
Definition: initdb.c:134
#define ereport(elevel,...)
Definition: elog.h:155
void _PG_init(void)
int errmsg(const char *fmt,...)
Definition: elog.c:902
int i
void _PG_fini(void)
check_password_hook_type check_password_hook
Definition: user.c:49
#define MIN_PWD_LENGTH
Definition: passwordcheck.c:33