Completed
Pull Request — master (#2472)
by Kevin
23:34 queued 05:01
created

html/inc/user_util.inc::check_passwd_ui()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 7
Ratio 70 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 2
dl 7
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2017 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// functions for creating and deleting users
20
21
22
include_once("../inc/boinc_db.inc");
23
include_once("../inc/util.inc");
24
include_once("../inc/email.inc");
25
include_once("../inc/recaptchalib.php");
26
require_once("../inc/password_compat/password.inc");
27
28
// Password hash has old format.
29
// Update user record with new format
30
//
31
function do_passwd_rehash($user, $passwd_hash) {
32
    $database_passwd_hash = password_hash($passwd_hash, PASSWORD_DEFAULT);
33
    $result = $user->update(" passwd_hash='$database_passwd_hash' ");
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
34
}
35
36
// return true if the passwd hash (old format)
37
// matches the user's passwd hash (possibly new format)
38
//
39
function check_passwd_hash($user, $passwd_hash) {
40 View Code Duplication
    if (password_verify($passwd_hash, $user->passwd_hash)) {
41
        // on valid login, rehash password to upgrade hash overtime
42
        // as the defaults change.
43
        //
44
        if (password_needs_rehash($user->passwd_hash, PASSWORD_DEFAULT)) {
45
            do_passwd_rehash($user, $passwd_hash);
46
        }
47
    } else if ($passwd_hash == $user->passwd_hash) {
48
        // user record has old format.  Change to new.
49
        //
50
        do_passwd_rehash($user, $passwd_hash);
51
    } else {
52
        return false;
53
    }
54
    return true;
55
}
56
57
function check_passwd_ui($user, $passwd) {
58
    $passwd_hash = md5($passwd.$user->email_addr);
59 View Code Duplication
    if(!check_passwd_hash($user, $passwd_hash)) {
60
        sleep(LOGIN_FAIL_SLEEP_SEC);
61
        page_head("Password incorrect");
62
        echo "The password you entered is incorrect. Please go back and try again.\n";
63
        page_tail();
64
        exit;
65
    }
66
}
67
68
function is_banned_email_addr($email_addr) {
69
    global $banned_email_domains;
70
    if (isset($banned_email_domains)) {
71
        foreach($banned_email_domains as $d) {
72
            $x = strstr($email_addr, $d);
73
            if ($x == $d) return true;
74
        }
75
    }
76
    return false;
77
}
78
79
function is_valid_user_name($name, &$reason) {
80
    if (trim($name) !== $name) {
81
        $reason = tra("user name cannot have leading or trailing white space");
82
        return false;
83
    }
84
    if (strlen($name) == 0) {
85
        $reason = tra("user name must be nonempty");
86
        return false;
87
    }
88
    if (sanitize_tags($name) !== $name) {
89
        $reason = tra("user name may not contain HTML tags");
90
        return false;
91
    }
92
    return true;
93
}
94
95
// the following DB-escapes its args
96
//
97
function make_user(
98
    $email_addr, $name, $passwd_hash,
99
    $country=null, $postal_code=null, $project_prefs=null, $teamid=0
100
) {
101
    if (!is_valid_email_addr($email_addr)) return null;
102
    if (is_banned_email_addr($email_addr)) return null;
103
104
    $authenticator = random_string();
105
    $cross_project_id = random_string();
106
    $now = time();
107
    if (!is_valid_country($country)) return null;
108
109
    $email_addr = BoincDb::escape_string($email_addr);
110
    $name = sanitize_tags($name);
111
    $name = BoincDb::escape_string($name);
112
    $database_passwd_hash = password_hash($passwd_hash, PASSWORD_DEFAULT);
113
114
    $country = BoincDb::escape_string($country);
115
    $postal_code = sanitize_tags(BoincDb::escape_string($postal_code));
116
117
    $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid,  venue, send_email, show_hosts, posts, seti_id, seti_nresults, seti_last_result_time, seti_total_cpu, has_profile, cross_project_id, passwd_hash, email_validated, donated) values($now, '$email_addr', '$name', '$authenticator', '$country', '$postal_code', 0, 0, unix_timestamp(), '$project_prefs', $teamid, '', 1, 1, 0, 0, 0, 0, 0, 0, '$cross_project_id', '$database_passwd_hash', 0, 0)");
118
119
    if (!$uid) {
120
        return null;
121
    }
122
    $user = BoincUser::lookup_id($uid);
123
    if (defined('RECORD_USER_IP')) {
124
        $ip = $_SERVER['REMOTE_ADDR'];
125
        $ip = BoincDb::escape_string($ip);
126
        $user->update("venue='$ip'");
127
    }
128
    return $user;
129
}
130
131
function make_user_ldap($email_addr, $name) {
132
    $email_addr = BoincDb::escape_string($email_addr);
133
    $name = sanitize_tags($name);
134
    $name = BoincDb::escape_string($name);
135
    $authenticator = random_string();
136
    $cross_project_id = random_string();
137
    $passwd_hash = random_string();
138
    $now = time();
139
    $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid,  send_email, show_hosts, cross_project_id, passwd_hash) values($now, '$email_addr', '$name', '$authenticator', '', '', 0, 0, unix_timestamp(), '', 0, 1, 1, '$cross_project_id', '$passwd_hash')");
140
141
    if ($uid) {
142
        return BoincUser::lookup_id($uid);
143
    } else {
144
        return null;
145
    }
146
}
147
148
function show_error($str) {
149
    page_head(tra("Can't create account"));
150
    echo "$str<br>\n";
151
    echo BoincDb::error();
152
    echo "<p>".tra("Click your browser's <b>Back</b> button to try again.")."\n</p>\n";
153
    page_tail();
154
    exit();
155
}
156
157
// validate POST args and make user.
158
// If error show error page and exit.
159
// Else return user object
160
//
161
function validate_post_make_user() {
162
    global $recaptcha_private_key;
163
    $config = get_config();
164
    if (parse_bool($config, "disable_account_creation")
165
        || parse_bool($config, "no_web_account_creation")
166
    ) {
167
        error_page("Account creation is disabled");
168
    }
169
170
    if ($recaptcha_private_key) {
171
        if (!boinc_recaptcha_isValidated($recaptcha_private_key)) {
172
            show_error(
173
                tra("Your reCAPTCHA response was not correct. Please try again.")
174
            );
175
        }
176
    }
177
178
    // see whether the new account should be pre-enrolled in a team,
179
    // and initialized with its founder's project prefs
180
    //
181
    $teamid = post_int("teamid", true);
182
    if ($teamid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $teamid of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
183
        $team = BoincTeam::lookup_id($teamid);
184
        $clone_user = BoincUser::lookup_id($team->userid);
185
        if (!$clone_user) {
186
            error_page("User $userid not found");
0 ignored issues
show
Bug introduced by
The variable $userid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
187
        }
188
        $project_prefs = $clone_user->project_prefs;
189
    } else {
190
        $teamid = 0;
191
        $project_prefs = "";
192
    }
193
194
    if (defined('INVITE_CODES')) {
195
        $invite_code = post_str("invite_code");
196
        if (strlen($invite_code) == 0) {
197
            show_error(tra("You must supply an invitation code to create an account."));
198
        }
199
        if (!preg_match(INVITE_CODES, $invite_code)) {
200
            show_error(tra("The invitation code you gave is not valid."));
201
        }
202
    } 
203
204
    $new_name = post_str("new_name");
205
    if (!is_valid_user_name($new_name, $reason)) {
206
        show_error($reason);
207
    }
208
209
    $new_email_addr = strtolower(post_str("new_email_addr"));
210
    if (!is_valid_email_addr($new_email_addr)) {
211
        show_error(tra("Invalid email address: please enter a valid address of the form [email protected]"));
212
    }
213
    $user = BoincUser::lookup_email_addr($new_email_addr);
214
    if ($user) {
215
        show_error(tra("There's already an account with that email address."));
216
    }
217
    $tmpuser = BoincUser::lookup_prev_email_addr($new_email_addr);
218
    if ($tmpuser) {
219
        show_error(tra("There's already an account with that email address."));
220
    }
221
222
    $passwd = post_str("passwd");
223
224
    $min_passwd_length = parse_config($config, "<min_passwd_length>");
225
    if (!$min_passwd_length) $min_passwd_length = 6;
226
227
    if (!is_ascii($passwd)) {
228
        show_error(tra("Passwords may only include ASCII characters."));
229
    }
230
231
    if (strlen($passwd)<$min_passwd_length) {
232
        show_error(
233
            tra("New password is too short: minimum password length is %1 characters.", $min_passwd_length)
234
        );
235
    }
236
237
    $passwd_hash = md5($passwd.$new_email_addr);
238
239
    $country = post_str("country", true);
240
    if (!$country) {
241
        $country = "None";
242
    }
243
    if (!is_valid_country($country)) {
244
        error_page("bad country");
245
    }
246
247
    if (POSTAL_CODE) {
248
        $postal_code = sanitize_tags(post_str("postal_code", true));
249
    } else {
250
        $postal_code = '';
251
    }
252
253
    $user = make_user(
254
        $new_email_addr, $new_name, $passwd_hash,
255
        $country, $postal_code, $project_prefs, $teamid
256
    );
257
    if (!$user) {
258
        show_error(
259
            tra("Couldn't create account").": ".BoincDb::error()
260
        );
261
    }
262
263
    if (defined('INVITE_CODES')) {
264
        error_log("Account '$new_email_addr' created using invitation code '$invite_code'");
0 ignored issues
show
Bug introduced by
The variable $invite_code does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
265
    }
266
    return $user;
267
}
268
269
// delete a user and all associated records except
270
// result
271
// host
272
// batch
273
// team
274
// user_submit
275
// user_submit_app
276
// credited_job
277
// donation_paypal
278
// sent_email
279
//
280
function delete_user($user) {
281
    delete_profile($user);
282
    forum_delete_user($user);
283
        // deletes post, thread, subscription, forum_preferences, forum_logging
284
    BoincPrivateMessage::delete_aux("userid=$user->id or senderid=$user->id");
285
    BoincNotify::delete_aux("userid=$user->id");
286
    BoincCreditUser::delete_user($user);
287
    BoincBadgeUser::delete("user_id=$user->id");
288
    BoincFriend::delete_aux("user_src=$user->id or user_dest=$user->id");
289
    BoincToken::delete_for_user($user->id);
290
    $user->delete();
291
}
292
?>
293