@@ -396,6 +396,89 @@ protected function executeQuery(PDO $db, string $query, array $params): array
396396 }
397397
398398
399+ /**
400+ * Authenticate using the optional password_verify() support against a hash retrieved from the database.
401+ *
402+ * @param string $queryname Name of the auth query being processed
403+ * @param array $queryConfig Configuration from authsources.php for this auth query
404+ * @param array $data Result data from the database query
405+ * @param string $password Password to verify with password_verify()
406+ * @return bool True if password_verify() password verification succeeded, false otherwise
407+ */
408+ protected function authenticatePasswordVerifyHash (
409+ string $ queryname ,
410+ array $ queryConfig ,
411+ array $ data ,
412+ string $ password ,
413+ ): bool {
414+ // If password_verify_hash_column is not set, we are not using password_verify()
415+ if (!array_key_exists ('password_verify_hash_column ' , $ queryConfig )) {
416+ Logger::error (sprintf (
417+ 'sqlauth:%s: authenticatePasswordVerifyHash() called but configuration for ' .
418+ '"password_verify_hash_column" not found in query config for query %s. ' ,
419+ $ this ->authId ,
420+ $ queryname ,
421+ ));
422+ throw new Error \Error ('WRONGUSERPASS ' );
423+ } elseif (count ($ data ) < 1 ) {
424+ // No rows returned, password_verify() cannot succeed
425+ return false ;
426+ }
427+
428+ /* This is where we need to run password_verify() if we are using password_verify() to
429+ * authenticate hashed passwords that are only stored in the database. */
430+ $ hashColumn = $ queryConfig ['password_verify_hash_column ' ];
431+ if (!array_key_exists ($ hashColumn , $ data [0 ])) {
432+ Logger::error ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
433+ ' did not return expected hash column \'' . $ hashColumn . '\'' );
434+ throw new Error \Error ('WRONGUSERPASS ' );
435+ }
436+
437+ $ validPasswordHashFound = false ;
438+ $ passwordHash = null ;
439+ foreach ($ data as $ row ) {
440+ if ((!array_key_exists ($ hashColumn , $ row )) || is_null ($ row [$ hashColumn ])) {
441+ Logger::error (sprintf (
442+ 'sqlauth:%s: column `%s` must be in every result tuple. ' ,
443+ $ this ->authId ,
444+ $ hashColumn ,
445+ ));
446+ throw new Error \Error ('WRONGUSERPASS ' );
447+ }
448+ if (($ passwordHash === null ) && (strlen ($ row [$ hashColumn ]) > 0 )) {
449+ $ passwordHash = $ row [$ hashColumn ];
450+ $ validPasswordHashFound = true ;
451+ } elseif ($ passwordHash != $ row [$ hashColumn ]) {
452+ Logger::error (sprintf (
453+ 'sqlauth:%s: column %s must be THE SAME in every result tuple. ' ,
454+ $ this ->authId ,
455+ $ hashColumn ,
456+ ));
457+ throw new Error \Error ('WRONGUSERPASS ' );
458+ } elseif (strlen ($ row [$ hashColumn ]) === 0 ) {
459+ Logger::error (sprintf (
460+ 'sqlauth:%s: column `%s` must contain a valid password hash. ' ,
461+ $ this ->authId ,
462+ $ hashColumn ,
463+ ));
464+ throw new Error \Error ('WRONGUSERPASS ' );
465+ }
466+ }
467+
468+ if ((!$ validPasswordHashFound ) || (!password_verify ($ password , $ passwordHash ))) {
469+ Logger::error ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
470+ ' password verification failed ' );
471+ /* Authentication with verify_password() failed, however that only means that
472+ * this auth query did not succeed. We should try the next auth query if any. */
473+ return false ;
474+ }
475+
476+ Logger::debug ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
477+ ' password verification using password_verify() succeeded ' );
478+ return true ;
479+ }
480+
481+
399482 /**
400483 * Attempt to log in using the given username and password.
401484 *
@@ -449,60 +532,11 @@ protected function login(
449532 }
450533
451534 // If we got any rows, the authentication succeeded. If not, try the next query.
452- if (count ($ data ) > 0 ) {
453- /* This is where we need to run password_verify() if we are using password_verify() to
454- * authenticate hashed passwords that are only stored in the database. */
455- if (array_key_exists ('password_verify_hash_column ' , $ queryConfig )) {
456- $ hashColumn = $ queryConfig ['password_verify_hash_column ' ];
457- if (!array_key_exists ($ hashColumn , $ data [0 ])) {
458- Logger::error ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
459- ' did not return expected hash column \'' . $ hashColumn . '\'' );
460- throw new Error \Error ('WRONGUSERPASS ' );
461- }
462-
463- $ validPasswordHashFound = false ;
464- $ passwordHash = null ;
465- foreach ($ data as $ row ) {
466- if ((!array_key_exists ($ hashColumn , $ row )) || is_null ($ row [$ hashColumn ])) {
467- Logger::error (sprintf (
468- 'sqlauth:%s: column `%s` must be in every result tuple. ' ,
469- $ this ->authId ,
470- $ hashColumn ,
471- ));
472- throw new Error \Error ('WRONGUSERPASS ' );
473- }
474- if (($ passwordHash === null ) && (strlen ($ row [$ hashColumn ]) > 0 )) {
475- $ passwordHash = $ row [$ hashColumn ];
476- $ validPasswordHashFound = true ;
477- } elseif ($ passwordHash != $ row [$ hashColumn ]) {
478- Logger::error (sprintf (
479- 'sqlauth:%s: column %s must be THE SAME in every result tuple. ' ,
480- $ this ->authId ,
481- $ hashColumn ,
482- ));
483- throw new Error \Error ('WRONGUSERPASS ' );
484- } elseif (strlen ($ row [$ hashColumn ]) === 0 ) {
485- Logger::error (sprintf (
486- 'sqlauth:%s: column `%s` must contain a valid password hash. ' ,
487- $ this ->authId ,
488- $ hashColumn ,
489- ));
490- throw new Error \Error ('WRONGUSERPASS ' );
491- }
492- }
493-
494- if ((!$ validPasswordHashFound ) || (!password_verify ($ password , $ passwordHash ))) {
495- Logger::error ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
496- ' password verification failed ' );
497- /* Authentication with verify_password() failed, however that only means that
498- * this auth query did not succeed. We should try the next auth query if any. */
499- continue ;
500- }
501-
502- Logger::debug ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
503- ' password verification using password_verify() succeeded ' );
504- }
505-
535+ if (
536+ (count ($ data ) > 0 ) &&
537+ ((array_key_exists ('password_verify_hash_column ' , $ queryConfig ) === false ) ||
538+ $ this ->authenticatePasswordVerifyHash ($ queryname , $ queryConfig , $ data , $ password ))
539+ ) {
506540 Logger::debug ('sqlauth: ' . $ this ->authId . ': Auth query ' . $ queryname .
507541 ' succeeded with ' . count ($ data ) . ' rows ' );
508542 $ queryConfig ['_winning_auth_query ' ] = true ;
0 commit comments