Issues (1270)

plugins/auth_internal/init.php (6 issues)

1
<?php
2
class Auth_Internal extends Plugin implements IAuthModule {
3
4
    private $host;
5
6
    public function about() {
7
        return array(1.0,
8
            "Authenticates against internal tt-rss database",
9
            "fox",
10
            true);
11
    }
12
13
    /* @var PluginHost $host */
14
    public function init($host) {
15
        $this->host = $host;
16
        $this->pdo = Db::pdo();
17
18
        $host->add_hook($host::HOOK_AUTH_USER, $this);
19
    }
20
21
    public function authenticate($login, $password, $service = '') {
22
23
        $pwd_hash1 = encrypt_password($password);
24
        $pwd_hash2 = encrypt_password($password, $login);
25
        $otp = $_REQUEST["otp"];
26
27
        if (get_schema_version() > 96) {
28
29
            $sth = $this->pdo->prepare("SELECT otp_enabled,salt FROM ttrss_users WHERE
30
				login = ?");
31
            $sth->execute([$login]);
32
33
            if ($row = $sth->fetch()) {
34
                $otp_enabled = $row['otp_enabled'];
35
36
                if ($otp_enabled) {
37
38
                    // only allow app password checking if OTP is enabled
39
                    if ($service && get_schema_version() > 138) {
40
                        return $this->check_app_password($login, $password, $service);
41
                    }
42
43
                    if ($otp) {
44
                        $base32 = new \OTPHP\Base32();
0 ignored issues
show
The type OTPHP\Base32 was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
45
46
                        $secret = $base32->encode(mb_substr(sha1($row["salt"]), 0, 12), false);
47
                        $secret_legacy = $base32->encode(sha1($row["salt"]));
48
49
                        $totp = new \OTPHP\TOTP($secret);
0 ignored issues
show
The call to OTPHP\TOTP::__construct() has too few arguments starting with period. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
                        $totp = /** @scrutinizer ignore-call */ new \OTPHP\TOTP($secret);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
50
                        $otp_check = $totp->now();
51
52
                        $totp_legacy = new \OTPHP\TOTP($secret_legacy);
53
                        $otp_check_legacy = $totp_legacy->now();
54
55
                        if ($otp != $otp_check && $otp != $otp_check_legacy) {
56
                            return false;
57
                        }
58
                    } else {
59
                        $return = urlencode($_REQUEST["return"]);
60
                        ?>
61
						<!DOCTYPE html>
62
						<html>
63
							<head>
64
								<title>Tiny Tiny RSS</title>
65
								<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
66
							</head>
67
							<?php echo stylesheet_tag("css/default.css") ?>
0 ignored issues
show
Are you sure the usage of stylesheet_tag('css/default.css') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function stylesheet_tag() has been deprecated: Use Twig filter cssTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

67
							<?php echo /** @scrutinizer ignore-deprecated */ stylesheet_tag("css/default.css") ?>

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.

Loading history...
68
						<body class="ttrss_utility otp">
69
						<h1><?php echo __("Authentication") ?></h1>
70
						<div class="content">
71
						<form action="public.php?return=<?php echo $return ?>"
72
								method="POST" class="otpform">
73
							<input type="hidden" name="op" value="login">
74
							<input type="hidden" name="login" value="<?php echo htmlspecialchars($login) ?>">
75
							<input type="hidden" name="password" value="<?php echo htmlspecialchars($password) ?>">
76
							<input type="hidden" name="bw_limit" value="<?php echo htmlspecialchars($_POST["bw_limit"]) ?>">
77
							<input type="hidden" name="remember_me" value="<?php echo htmlspecialchars($_POST["remember_me"]) ?>">
78
							<input type="hidden" name="profile" value="<?php echo htmlspecialchars($_POST["profile"]) ?>">
79
80
							<fieldset>
81
								<label><?php echo __("Please enter your one time password:") ?></label>
82
								<input autocomplete="off" size="6" name="otp" value=""/>
83
								<input type="submit" value="Continue"/>
84
							</fieldset>
85
						</form></div>
86
						<script type="text/javascript">
87
							document.forms[0].otp.focus();
88
						</script>
89
						<?php
90
                        exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
91
                    }
92
                }
93
            }
94
        }
95
96
        // check app passwords first but allow regular password as a fallback for the time being
97
        // if OTP is not enabled
98
99
        if ($service && get_schema_version() > 138) {
100
            $user_id = $this->check_app_password($login, $password, $service);
101
102
            if ($user_id) {
103
                            return $user_id;
104
            }
105
        }
106
107
        if (get_schema_version() > 87) {
108
109
            $sth = $this->pdo->prepare("SELECT salt FROM ttrss_users WHERE login = ?");
110
            $sth->execute([$login]);
111
112
            if ($row = $sth->fetch()) {
113
                $salt = $row['salt'];
114
115
                if ($salt == "") {
116
117
                    $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE
118
						login = ? AND (pwd_hash = ? OR pwd_hash = ?)");
119
120
                    $sth->execute([$login, $pwd_hash1, $pwd_hash2]);
121
122
                    // verify and upgrade password to new salt base
123
124
                    if ($row = $sth->fetch()) {
125
                        // upgrade password to MODE2
126
127
                        $user_id = $row['id'];
128
129
                        $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
130
                        $pwd_hash = encrypt_password($password, $salt, true);
131
132
                        $sth = $this->pdo->prepare("UPDATE ttrss_users SET
133
							pwd_hash = ?, salt = ? WHERE login = ?");
134
135
                        $sth->execute([$pwd_hash, $salt, $login]);
136
137
                        return $user_id;
138
139
                    } else {
140
                        return false;
141
                    }
142
143
                } else {
144
                    $pwd_hash = encrypt_password($password, $salt, true);
145
146
                    $sth = $this->pdo->prepare("SELECT id
147
						  FROM ttrss_users WHERE
148
						  login = ? AND pwd_hash = ?");
149
                    $sth->execute([$login, $pwd_hash]);
150
151
                    if ($row = $sth->fetch()) {
152
                        return $row['id'];
153
                    }
154
                }
155
156
            } else {
157
                $sth = $this->pdo->prepare("SELECT id
158
					FROM ttrss_users WHERE
159
					  login = ? AND (pwd_hash = ? OR pwd_hash = ?)");
160
161
                $sth->execute([$login, $pwd_hash1, $pwd_hash2]);
162
163
                if ($row = $sth->fetch()) {
164
                    return $row['id'];
165
                }
166
            }
167
        } else {
168
            $sth = $this->pdo->prepare("SELECT id
169
					FROM ttrss_users WHERE
170
					  login = ? AND (pwd_hash = ? OR pwd_hash = ?)");
171
172
            $sth->execute([$login, $pwd_hash1, $pwd_hash2]);
173
174
            if ($row = $sth->fetch()) {
175
                return $row['id'];
176
            }
177
        }
178
179
        return false;
180
    }
181
182
    public function check_password($owner_uid, $password) {
183
184
        $sth = $this->pdo->prepare("SELECT salt,login,otp_enabled FROM ttrss_users WHERE
185
			id = ?");
186
        $sth->execute([$owner_uid]);
187
188
        if ($row = $sth->fetch()) {
189
190
            $salt = $row['salt'];
191
            $login = $row['login'];
192
193
            if (!$salt) {
194
                $password_hash1 = encrypt_password($password);
195
                $password_hash2 = encrypt_password($password, $login);
196
197
                $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE
198
					id = ? AND (pwd_hash = ? OR pwd_hash = ?)");
199
200
                $sth->execute([$owner_uid, $password_hash1, $password_hash2]);
201
202
                return $sth->fetch();
203
204
            } else {
205
                $password_hash = encrypt_password($password, $salt, true);
206
207
                $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE
208
					id = ? AND pwd_hash = ?");
209
210
                $sth->execute([$owner_uid, $password_hash]);
211
212
                return $sth->fetch();
213
            }
214
        }
215
216
        return false;
217
    }
218
219
    public function change_password($owner_uid, $old_password, $new_password) {
220
221
        if ($this->check_password($owner_uid, $old_password)) {
222
223
            $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
224
            $new_password_hash = encrypt_password($new_password, $new_salt, true);
225
226
            $sth = $this->pdo->prepare("UPDATE ttrss_users SET
227
				pwd_hash = ?, salt = ?, otp_enabled = false
228
					WHERE id = ?");
229
            $sth->execute([$new_password_hash, $new_salt, $owner_uid]);
230
231
            $_SESSION["pwd_hash"] = $new_password_hash;
232
233
            $sth = $this->pdo->prepare("SELECT email, login FROM ttrss_users WHERE id = ?");
234
            $sth->execute([$owner_uid]);
235
236
            if ($row = $sth->fetch()) {
237
                $mailer = new Mailer();
238
239
                require_once "lib/MiniTemplator.class.php";
240
241
                $tpl = new MiniTemplator;
242
243
                $tpl->readTemplateFromFile("templates/password_change_template.txt");
244
245
                $tpl->setVariable('LOGIN', $row["login"]);
246
                $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
0 ignored issues
show
The constant SELF_URL_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
247
248
                $tpl->addBlock('message');
249
250
                $tpl->generateOutputToString($message);
251
252
                $mailer->mail(["to_name" => $row["login"],
253
                    "to_address" => $row["email"],
254
                    "subject" => "[tt-rss] Password change notification",
255
                    "message" => $message]);
256
257
            }
258
259
            return __("Password has been changed.");
260
        } else {
261
            return "ERROR: ".__('Old password is incorrect.');
262
        }
263
    }
264
265
    private function check_app_password($login, $password, $service) {
266
        $sth = $this->pdo->prepare("SELECT p.id, p.pwd_hash, u.id AS uid
267
			FROM ttrss_app_passwords p, ttrss_users u
268
			WHERE p.owner_uid = u.id AND u.login = ? AND service = ?");
269
        $sth->execute([$login, $service]);
270
271
        while ($row = $sth->fetch()) {
272
            list ($algo, $hash, $salt) = explode(":", $row["pwd_hash"]);
273
274
            if ($algo == "SSHA-512") {
275
                $test_hash = hash('sha512', $salt.$password);
276
277
                if ($test_hash == $hash) {
278
                    $usth = $this->pdo->prepare("UPDATE ttrss_app_passwords SET last_used = NOW() WHERE id = ?");
279
                    $usth->execute([$row['id']]);
280
281
                    return $row['uid'];
282
                }
283
            } else {
284
                user_error("Got unknown algo of app password for user $login: $algo");
285
            }
286
        }
287
288
        return false;
289
    }
290
291
    public function api_version() {
292
        return 2;
293
    }
294
295
}
296