1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Authentication source for Apache 'htpasswd' files. |
5
|
|
|
* |
6
|
|
|
* @package SimpleSAMLphp |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
declare(strict_types=1); |
10
|
|
|
|
11
|
|
|
namespace SimpleSAML\Module\authcrypt\Auth\Source; |
12
|
|
|
|
13
|
|
|
use Exception; |
14
|
|
|
use SimpleSAML\{Error, Logger, Utils}; |
15
|
|
|
use SimpleSAML\Module\core\Auth\UserPassBase; |
16
|
|
|
use WhiteHat101\Crypt\APR1_MD5; |
17
|
|
|
|
18
|
|
|
use function array_merge; |
19
|
|
|
use function crypt; |
20
|
|
|
use function explode; |
21
|
|
|
use function file_get_contents; |
22
|
|
|
use function trim; |
23
|
|
|
|
24
|
|
|
class Htpasswd extends UserPassBase |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* Our users, stored in an array, where each value is "<username>:<passwordhash>". |
28
|
|
|
* |
29
|
|
|
* @var array<int, mixed> |
30
|
|
|
*/ |
31
|
|
|
private array $users; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* An array containing static attributes for our users. |
35
|
|
|
* |
36
|
|
|
* @var array<string, mixed> |
37
|
|
|
*/ |
38
|
|
|
private array $attributes = []; |
39
|
|
|
|
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Constructor for this authentication source. |
43
|
|
|
* |
44
|
|
|
* @param array<string, mixed> $info Information about this authentication source. |
45
|
|
|
* @param array<string, mixed> $config Configuration. |
46
|
|
|
* |
47
|
|
|
* @throws \Exception if the htpasswd file is not readable or the static_attributes array is invalid. |
48
|
|
|
*/ |
49
|
|
|
public function __construct(array $info, array $config) |
50
|
|
|
{ |
51
|
|
|
// Call the parent constructor first, as required by the interface |
52
|
|
|
parent::__construct($info, $config); |
53
|
|
|
|
54
|
|
|
$this->users = []; |
55
|
|
|
|
56
|
|
|
if (!$htpasswd = file_get_contents($config['htpasswd_file'])) { |
57
|
|
|
throw new Exception('Could not read ' . $config['htpasswd_file']); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$this->users = explode("\n", trim($htpasswd)); |
61
|
|
|
|
62
|
|
|
try { |
63
|
|
|
$attrUtils = new Utils\Attributes(); |
64
|
|
|
$this->attributes = $attrUtils->normalizeAttributesArray($config['static_attributes']); |
65
|
|
|
} catch (Exception $e) { |
66
|
|
|
throw new Exception('Invalid static_attributes in authentication source ' . |
67
|
|
|
$this->authId . ': ' . $e->getMessage()); |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Attempt to log in using the given username and password. |
74
|
|
|
* |
75
|
|
|
* On a successful login, this function should return the username as 'uid' attribute, |
76
|
|
|
* and merged attributes from the configuration file. |
77
|
|
|
* On failure, it should throw an exception. A \SimpleSAML\Error\Error('WRONGUSERPASS') |
78
|
|
|
* should be thrown in case of a wrong username OR a wrong password, to prevent the |
79
|
|
|
* enumeration of usernames. |
80
|
|
|
* |
81
|
|
|
* @param string $username The username the user wrote. |
82
|
|
|
* @param string $password The password the user wrote. |
83
|
|
|
* |
84
|
|
|
* @return array<string, mixed> Associative array with the users attributes. |
85
|
|
|
* |
86
|
|
|
* @throws \SimpleSAML\Error\Error if authentication fails. |
87
|
|
|
*/ |
88
|
|
|
protected function login( |
89
|
|
|
string $username, |
90
|
|
|
#[\SensitiveParameter] |
91
|
|
|
string $password, |
92
|
|
|
): array { |
93
|
|
|
$cryptoUtils = new Utils\Crypto(); |
94
|
|
|
foreach ($this->users as $userpass) { |
95
|
|
|
$matches = explode(':', $userpass, 2); |
96
|
|
|
if ($matches[0] == $username) { |
97
|
|
|
$crypted = $matches[1]; |
98
|
|
|
|
99
|
|
|
// This is about the only attribute we can add |
100
|
|
|
$attributes = array_merge(['uid' => [$username]], $this->attributes); |
101
|
|
|
|
102
|
|
|
// Traditional crypt(3) |
103
|
|
|
if ($cryptoUtils->secureCompare($crypted, crypt($password, $crypted))) { |
|
|
|
|
104
|
|
|
Logger::debug('User ' . $username . ' authenticated successfully'); |
105
|
|
|
Logger::warning( |
106
|
|
|
'CRYPT authentication is insecure. Please consider using something else.', |
107
|
|
|
); |
108
|
|
|
return $attributes; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// Apache's custom MD5 |
112
|
|
|
if (APR1_MD5::check($password, $crypted)) { |
113
|
|
|
Logger::debug('User ' . $username . ' authenticated successfully'); |
114
|
|
|
Logger::warning( |
115
|
|
|
'APR1 authentication is insecure. Please consider using something else.', |
116
|
|
|
); |
117
|
|
|
return $attributes; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
// PASSWORD_BCRYPT |
121
|
|
|
if ($cryptoUtils->pwValid($crypted, $password)) { |
|
|
|
|
122
|
|
|
Logger::debug('User ' . $username . ' authenticated successfully'); |
123
|
|
|
return $attributes; |
124
|
|
|
} |
125
|
|
|
throw new Error\Error('WRONGUSERPASS'); |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
throw new Error\Error('WRONGUSERPASS'); |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.