Skip to content

Commit 74c3e6d

Browse files
authored
Merge pull request #31777 from nextcloud/backport/31514/stable22
[stable22] user_ldap fix ldap connection resets #31421
2 parents 82891bc + 6b7f95b commit 74c3e6d

11 files changed

Lines changed: 128 additions & 147 deletions

File tree

apps/user_ldap/lib/Access.php

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
218218
$values = [];
219219
$isRangeRequest = false;
220220
do {
221-
$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
221+
$result = $this->executeRead($dn, $attrToRead, $filter, $maxResults);
222222
if (is_bool($result)) {
223223
// when an exists request was run and it was successful, an empty
224224
// array must be returned
@@ -260,17 +260,12 @@ public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
260260
/**
261261
* Runs an read operation against LDAP
262262
*
263-
* @param resource $cr the LDAP connection
264-
* @param string $dn
265-
* @param string $attribute
266-
* @param string $filter
267-
* @param int $maxResults
268263
* @return array|bool false if there was any error, true if an exists check
269264
* was performed and the requested DN found, array with the
270265
* returned data on a successful usual operation
271266
* @throws ServerNotAvailableException
272267
*/
273-
public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
268+
public function executeRead(string $dn, string $attribute, string $filter, int $maxResults) {
274269
try {
275270
$this->initPagedSearch($filter, $dn, [$attribute], $maxResults, 0);
276271
} catch (NoMoreResults $e) {
@@ -280,7 +275,7 @@ public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
280275
return false;
281276
}
282277
$dn = $this->helper->DNasBaseParameter($dn);
283-
$rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, [$attribute]);
278+
$rr = @$this->invokeLDAPMethod('read', $dn, $filter, [$attribute]);
284279
if (!$this->ldap->isResource($rr)) {
285280
if ($attribute !== '') {
286281
//do not throw this message on userExists check, irritates
@@ -289,18 +284,18 @@ public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
289284
//in case an error occurs , e.g. object does not exist
290285
return false;
291286
}
292-
if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
287+
if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $rr) === 1)) {
293288
$this->logger->debug('readAttribute: ' . $dn . ' found', ['app' => 'user_ldap']);
294289
return true;
295290
}
296-
$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
291+
$er = $this->invokeLDAPMethod('firstEntry', $rr);
297292
if (!$this->ldap->isResource($er)) {
298293
//did not match the filter, return false
299294
return false;
300295
}
301296
//LDAP attributes are not case sensitive
302297
$result = \OCP\Util::mb_array_change_key_case(
303-
$this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
298+
$this->invokeLDAPMethod('getAttributes', $er), MB_CASE_LOWER, 'UTF-8');
304299

305300
return $result;
306301
}
@@ -381,10 +376,10 @@ public function setPassword($userDN, $password) {
381376
}
382377
try {
383378
// try PASSWD extended operation first
384-
return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
385-
@$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
379+
return @$this->invokeLDAPMethod('exopPasswd', $userDN, '', $password) ||
380+
@$this->invokeLDAPMethod('modReplace', $userDN, $password);
386381
} catch (ConstraintViolationException $e) {
387-
throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ') . $e->getMessage(), $e->getCode());
382+
throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ') . $e->getMessage(), (int)$e->getCode());
388383
}
389384
}
390385

@@ -1092,26 +1087,23 @@ public function countObjects($limit = null, $offset = null) {
10921087
*/
10931088

10941089
/**
1090+
* @param mixed[] $arguments
10951091
* @return mixed
10961092
* @throws \OC\ServerNotAvailableException
10971093
*/
1098-
private function invokeLDAPMethod() {
1099-
$arguments = func_get_args();
1100-
$command = array_shift($arguments);
1101-
$cr = array_shift($arguments);
1094+
private function invokeLDAPMethod(string $command, ...$arguments) {
1095+
if ($command == 'controlPagedResultResponse') {
1096+
// php no longer supports call-time pass-by-reference
1097+
// thus cannot support controlPagedResultResponse as the third argument
1098+
// is a reference
1099+
throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1100+
}
11021101
if (!method_exists($this->ldap, $command)) {
11031102
return null;
11041103
}
1105-
array_unshift($arguments, $cr);
1106-
// php no longer supports call-time pass-by-reference
1107-
// thus cannot support controlPagedResultResponse as the third argument
1108-
// is a reference
1104+
array_unshift($arguments, $this->connection->getConnectionResource());
11091105
$doMethod = function () use ($command, &$arguments) {
1110-
if ($command == 'controlPagedResultResponse') {
1111-
throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1112-
} else {
1113-
return call_user_func_array([$this->ldap, $command], $arguments);
1114-
}
1106+
return call_user_func_array([$this->ldap, $command], $arguments);
11151107
};
11161108
try {
11171109
$ret = $doMethod();
@@ -1172,8 +1164,7 @@ private function executeSearch(
11721164
return false;
11731165
}
11741166

1175-
$sr = $this->invokeLDAPMethod('search', $cr, $base, $filter, $attr);
1176-
// cannot use $cr anymore, might have changed in the previous call!
1167+
$sr = $this->invokeLDAPMethod('search', $base, $filter, $attr);
11771168
$error = $this->ldap->errno($this->connection->getConnectionResource());
11781169
if (!$this->ldap->isResource($sr) || $error !== 0) {
11791170
$this->logger->error('Attempt for Paging? ' . print_r($pagedSearchOK, true), ['app' => 'user_ldap']);
@@ -1308,7 +1299,7 @@ private function count(
13081299
* @throws ServerNotAvailableException
13091300
*/
13101301
private function countEntriesInSearchResults($sr): int {
1311-
return (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $sr);
1302+
return (int)$this->invokeLDAPMethod('countEntries', $sr);
13121303
}
13131304

13141305
/**
@@ -1349,7 +1340,6 @@ public function search(
13491340
return [];
13501341
}
13511342
[$sr, $pagedSearchOK] = $search;
1352-
$cr = $this->connection->getConnectionResource();
13531343

13541344
if ($skipHandling) {
13551345
//i.e. result do not need to be fetched, we just need the cookie
@@ -1359,7 +1349,7 @@ public function search(
13591349
return [];
13601350
}
13611351

1362-
$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $sr));
1352+
$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $sr));
13631353
$iFoundItems = max($iFoundItems, $findings['count']);
13641354
unset($findings['count']);
13651355

@@ -1536,7 +1526,7 @@ private function combineFilter($filters, $operator) {
15361526
* @param string $search the search term
15371527
* @return string the final filter part to use in LDAP searches
15381528
*/
1539-
public function getFilterPartForUserSearch($search) {
1529+
public function getFilterPartForUserSearch($search): string {
15401530
return $this->getFilterPartForSearch($search,
15411531
$this->connection->ldapAttributesForUserSearch,
15421532
$this->connection->ldapUserDisplayName);
@@ -1548,7 +1538,7 @@ public function getFilterPartForUserSearch($search) {
15481538
* @param string $search the search term
15491539
* @return string the final filter part to use in LDAP searches
15501540
*/
1551-
public function getFilterPartForGroupSearch($search) {
1541+
public function getFilterPartForGroupSearch($search): string {
15521542
return $this->getFilterPartForSearch($search,
15531543
$this->connection->ldapAttributesForGroupSearch,
15541544
$this->connection->ldapGroupDisplayName);
@@ -1642,10 +1632,8 @@ private function prepareSearchTerm($term) {
16421632

16431633
/**
16441634
* returns the filter used for counting users
1645-
*
1646-
* @return string
16471635
*/
1648-
public function getFilterForUserCount() {
1636+
public function getFilterForUserCount(): string {
16491637
$filter = $this->combineFilterWithAnd([
16501638
$this->connection->ldapUserFilter,
16511639
$this->connection->ldapUserDisplayName . '=*'
@@ -1994,8 +1982,7 @@ private function abandonPagedSearch() {
19941982
if ($this->lastCookie === '') {
19951983
return;
19961984
}
1997-
$cr = $this->connection->getConnectionResource();
1998-
$this->invokeLDAPMethod('controlPagedResult', $cr, 0, false);
1985+
$this->invokeLDAPMethod('controlPagedResult', 0, false);
19991986
$this->getPagedSearchResultState();
20001987
$this->lastCookie = '';
20011988
}
@@ -2082,7 +2069,7 @@ private function initPagedSearch(
20822069
$this->abandonPagedSearch();
20832070
}
20842071
$pagedSearchOK = true === $this->invokeLDAPMethod(
2085-
'controlPagedResult', $this->connection->getConnectionResource(), $limit, false
2072+
'controlPagedResult', $limit, false
20862073
);
20872074
if ($pagedSearchOK) {
20882075
$this->logger->debug('Ready for a paged search', ['app' => 'user_ldap']);
@@ -2102,7 +2089,6 @@ private function initPagedSearch(
21022089
// be returned.
21032090
$pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
21042091
$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2105-
$this->connection->getConnectionResource(),
21062092
$pageSize, false);
21072093
}
21082094

apps/user_ldap/lib/Connection.php

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,25 @@
7575
*/
7676
class Connection extends LDAPUtility {
7777
private $ldapConnectionRes = null;
78+
79+
/**
80+
* @var string
81+
*/
7882
private $configPrefix;
83+
84+
/**
85+
* @var ?string
86+
*/
7987
private $configID;
88+
89+
/**
90+
* @var bool
91+
*/
8092
private $configured = false;
81-
//whether connection should be kept on __destruct
93+
94+
/**
95+
* @var bool whether connection should be kept on __destruct
96+
*/
8297
private $dontDestruct = false;
8398

8499
/**
@@ -91,33 +106,42 @@ class Connection extends LDAPUtility {
91106
*/
92107
public $hasGidNumber = true;
93108

94-
//cache handler
95-
protected $cache;
109+
/**
110+
* @var \OCP\ICache|null
111+
*/
112+
protected $cache = null;
96113

97114
/** @var Configuration settings handler **/
98115
protected $configuration;
99116

117+
/**
118+
* @var bool
119+
*/
100120
protected $doNotValidate = false;
101121

122+
/**
123+
* @var bool
124+
*/
102125
protected $ignoreValidation = false;
103126

127+
/**
128+
* @var array{dn?: mixed, hash?: string, result?: bool}
129+
*/
104130
protected $bindResult = [];
105131

106132
/** @var LoggerInterface */
107133
protected $logger;
108134

109135
/**
110136
* Constructor
111-
* @param ILDAPWrapper $ldap
112137
* @param string $configPrefix a string with the prefix for the configkey column (appconfig table)
113138
* @param string|null $configID a string with the value for the appid column (appconfig table) or null for on-the-fly connections
114139
*/
115-
public function __construct(ILDAPWrapper $ldap, $configPrefix = '', $configID = 'user_ldap') {
140+
public function __construct(ILDAPWrapper $ldap, string $configPrefix = '', ?string $configID = 'user_ldap') {
116141
parent::__construct($ldap);
117142
$this->configPrefix = $configPrefix;
118143
$this->configID = $configID;
119-
$this->configuration = new Configuration($configPrefix,
120-
!is_null($configID));
144+
$this->configuration = new Configuration($configPrefix, !is_null($configID));
121145
$memcache = \OC::$server->getMemCacheFactory();
122146
if ($memcache->isAvailable()) {
123147
$this->cache = $memcache->createDistributed();

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,7 +1349,7 @@ public function getDisplayName(string $gid): string {
13491349
$this->access->groupname2dn($gid),
13501350
$this->access->connection->ldapGroupDisplayName);
13511351

1352-
if ($displayName && (count($displayName) > 0)) {
1352+
if (($displayName !== false) && (count($displayName) > 0)) {
13531353
$displayName = $displayName[0];
13541354
$this->access->connection->writeToCache($cacheKey, $displayName);
13551355
return $displayName;

apps/user_ldap/lib/ILDAPWrapper.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function nextEntry($link, $result);
147147
/**
148148
* Read an entry
149149
* @param resource $link LDAP link resource
150-
* @param array $baseDN The DN of the entry to read from
150+
* @param string $baseDN The DN of the entry to read from
151151
* @param string $filter An LDAP filter
152152
* @param array $attr array of the attributes to read
153153
* @return resource an LDAP search result resource
@@ -178,8 +178,8 @@ public function modReplace($link, $userDN, $password);
178178
/**
179179
* Sets the value of the specified option to be $value
180180
* @param resource $link LDAP link resource
181-
* @param string $option a defined LDAP Server option
182-
* @param int $value the new value for the option
181+
* @param int $option a defined LDAP Server option
182+
* @param mixed $value the new value for the option
183183
* @return bool true on success, false otherwise
184184
*/
185185
public function setOption($link, $option, $value);

0 commit comments

Comments
 (0)