protected function getPasswordHash(PhutilOpaqueEnvelope $envelope)
 {
     $raw_input = $envelope->openEnvelope();
     $hash = $raw_input;
     for ($ii = 0; $ii < 1000; $ii++) {
         $hash = md5($hash);
     }
     return new PhutilOpaqueEnvelope($hash);
 }
Пример #2
0
 /**
  * Digest a string into a password hash. This is similar to @{method:digest},
  * but requires a salt and iterates the hash to increase cost.
  */
 public static function digestPassword(PhutilOpaqueEnvelope $envelope, $salt)
 {
     $result = $envelope->openEnvelope();
     if (!$result) {
         throw new Exception(pht('Trying to digest empty password!'));
     }
     for ($ii = 0; $ii < 1000; $ii++) {
         $result = self::digest($result, $salt);
     }
     return $result;
 }
 protected function canUpgradeInternalHash(PhutilOpaqueEnvelope $hash)
 {
     $info = password_get_info($hash->openEnvelope());
     // NOTE: If the costs don't match -- even if the new cost is lower than
     // the old cost -- count this as an upgrade. This allows costs to be
     // adjusted down and hashing to be migrated toward the new cost if costs
     // are ever configured too high for some reason.
     $cost = idx($info['options'], 'cost');
     if ($cost != $this->getBcryptCost()) {
         return true;
     }
     return false;
 }
 private function formatStorageProperties(PhutilOpaqueEnvelope $key_envelope, PhutilOpaqueEnvelope $iv_envelope)
 {
     // Encode the raw binary data with base64 so we can wrap it in JSON.
     $data = array('iv.base64' => base64_encode($iv_envelope->openEnvelope()), 'key.base64' => base64_encode($key_envelope->openEnvelope()));
     // Encode the base64 data with JSON.
     $data_clear = phutil_json_encode($data);
     // Encrypt the block key with the master key, using a unique IV.
     $data_iv = self::newAES256IV();
     $key_name = $this->getMasterKeyName();
     $master_key = $this->getMasterKeyMaterial($key_name);
     $data_cipher = $this->encryptData($data_clear, $master_key, $data_iv);
     return array('key.name' => $key_name, 'iv.base64' => base64_encode($data_iv->openEnvelope()), 'payload.base64' => base64_encode($data_cipher));
 }
 public function decryptSecret(PhutilOpaqueEnvelope $secret, PhutilOpaqueEnvelope $password)
 {
     $tmp = new TempFile();
     Filesystem::writeFile($tmp, $secret->openEnvelope());
     if (!Filesystem::binaryExists('ssh-keygen')) {
         throw new Exception(pht('Decrypting SSH keys requires the `ssh-keygen` binary, but it ' . 'is not available in PATH. Either make it available or strip the ' . 'password fromt his SSH key manually before uploading it.'));
     }
     list($err, $stdout, $stderr) = exec_manual('ssh-keygen -p -P %P -N %s -f %s', $password, '', (string) $tmp);
     if ($err) {
         return null;
     } else {
         return new PhutilOpaqueEnvelope(Filesystem::readFile($tmp));
     }
 }
 private function authenticateHTTPRepositoryUser($username, PhutilOpaqueEnvelope $password)
 {
     if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
         // No HTTP auth permitted.
         return null;
     }
     if (!strlen($username)) {
         // No username.
         return null;
     }
     if (!strlen($password->openEnvelope())) {
         // No password.
         return null;
     }
     $user = id(new PhabricatorPeopleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withUsernames(array($username))->executeOne();
     if (!$user) {
         // Username doesn't match anything.
         return null;
     }
     if (!$user->isUserActivated()) {
         // User is not activated.
         return null;
     }
     $password_entry = id(new PhabricatorRepositoryVCSPassword())->loadOneWhere('userPHID = %s', $user->getPHID());
     if (!$password_entry) {
         // User doesn't have a password set.
         return null;
     }
     if (!$password_entry->comparePassword($password, $user)) {
         // Password doesn't match.
         return null;
     }
     // If the user's password is stored using a less-than-optimal hash, upgrade
     // them to the strongest available hash.
     $hash_envelope = new PhutilOpaqueEnvelope($password_entry->getPasswordHash());
     if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
         $password_entry->setPassword($password, $user);
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         $password_entry->save();
         unset($unguarded);
     }
     return $user;
 }
 public static function getTOTPCode(PhutilOpaqueEnvelope $key, $timestamp)
 {
     $binary_timestamp = pack('N*', 0) . pack('N*', $timestamp);
     $binary_key = self::base32Decode($key->openEnvelope());
     $hash = hash_hmac('sha1', $binary_timestamp, $binary_key, true);
     // See RFC 4226.
     $offset = ord($hash[19]) & 0xf;
     $code = (ord($hash[$offset + 0]) & 0x7f) << 24 | (ord($hash[$offset + 1]) & 0xff) << 16 | (ord($hash[$offset + 2]) & 0xff) << 8 | ord($hash[$offset + 3]);
     $code = $code % 1000000;
     $code = str_pad($code, 6, '0', STR_PAD_LEFT);
     return $code;
 }
 public function auth($username, PhutilOpaqueEnvelope $password)
 {
     if (strlen(trim($username)) == 0) {
         throw new Exception('Username can not be empty');
     }
     if (PhabricatorEnv::getEnvConfig('ldap.search-first')) {
         // To protect against people phishing for accounts we catch the
         // exception and present the default exception that would be presented
         // in the case of a failed bind.
         try {
             $user = $this->getUser($this->getUsernameAttribute(), $username);
             $username = $user[$this->getSearchAttribute()][0];
         } catch (PhabricatorLDAPUnknownUserException $e) {
             throw new Exception($this->invalidLDAPUserErrorMessage(self::LDAP_INVALID_CREDENTIALS, ldap_err2str(self::LDAP_INVALID_CREDENTIALS)));
         }
     }
     $conn = $this->getConnection();
     $activeDirectoryDomain = PhabricatorEnv::getEnvConfig('ldap.activedirectory_domain');
     if ($activeDirectoryDomain) {
         $dn = $username . '@' . $activeDirectoryDomain;
     } else {
         $dn = ldap_sprintf('%Q=%s,%Q', $this->getSearchAttribute(), $username, $this->getBaseDN());
     }
     // NOTE: It is very important we suppress any messages that occur here,
     // because it logs passwords if it reaches an error log of any sort.
     DarkConsoleErrorLogPluginAPI::enableDiscardMode();
     $result = @ldap_bind($conn, $dn, $password->openEnvelope());
     DarkConsoleErrorLogPluginAPI::disableDiscardMode();
     if (!$result) {
         throw new Exception($this->invalidLDAPUserErrorMessage(ldap_errno($conn), ldap_error($conn)));
     }
     $this->userData = $this->getUser($this->getSearchAttribute(), $username);
     return $this->userData;
 }
Пример #9
0
 private function getPasswordHashInput(PhutilOpaqueEnvelope $password)
 {
     $input = $this->getUsername() . $password->openEnvelope() . $this->getPHID() . $this->getPasswordSalt();
     return new PhutilOpaqueEnvelope($input);
 }
 /**
  * Get the human-readable algorithm name for a given hash.
  *
  * @param   PhutilOpaqueEnvelope  Storage hash.
  * @return  string                Human-readable algorithm name.
  */
 public static function getCurrentAlgorithmName(PhutilOpaqueEnvelope $hash)
 {
     $raw_hash = $hash->openEnvelope();
     if (!strlen($raw_hash)) {
         return pht('None');
     }
     try {
         $current_hasher = self::getHasherForHash($hash);
         return $current_hasher->getHumanReadableName();
     } catch (Exception $ex) {
         $info = self::parseHashFromStorage($hash);
         $name = $info['name'];
         return pht('Unknown ("%s")', $name);
     }
 }
 private function bindLDAP($conn, $user, PhutilOpaqueEnvelope $pass)
 {
     $profiler = PhutilServiceProfiler::getInstance();
     $call_id = $profiler->beginServiceCall(array('type' => 'ldap', 'call' => 'bind', 'user' => $user));
     // NOTE: ldap_bind() dumps cleartext passwords into logs by default. Keep
     // it quiet.
     if (strlen($user)) {
         $ok = @ldap_bind($conn, $user, $pass->openEnvelope());
     } else {
         $ok = @ldap_bind($conn);
     }
     $profiler->endServiceCall($call_id, array());
     if (!$ok) {
         if (strlen($user)) {
             $this->raiseConnectionException($conn, pht('Failed to bind to LDAP server (as user "%s").', $user));
         } else {
             $this->raiseConnectionException($conn, pht('Failed to bind to LDAP server (without username).'));
         }
     }
 }
 public function processRequest(AphrontRequest $request)
 {
     $user = $request->getUser();
     $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($user, $request, '/settings/');
     $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
     $min_len = (int) $min_len;
     // NOTE: To change your password, you need to prove you own the account,
     // either by providing the old password or by carrying a token to
     // the workflow from a password reset email.
     $key = $request->getStr('key');
     $token = null;
     if ($key) {
         $token = id(new PhabricatorAuthTemporaryTokenQuery())->setViewer($user)->withObjectPHIDs(array($user->getPHID()))->withTokenTypes(array(PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE))->withTokenCodes(array(PhabricatorHash::digest($key)))->withExpired(false)->executeOne();
     }
     $e_old = true;
     $e_new = true;
     $e_conf = true;
     $errors = array();
     if ($request->isFormPost()) {
         if (!$token) {
             $envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
             if (!$user->comparePassword($envelope)) {
                 $errors[] = pht('The old password you entered is incorrect.');
                 $e_old = pht('Invalid');
             }
         }
         $pass = $request->getStr('new_pw');
         $conf = $request->getStr('conf_pw');
         if (strlen($pass) < $min_len) {
             $errors[] = pht('Your new password is too short.');
             $e_new = pht('Too Short');
         } else {
             if ($pass !== $conf) {
                 $errors[] = pht('New password and confirmation do not match.');
                 $e_conf = pht('Invalid');
             } else {
                 if (PhabricatorCommonPasswords::isCommonPassword($pass)) {
                     $e_new = pht('Very Weak');
                     $e_conf = pht('Very Weak');
                     $errors[] = pht('Your new password is very weak: it is one of the most common ' . 'passwords in use. Choose a stronger password.');
                 }
             }
         }
         if (!$errors) {
             // This write is unguarded because the CSRF token has already
             // been checked in the call to $request->isFormPost() and
             // the CSRF token depends on the password hash, so when it
             // is changed here the CSRF token check will fail.
             $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
             $envelope = new PhutilOpaqueEnvelope($pass);
             id(new PhabricatorUserEditor())->setActor($user)->changePassword($user, $envelope);
             unset($unguarded);
             if ($token) {
                 // Destroy the token.
                 $token->delete();
                 // If this is a password set/reset, kick the user to the home page
                 // after we update their account.
                 $next = '/';
             } else {
                 $next = $this->getPanelURI('?saved=true');
             }
             id(new PhabricatorAuthSessionEngine())->terminateLoginSessions($user, $request->getCookie(PhabricatorCookies::COOKIE_SESSION));
             return id(new AphrontRedirectResponse())->setURI($next);
         }
     }
     $hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
     if (strlen($hash_envelope->openEnvelope())) {
         try {
             $can_upgrade = PhabricatorPasswordHasher::canUpgradeHash($hash_envelope);
         } catch (PhabricatorPasswordHasherUnavailableException $ex) {
             $can_upgrade = false;
             // Only show this stuff if we aren't on the reset workflow. We can
             // do resets regardless of the old hasher's availability.
             if (!$token) {
                 $errors[] = pht('Your password is currently hashed using an algorithm which is ' . 'no longer available on this install.');
                 $errors[] = pht('Because the algorithm implementation is missing, your password ' . 'can not be used or updated.');
                 $errors[] = pht('To set a new password, request a password reset link from the ' . 'login screen and then follow the instructions.');
             }
         }
         if ($can_upgrade) {
             $errors[] = pht('The strength of your stored password hash can be upgraded. ' . 'To upgrade, either: log out and log in using your password; or ' . 'change your password.');
         }
     }
     $len_caption = null;
     if ($min_len) {
         $len_caption = pht('Minimum password length: %d characters.', $min_len);
     }
     $form = new AphrontFormView();
     $form->setUser($user)->addHiddenInput('key', $key);
     if (!$token) {
         $form->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Old Password'))->setError($e_old)->setName('old_pw'));
     }
     $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('New Password'))->setError($e_new)->setName('new_pw'));
     $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('Confirm Password'))->setCaption($len_caption)->setError($e_conf)->setName('conf_pw'));
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Change Password')));
     $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Current Algorithm'))->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName(new PhutilOpaqueEnvelope($user->getPasswordHash()))));
     $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Best Available Algorithm'))->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
     $form->appendRemarkupInstructions(pht('NOTE: Changing your password will terminate any other outstanding ' . 'login sessions.'));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Change Password'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form);
     return array($form_box);
 }
Пример #13
0
 /**
  * Add a HTTP basic authentication header to the request.
  *
  * @param string                Username to authenticate with.
  * @param PhutilOpaqueEnvelope  Password to authenticate with.
  * @return this
  * @task config
  */
 public function setHTTPBasicAuthCredentials($username, PhutilOpaqueEnvelope $password)
 {
     $password_plaintext = $password->openEnvelope();
     $credentials = base64_encode($username . ':' . $password_plaintext);
     return $this->addHeader('Authorization', 'Basic ' . $credentials);
 }
Пример #14
0
 private function hashPassword(PhutilOpaqueEnvelope $envelope)
 {
     $hash = $this->getUsername() . $envelope->openEnvelope() . $this->getPHID() . $this->getPasswordSalt();
     for ($ii = 0; $ii < 1000; $ii++) {
         $hash = md5($hash);
     }
     return $hash;
 }