CVE-2021-25956 (PHP, published 2021-08-17T15:15Z)
Description: value: In “Dolibarr” application, v3.3.beta1_20121221 to v13.0.2 have “Modify” access for admin level users to change other user’s details but fails to validate already existing “Login” name, while renaming the user “Login”. This leads to complete account takeover of the victim user. This happens since the password gets overwritten for the victim user having a similar login name.
Severity: MEDIUM (CVSS v3 base score: 7.2)
Weakness: NVD-CWE-Other \u2014 Other
NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Repository: https://github.com/Dolibarr/dolibarr
Files changed:
htdocs/langs/en_US/errors.lang
htdocs/user/card.php
htdocs/user/class/user.class.php
Commit message: FIX Broken Access Control reported by Ahsan Aziz.

Vulnerable code:
				if (!empty($conf->multicompany->enabled)) {
					if (GETPOST("superadmin")) {
						$object->entity = 0;
					} elseif (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
						$object->entity = 1; // all users in master entity
						$object->entity = (!GETPOST('entity', 'int') ? 0 : GETPOST('entity', 'int'));
				} else {
					$object->entity = (!GETPOST('entity', 'int') ? 0 : GETPOST('entity', 'int'));
		$sql .= ", entity = '".$this->db->escape($this->entity)."'";

Fixed code:
ErrorEmailAlreadyExists=Email %s already exists.
				// Do we update also ->entity ?
				if (!empty($conf->multicompany->enabled)) {	// If multicompany is not enabled, we never update the entity of a user.
					if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
						$object->entity = 1; // all users are in master entity
						// A user should not be able to move a user into another entity. Only superadmin should be able to do this.
						if ($user->entity == 0 && $user->admin) {
							if (GETPOST("superadmin")) {
								// We try to set the user as superadmin.
								$object->entity = 0;
							} else {
								// We try to change the entity of user
								$object->entity = (GETPOSTISSET('entity') ? GETPOSTINT('entity') : $object->entity);
							}
						}
		// Check if login already exists in same entity or into entity 0.
		if ($this->oldcopy->login != $this->login) {
			$sqltochecklogin = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."user WHERE entity IN (".((int) $this->entity).", 0) AND login = '".$this->db->escape($this->login)."'";
			$resqltochecklogin = $this->db->query($sqltochecklogin);
			if ($resqltochecklogin) {
				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
				if ($objtochecklogin && $objtochecklogin->nb > 0) {
					$langs->load("errors");
					$this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
					return -1;
				}
			}
		}
		if ($this->email !== '' && $this->oldcopy->email != $this->email) {
			$sqltochecklogin = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."user WHERE entity IN (".((int) $this->entity).", 0) AND email = '".$this->db->escape($this->email)."'";
			$resqltochecklogin = $this->db->query($sqltochecklogin);
			if ($resqltochecklogin) {
				$objtochecklogin = $this->db->fetch_object($resqltochecklogin);
				if ($objtochecklogin && $objtochecklogin->nb > 0) {
					$langs->load("errors");
					$this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
					return -1;
				}
			}
		}

		$sql .= ", entity = ".((int) $this->entity);