diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 9a8fb74a03d2b..7cc91748a5137 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -240,7 +240,12 @@ public function checkPassword($loginName, $password) { * @return IUser|false the User object on success, false otherwise */ public function checkPasswordNoLogging($loginName, $password) { - $loginName = str_replace("\0", '', $loginName); + // A NUL byte in the login name must not turn it into a second name for an + // account: the checks done before the password check (e.g. 2FA enforcement + // in Session::logClientIn()) see the name as sent, so the name has to stay the same + if (str_contains($loginName, "\0")) { + return false; + } $password = str_replace("\0", '', $password); $cachedBackend = $this->cache->get($loginName); diff --git a/tests/lib/User/ManagerTest.php b/tests/lib/User/ManagerTest.php index b1585dd3259f7..688169787cb16 100644 --- a/tests/lib/User/ManagerTest.php +++ b/tests/lib/User/ManagerTest.php @@ -63,7 +63,6 @@ public function testGetBackends(): void { $this->assertEquals([$userDummyBackend, $dummyDatabaseBackend], $manager->getBackends()); } - public function testUserExistsSingleBackendExists(): void { $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -186,6 +185,27 @@ public function testCheckPasswordNotSupported(): void { $this->assertFalse($manager->checkPassword('foo', 'bar')); } + public function testCheckPasswordNullByteInLoginName(): void { + $backend = $this->createMock(\Test\Util\User\Dummy::class); + $backend->expects($this->never()) + ->method('checkPassword'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->willReturnCallback(function ($actions) { + if ($actions === BACKEND::CHECK_PASSWORD) { + return true; + } else { + return false; + } + }); + + $manager = new Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); + $manager->registerBackend($backend); + + $this->assertFalse($manager->checkPassword("foo\0", 'bar')); + } + public function testGetOneBackendExists(): void { $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -330,7 +350,6 @@ public function testCreateUserInvalid($uid, $password, $exception): void { ->with(\OC\User\Backend::CREATE_USER) ->willReturn(true); - $manager = new Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); @@ -362,7 +381,6 @@ public function testCreateUserSingleBackendNotExists(): void { $this->assertEquals('foo', $user->getUID()); } - public function testCreateUserSingleBackendExists(): void { $this->expectException(\Exception::class); @@ -409,7 +427,6 @@ public function testCreateUserNoBackends(): void { $this->assertFalse($manager->createUser('foo', 'bar')); } - public function testCreateUserFromBackendWithBackendError(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Could not create account'); @@ -426,7 +443,6 @@ public function testCreateUserFromBackendWithBackendError(): void { $manager->createUserFromBackend('MyUid', 'MyPassword', $backend); } - public function testCreateUserTwoBackendExists(): void { $this->expectException(\Exception::class);