This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * Filter to add attributes to the identity by executing a query against an LDAP directory |
||||
5 | * |
||||
6 | * @package simplesamlphp/simplesamlphp-module-ldap |
||||
7 | */ |
||||
8 | |||||
9 | declare(strict_types=1); |
||||
10 | |||||
11 | namespace SimpleSAML\Module\ldap\Auth\Process; |
||||
12 | |||||
13 | use SimpleSAML\Assert\Assert; |
||||
14 | use SimpleSAML\Logger; |
||||
15 | use Symfony\Component\Ldap\Adapter\ExtLdap\Query; |
||||
16 | |||||
17 | use function array_values; |
||||
18 | |||||
19 | class AttributeAddFromLDAP extends BaseFilter |
||||
20 | { |
||||
21 | /** |
||||
22 | * LDAP attributes to add to the request attributes |
||||
23 | * |
||||
24 | * @var string[] |
||||
25 | */ |
||||
26 | protected array $searchAttributes; |
||||
27 | |||||
28 | /** |
||||
29 | * LDAP attributes to base64 encode |
||||
30 | * |
||||
31 | * @var string[] |
||||
32 | */ |
||||
33 | protected array $binaryAttributes; |
||||
34 | |||||
35 | /** |
||||
36 | * LDAP search filter to use in the LDAP query |
||||
37 | * |
||||
38 | * @var string |
||||
39 | */ |
||||
40 | protected string $searchFilter; |
||||
41 | |||||
42 | /** |
||||
43 | * What to do with attributes when the target already exists. Either replace, merge or add. |
||||
44 | * |
||||
45 | * @var string |
||||
46 | */ |
||||
47 | protected string $attrPolicy; |
||||
48 | |||||
49 | /** @var string|null */ |
||||
50 | protected ?string $searchUsername; |
||||
51 | |||||
52 | /** @var string|null */ |
||||
53 | protected ?string $searchPassword; |
||||
54 | |||||
55 | /** |
||||
56 | * Initialize this filter. |
||||
57 | * |
||||
58 | * @param array<mixed> $config Configuration information about this filter. |
||||
59 | * @param mixed $reserved For future use. |
||||
60 | */ |
||||
61 | public function __construct(array $config, $reserved) |
||||
62 | { |
||||
63 | parent::__construct($config, $reserved); |
||||
64 | |||||
65 | // Get filter specific config options |
||||
66 | $this->binaryAttributes = $this->config->getOptionalArray('attributes.binary', []); |
||||
67 | $this->searchAttributes = $this->config->getOptionalArrayize('attributes', []); |
||||
68 | if (empty($this->searchAttributes)) { |
||||
69 | $new_attribute = $this->config->getString('attribute.new'); |
||||
70 | $this->searchAttributes[$new_attribute] = $this->config->getString('search.attribute'); |
||||
71 | } |
||||
72 | $this->searchFilter = $this->config->getString('search.filter'); |
||||
73 | |||||
74 | // get the attribute policy |
||||
75 | $this->attrPolicy = $this->config->getOptionalString('attribute.policy', 'merge'); |
||||
76 | Assert::oneOf($this->attrPolicy, ['merge', 'replace', 'add']); |
||||
77 | |||||
78 | $this->searchUsername = $this->config->getOptionalString('search.username', null); |
||||
79 | $this->searchPassword = $this->config->getOptionalString('search.password', null); |
||||
80 | } |
||||
81 | |||||
82 | |||||
83 | /** |
||||
84 | * Add attributes from an LDAP server. |
||||
85 | * |
||||
86 | * @param array<mixed> &$state The current request |
||||
87 | */ |
||||
88 | public function process(array &$state): void |
||||
89 | { |
||||
90 | Assert::keyExists($state, 'Attributes'); |
||||
91 | $attributes = &$state['Attributes']; |
||||
92 | |||||
93 | // perform a merge on the ldap_search_filter |
||||
94 | // loop over the attributes and build the search and replace arrays |
||||
95 | $arrSearch = $arrReplace = []; |
||||
96 | foreach ($attributes as $attr => $val) { |
||||
97 | $arrSearch[] = '%' . $attr . '%'; |
||||
98 | |||||
99 | if (is_array($val) && count($val) > 0 && is_string($val[0]) && strlen($val[0]) > 0) { |
||||
100 | $arrReplace[] = $this->connector->escapeFilterValue($val[0], true); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
101 | } else { |
||||
102 | $arrReplace[] = ''; |
||||
103 | } |
||||
104 | } |
||||
105 | |||||
106 | // merge the attributes into the ldap_search_filter |
||||
107 | /** @psalm-var string[] $arrReplace */ |
||||
108 | $filter = str_replace($arrSearch, $arrReplace, $this->searchFilter); |
||||
109 | if (strpos($filter, '%') !== false) { |
||||
110 | Logger::info(sprintf( |
||||
111 | '%s: There are non-existing attributes in the search filter. (%s)', |
||||
112 | $this->title, |
||||
113 | $filter, |
||||
114 | )); |
||||
115 | return; |
||||
116 | } |
||||
117 | |||||
118 | $this->connector->bind($this->searchUsername, $this->searchPassword); |
||||
119 | |||||
120 | $wantedAttrs = $this->config->getOptionalValue( |
||||
121 | 'attributes', |
||||
122 | // If specifically set to NULL return all attributes, if not set at all return nothing (safe default) |
||||
123 | in_array('attributes', $this->config->getOptions(), true) ? ['*'] : [], |
||||
124 | ); |
||||
125 | |||||
126 | $options = [ |
||||
127 | 'scope' => $this->config->getOptionalString('search.scope', Query::SCOPE_SUB), |
||||
128 | 'timeout' => $this->config->getOptionalInteger('timeout', 3), |
||||
129 | 'filter' => array_values($wantedAttrs), |
||||
130 | ]; |
||||
131 | |||||
132 | $entries = $this->connector->searchForMultiple( |
||||
133 | $this->searchBase, |
||||
134 | $filter, |
||||
135 | $options, |
||||
136 | true, |
||||
137 | ); |
||||
138 | |||||
139 | $results = []; |
||||
140 | foreach ($entries as $entry) { |
||||
141 | $tmp = array_intersect_key( |
||||
142 | $entry->getAttributes(), |
||||
143 | array_fill_keys(array_values($this->searchAttributes), null), |
||||
144 | ); |
||||
145 | |||||
146 | $binaries = array_intersect( |
||||
147 | array_keys($tmp), |
||||
148 | $this->binaryAttributes, |
||||
149 | ); |
||||
150 | foreach ($binaries as $binary) { |
||||
151 | $attr = $entry->getAttribute($binary); |
||||
152 | $tmp[$binary] = array_map('base64_encode', $attr); |
||||
0 ignored issues
–
show
It seems like
$attr can also be of type null ; however, parameter $array of array_map() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
153 | } |
||||
154 | |||||
155 | $results[] = $tmp; |
||||
156 | } |
||||
157 | |||||
158 | // handle [multiple] values |
||||
159 | foreach ($results as $result) { |
||||
160 | foreach ($this->searchAttributes as $target => $name) { |
||||
161 | // If there is no mapping defined, just use the name of the LDAP-attribute as a target |
||||
162 | if (is_int($target)) { |
||||
163 | $target = $name; |
||||
164 | } |
||||
165 | |||||
166 | if (isset($attributes[$target]) && $this->attrPolicy === 'replace') { |
||||
167 | unset($attributes[$target]); |
||||
168 | } |
||||
169 | |||||
170 | if (isset($result[$name])) { |
||||
171 | if (isset($attributes[$target])) { |
||||
172 | foreach (array_values($result[$name]) as $value) { |
||||
173 | if ($this->attrPolicy === 'merge') { |
||||
174 | if (!in_array($value, $attributes[$target], true)) { |
||||
175 | $attributes[$target][] = $value; |
||||
176 | } |
||||
177 | } else { |
||||
178 | $attributes[$target][] = $value; |
||||
179 | } |
||||
180 | } |
||||
181 | } else { |
||||
182 | $attributes[$target] = array_values($result[$name]); |
||||
183 | } |
||||
184 | } |
||||
185 | } |
||||
186 | } |
||||
187 | } |
||||
188 | } |
||||
189 |