3030
3131
3232/*
33- * Check given password for given user, and return STATUS_OK or STATUS_ERROR .
33+ * Fetch stored password for a user, for authentication .
3434 *
35- * 'client_pass' is the password response given by the remote user. If
36- * 'md5_salt' is not NULL, it is a response to an MD5 authentication
37- * challenge, with the given salt. Otherwise, it is a plaintext password.
35+ * Returns STATUS_OK on success. On error, returns STATUS_ERROR, and stores
36+ * a palloc'd string describing the reason, for the postmaster log, in
37+ * *logdetail. The error reason should *not* be sent to the client, to avoid
38+ * giving away user information!
3839 *
39- * In the error case, optionally store a palloc'd string at *logdetail
40- * that will be sent to the postmaster log (but not the client).
40+ * If the password is expired, it is still returned in *shadow_pass, but the
41+ * return code is STATUS_ERROR. On other errors, *shadow_pass is set to
42+ * NULL.
4143 */
4244int
43- md5_crypt_verify (const char * role , char * client_pass ,
44- char * md5_salt , int md5_salt_len , char * * logdetail )
45+ get_role_password (const char * role , char * * shadow_pass , char * * logdetail )
4546{
4647 int retval = STATUS_ERROR ;
47- char * shadow_pass ,
48- * crypt_pwd ;
4948 TimestampTz vuntil = 0 ;
50- char * crypt_client_pass = client_pass ;
5149 HeapTuple roleTup ;
5250 Datum datum ;
5351 bool isnull ;
5452
53+ * shadow_pass = NULL ;
54+
5555 /* Get role info from pg_authid */
5656 roleTup = SearchSysCache1 (AUTHNAME , PointerGetDatum (role ));
5757 if (!HeapTupleIsValid (roleTup ))
@@ -70,7 +70,7 @@ md5_crypt_verify(const char *role, char *client_pass,
7070 role );
7171 return STATUS_ERROR ; /* user has no password */
7272 }
73- shadow_pass = TextDatumGetCString (datum );
73+ * shadow_pass = TextDatumGetCString (datum );
7474
7575 datum = SysCacheGetAttr (AUTHNAME , roleTup ,
7676 Anum_pg_authid_rolvaliduntil , & isnull );
@@ -79,104 +79,151 @@ md5_crypt_verify(const char *role, char *client_pass,
7979
8080 ReleaseSysCache (roleTup );
8181
82- if (* shadow_pass == '\0' )
82+ if (* * shadow_pass == '\0' )
8383 {
8484 * logdetail = psprintf (_ ("User \"%s\" has an empty password." ),
8585 role );
86+ pfree (* shadow_pass );
87+ * shadow_pass = NULL ;
8688 return STATUS_ERROR ; /* empty password */
8789 }
8890
8991 /*
90- * Compare with the encrypted or plain password depending on the
91- * authentication method being used for this connection. (We do not
92- * bother setting logdetail for pg_md5_encrypt failure: the only possible
93- * error is out-of-memory, which is unlikely, and if it did happen adding
94- * a psprintf call would only make things worse.)
92+ * Password OK, now check to be sure we are not past rolvaliduntil
9593 */
96- if (md5_salt )
94+ if (isnull )
95+ retval = STATUS_OK ;
96+ else if (vuntil < GetCurrentTimestamp ())
9797 {
98- /* MD5 authentication */
99- Assert (md5_salt_len > 0 );
100- crypt_pwd = palloc (MD5_PASSWD_LEN + 1 );
101- if (isMD5 (shadow_pass ))
102- {
103- /* stored password already encrypted, only do salt */
104- if (!pg_md5_encrypt (shadow_pass + strlen ("md5" ),
105- md5_salt , md5_salt_len ,
106- crypt_pwd ))
107- {
108- pfree (crypt_pwd );
109- return STATUS_ERROR ;
110- }
111- }
112- else
98+ * logdetail = psprintf (_ ("User \"%s\" has an expired password." ),
99+ role );
100+ retval = STATUS_ERROR ;
101+ }
102+ else
103+ retval = STATUS_OK ;
104+
105+ return retval ;
106+ }
107+
108+ /*
109+ * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
110+ *
111+ * 'shadow_pass' is the user's correct password or password hash, as stored
112+ * in pg_authid.rolpassword.
113+ * 'client_pass' is the response given by the remote user to the MD5 challenge.
114+ * 'md5_salt' is the salt used in the MD5 authentication challenge.
115+ *
116+ * In the error case, optionally store a palloc'd string at *logdetail
117+ * that will be sent to the postmaster log (but not the client).
118+ */
119+ int
120+ md5_crypt_verify (const char * role , const char * shadow_pass ,
121+ const char * client_pass ,
122+ const char * md5_salt , int md5_salt_len ,
123+ char * * logdetail )
124+ {
125+ int retval ;
126+ char crypt_pwd [MD5_PASSWD_LEN + 1 ];
127+ char crypt_pwd2 [MD5_PASSWD_LEN + 1 ];
128+
129+ Assert (md5_salt_len > 0 );
130+
131+ /*
132+ * Compute the correct answer for the MD5 challenge.
133+ *
134+ * We do not bother setting logdetail for any pg_md5_encrypt failure
135+ * below: the only possible error is out-of-memory, which is unlikely, and
136+ * if it did happen adding a psprintf call would only make things worse.
137+ */
138+ if (isMD5 (shadow_pass ))
139+ {
140+ /* stored password already encrypted, only do salt */
141+ if (!pg_md5_encrypt (shadow_pass + strlen ("md5" ),
142+ md5_salt , md5_salt_len ,
143+ crypt_pwd ))
113144 {
114- /* stored password is plain, double-encrypt */
115- char * crypt_pwd2 = palloc (MD5_PASSWD_LEN + 1 );
116-
117- if (!pg_md5_encrypt (shadow_pass ,
118- role ,
119- strlen (role ),
120- crypt_pwd2 ))
121- {
122- pfree (crypt_pwd );
123- pfree (crypt_pwd2 );
124- return STATUS_ERROR ;
125- }
126- if (!pg_md5_encrypt (crypt_pwd2 + strlen ("md5" ),
127- md5_salt , md5_salt_len ,
128- crypt_pwd ))
129- {
130- pfree (crypt_pwd );
131- pfree (crypt_pwd2 );
132- return STATUS_ERROR ;
133- }
134- pfree (crypt_pwd2 );
145+ return STATUS_ERROR ;
135146 }
136147 }
137148 else
138149 {
139- /* Client sent password in plaintext */
140- if (isMD5 (shadow_pass ))
150+ /* stored password is plain, double-encrypt */
151+ if (!pg_md5_encrypt (shadow_pass ,
152+ role ,
153+ strlen (role ),
154+ crypt_pwd2 ))
141155 {
142- /* Encrypt user-supplied password to match stored MD5 */
143- crypt_client_pass = palloc (MD5_PASSWD_LEN + 1 );
144- if (!pg_md5_encrypt (client_pass ,
145- role ,
146- strlen (role ),
147- crypt_client_pass ))
148- {
149- pfree (crypt_client_pass );
150- return STATUS_ERROR ;
151- }
156+ return STATUS_ERROR ;
157+ }
158+ if (!pg_md5_encrypt (crypt_pwd2 + strlen ("md5" ),
159+ md5_salt , md5_salt_len ,
160+ crypt_pwd ))
161+ {
162+ return STATUS_ERROR ;
152163 }
153- crypt_pwd = shadow_pass ;
154164 }
155165
156- if (strcmp (crypt_client_pass , crypt_pwd ) == 0 )
166+ if (strcmp (client_pass , crypt_pwd ) == 0 )
167+ retval = STATUS_OK ;
168+ else
157169 {
158- /*
159- * Password OK, now check to be sure we are not past rolvaliduntil
160- */
161- if (isnull )
162- retval = STATUS_OK ;
163- else if (vuntil < GetCurrentTimestamp ())
170+ * logdetail = psprintf (_ ("Password does not match for user \"%s\"." ),
171+ role );
172+ retval = STATUS_ERROR ;
173+ }
174+
175+ return retval ;
176+ }
177+
178+ /*
179+ * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
180+ *
181+ * 'shadow_pass' is the user's correct password or password hash, as stored
182+ * in pg_authid.rolpassword.
183+ * 'client_pass' is the password given by the remote user.
184+ *
185+ * In the error case, optionally store a palloc'd string at *logdetail
186+ * that will be sent to the postmaster log (but not the client).
187+ */
188+ int
189+ plain_crypt_verify (const char * role , const char * shadow_pass ,
190+ const char * client_pass ,
191+ char * * logdetail )
192+ {
193+ int retval ;
194+ char crypt_client_pass [MD5_PASSWD_LEN + 1 ];
195+
196+ /*
197+ * Client sent password in plaintext. If we have an MD5 hash stored, hash
198+ * the password the client sent, and compare the hashes. Otherwise
199+ * compare the plaintext passwords directly.
200+ */
201+ if (isMD5 (shadow_pass ))
202+ {
203+ if (!pg_md5_encrypt (client_pass ,
204+ role ,
205+ strlen (role ),
206+ crypt_client_pass ))
164207 {
165- * logdetail = psprintf (_ ("User \"%s\" has an expired password." ),
166- role );
167- retval = STATUS_ERROR ;
208+ /*
209+ * We do not bother setting logdetail for pg_md5_encrypt failure:
210+ * the only possible error is out-of-memory, which is unlikely,
211+ * and if it did happen adding a psprintf call would only make
212+ * things worse.
213+ */
214+ return STATUS_ERROR ;
168215 }
169- else
170- retval = STATUS_OK ;
216+ client_pass = crypt_client_pass ;
171217 }
218+
219+ if (strcmp (client_pass , shadow_pass ) == 0 )
220+ retval = STATUS_OK ;
172221 else
222+ {
173223 * logdetail = psprintf (_ ("Password does not match for user \"%s\"." ),
174224 role );
175-
176- if (crypt_pwd != shadow_pass )
177- pfree (crypt_pwd );
178- if (crypt_client_pass != client_pass )
179- pfree (crypt_client_pass );
225+ retval = STATUS_ERROR ;
226+ }
180227
181228 return retval ;
182229}
0 commit comments