256; $isStillDefault = hash_equals('raspberry', $rawPassword); // hash_equals on user-supplied vs user-supplied isn't a security // boundary (no secret on either side), but it's the right idiom // for password compares — and guards against any future caller // mistaking the result for an authentication signal. $confirmMatches = hash_equals($rawPassword, $rawPasswordConfirm); if ($rawPassword === '' || $hasIllegalChar || $tooLong) { $passwordRejected = true; $rejectReason = 'invalid'; error_log('Pi-Star change_password_required.php: adminPassword rejected ' . '(empty, contains NUL/CR/LF, or > 256 bytes)'); } elseif ($isStillDefault) { $passwordRejected = true; $rejectReason = 'default'; error_log('Pi-Star change_password_required.php: adminPassword rejected (still the default)'); } elseif (!$confirmMatches) { $passwordRejected = true; $rejectReason = 'mismatch'; error_log('Pi-Star change_password_required.php: adminPassword rejected ' . '(confirmation does not match)'); } else { // Pi-Star runs with `/` mounted read-only by default. chpasswd // and htpasswd both write to files on the rootfs (/etc/shadow // and /var/www/.htpasswd respectively); without a remount-rw // they fail with PAM "Authentication token manipulation error" // and the symptom looks like every password is rejected. The // matching configure.php POST handler does this at line ~348. // Re-seal the rootfs at the bottom of this block on every // exit path (success, PAM rejection, htpasswd failure). system('sudo mount -o remount,rw /'); $descriptors = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'), ); // 1. Linux user via chpasswd. Hardcoded command; payload on stdin only. $cpExit = 1; $cpStderr = ''; $proc = proc_open('sudo -n /usr/sbin/chpasswd', $descriptors, $pipes); if (is_resource($proc)) { fwrite($pipes[0], 'pi-star:' . $rawPassword . "\n"); fclose($pipes[0]); stream_get_contents($pipes[1]); fclose($pipes[1]); $cpStderr = stream_get_contents($pipes[2]); fclose($pipes[2]); $cpExit = proc_close($proc); } else { error_log('Pi-Star change_password_required.php: failed to spawn chpasswd'); } if ($cpExit !== 0) { // PAM rejection (libpwquality, history, length, etc.). $passwordRejected = true; $rejectReason = 'pam'; error_log('Pi-Star change_password_required.php: chpasswd exit=' . $cpExit . '; stderr=' . trim($cpStderr)); } else { // 2. Apache basic-auth file. /var/www/.htpasswd is owned by // www-data, so no sudo needed. $htExit = 1; $htStderr = ''; $proc = proc_open('htpasswd -i /var/www/.htpasswd pi-star', $descriptors, $pipes); if (is_resource($proc)) { fwrite($pipes[0], $rawPassword); fclose($pipes[0]); stream_get_contents($pipes[1]); fclose($pipes[1]); $htStderr = stream_get_contents($pipes[2]); fclose($pipes[2]); $htExit = proc_close($proc); if ($htExit !== 0) { error_log('Pi-Star change_password_required.php: htpasswd exit=' . $htExit . ' (Linux password updated); stderr=' . trim($htStderr)); } } else { error_log('Pi-Star change_password_required.php: failed to spawn htpasswd'); } // Tighten /var/www/.htpasswd from htpasswd's default 644 // to 600. Owner stays www-data so nginx (running as // www-data) keeps reading for basic-auth checks; other // local users no longer see the bcrypt hash. // // Surface chmod failure into the error log. The realistic // failure modes are (a) htpasswd spawn failed and the // file is in some unexpected state, or (b) a future // refactor moves this call outside the rootfs-rw window // (it sits inside one today — see commit aa999d49). // Either way, the file silently staying mode 644 would // be worth knowing about. if (@chmod('/var/www/.htpasswd', 0600) === false) { error_log('Pi-Star change_password_required.php: chmod 0600 /var/www/.htpasswd failed (file may stay mode 644 until next save)'); } // chpasswd succeeded; treat as success even if htpasswd // failed (operator can retry — same behaviour as // configure.php). The browser will re-prompt for the new // credentials on the next request. $passwordChanged = true; } // Re-seal the rootfs on every exit path out of the writable // block. sync first so the shadow / htpasswd writes land on // disk before the remount makes the FS read-only again. system('sudo sync && sudo sync && sudo sync && sudo mount -o remount,ro /'); } } ?> Pi-Star — Change Default Password

Pi-Star — Default Password Change Required

Working...
Password changed. Your browser will re-prompt for the new credentials shortly.

You are connecting from outside the local network and your dashboard is still using the factory default password. Set a new password to continue.

Passwords do not match. Please enter the same value in both fields.'; break; case 'default': $msg = 'Password rejected. The new password must be different from the factory default.'; break; case 'pam': $msg = 'Password rejected by the system. It must satisfy the system password-quality rules (length, complexity, history).'; break; case 'invalid': default: $msg = 'Password rejected. It must not contain NUL / CR / LF bytes and must be at most 256 bytes long.'; break; } ?>