PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
passwordcheck.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * passwordcheck.c
4 *
5 *
6 * Copyright (c) 2009-2024, 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 */
31
32/* passwords shorter than this will be rejected */
33#define MIN_PWD_LENGTH 8
34
35/*
36 * check_password
37 *
38 * performs checks on an encrypted or unencrypted password
39 * ereport's if not acceptable
40 *
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
47 *
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.
51 */
52static void
54 const char *shadow_pass,
55 PasswordType password_type,
56 Datum validuntil_time,
57 bool validuntil_null)
58{
61 password_type, validuntil_time,
62 validuntil_null);
63
64 if (password_type != PASSWORD_TYPE_PLAINTEXT)
65 {
66 /*
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.)
71 *
72 * We only check for username = password.
73 */
74 const char *logdetail = NULL;
75
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")));
80 }
81 else
82 {
83 /*
84 * For unencrypted passwords we can perform better checks
85 */
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
94
95 /* enforce minimum length */
96 if (pwdlen < MIN_PWD_LENGTH)
98 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
99 errmsg("password is too short")));
100
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")));
106
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++)
111 {
112 /*
113 * isalpha() does not work for multibyte encodings but let's
114 * consider non-ASCII characters non-letters
115 */
116 if (isalpha((unsigned char) password[i]))
117 pwd_has_letter = true;
118 else
119 pwd_has_nonletter = true;
120 }
121 if (!pwd_has_letter || !pwd_has_nonletter)
123 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124 errmsg("password must contain both letters and nonletters")));
125
126#ifdef USE_CRACKLIB
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)));
133#endif
134 }
135
136 /* all checks passed, password is ok */
137}
138
139/*
140 * Module initialization function
141 */
142void
144{
145 /* activate password checks when the module is loaded */
148}
#define STATUS_OK
Definition: c.h:1123
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const char **logdetail)
Definition: crypt.c:256
PasswordType
Definition: crypt.h:41
@ PASSWORD_TYPE_PLAINTEXT
Definition: crypt.h:42
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
int errdetail_log(const char *fmt,...)
Definition: elog.c:1251
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
static char * username
Definition: initdb.c:153
int i
Definition: isn.c:72
static void check_password(const char *username, const char *shadow_pass, PasswordType password_type, Datum validuntil_time, bool validuntil_null)
Definition: passwordcheck.c:53
#define MIN_PWD_LENGTH
Definition: passwordcheck.c:33
void _PG_init(void)
PG_MODULE_MAGIC
Definition: passwordcheck.c:27
static check_password_hook_type prev_check_password_hook
Definition: passwordcheck.c:30
uintptr_t Datum
Definition: postgres.h:64
static char * password
Definition: streamutil.c:52
check_password_hook_type check_password_hook
Definition: user.c:91
void(* check_password_hook_type)(const char *username, const char *shadow_pass, PasswordType password_type, Datum validuntil_time, bool validuntil_null)
Definition: user.h:25