Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.1.7 Fixes & Features #98

Merged
merged 12 commits into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ function get($id, $data=[], $all=false) {
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "User privilege must be type array or string"
"message" => "System users cannot be deleted"
],
5006 => [
"status" => "bad request",
Expand Down Expand Up @@ -1726,6 +1726,36 @@ function get($id, $data=[], $all=false) {
"return" => $id,
"message" => "Authentication server name already in use"
],
5036 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Invalid characters in username"
],
5037 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Username is reserved by the system"
],
5038 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Username cannot contain more than 32 characters"
],
5039 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Invalid characters is IPsec PSK"
],
5040 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "User expiration date must be in MM/DD/YYYY format"
],
//6000-6999 reserved for /routing API calls
6000 => [
"status" => "bad request",
Expand Down Expand Up @@ -1904,7 +1934,7 @@ function get($id, $data=[], $all=false) {


];
$response = $responses[(!in_array($id, $responses)) ? $id : 1];
$response = $responses[(array_key_exists($id, $responses)) ? $id : 1];
$response["data"] = $data;
if ($all === true) {
$response = $responses;
Expand Down
1 change: 1 addition & 0 deletions pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require_once("util.inc");
require_once("interfaces.inc");
require_once("interfaces_fast.inc");
require_once("priv.defs.inc");
require_once("priv.inc");
require_once("service-utils.inc");
require_once("filter.inc");
require_once("shaper.inc");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class APIFirewallVirtualIPCreate extends APIModel {
if ($this->validated_data["mode"] === "carp") {
# Check for our optional 'vhid' payload value. Assume default if none was specified.
if (isset($this->initial_data['vhid'])) {
if ($this->__vhid_exists($this->initial_data['vhid'])) {
if ($this->__vhid_exists($this->initial_data["interface"], $this->initial_data['vhid'])) {
$this->errors[] = APIResponse\get(4027);
} elseif (1 > $this->initial_data['vhid'] or $this->initial_data['vhid'] > 255) {
$this->errors[] = APIResponse\get(4028);
Expand Down Expand Up @@ -153,14 +153,14 @@ class APIFirewallVirtualIPCreate extends APIModel {
$this->validated_data["type"] = "network";
}

private function __vhid_exists($vhid) {
private function __vhid_exists($interface, $vhid) {
# Loop through each virtual IP and ensure it is not using the requested vhid
foreach ($this->config["virtualip"]["vip"] as $vip) {
if (intval($vhid) === intval($vip["vhid"])) {
if ($interface === $vip["interface"] && intval($vhid) === intval($vip["vhid"])) {
return true;
}
}
return false;
}

}
}
134 changes: 114 additions & 20 deletions pfSense-pkg-API/files/etc/inc/api/models/APIUserCreate.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,57 +25,151 @@ class APIUserCreate extends APIModel {
}

public function action() {
# Increase the system's next UID by one and add our user to the configuration
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1);
$this->config['system']['user'][] = $this->validated_data;
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1); // Increase our next UID
local_user_set_password($this->validated_data, $this->validated_data["password"]); // Set our new user's password
local_user_set($this->validated_data);

# Write the user to configuration and set the user on the backend. Return response with created user object.
$this->write_config();
$userindex = index_users(); // Update our user index
local_user_set($this->validated_data);
return APIResponse\get(0, $this->validated_data);
}

public function validate_payload() {
$this->validated_data["uid"] = $this->config["system"]["nextuid"]; // Save our next UID
private function __validate_username() {
# Check for our required `username` payload value
if (isset($this->initial_data['username'])) {
// Check that our user already exists
if (array_key_exists($this->initial_data['username'], index_users())) {
$this->errors[] = APIResponse\get(5002);
# Ensure a user with this username does not already exist
if (!array_key_exists($this->initial_data['username'], index_users())) {
# Ensure the username does not contain invalid characters
if (!preg_match("/[^a-zA-Z0-9\.\-_]/", $this->initial_data['username'])) {
# Ensure username is not reserved by the system
if (!$this->is_username_reserved($this->initial_data["username"])) {
# Ensure username is not longer that 32 characters
if (strlen($this->initial_data["username"]) <= 32) {
$this->validated_data["name"] = $this->initial_data['username'];
} else {
$this->errors[] = APIResponse\get(5038);
}
} else {
$this->errors[] = APIResponse\get(5037);
}
} else {
$this->errors[] = APIResponse\get(5036);
}
} else {
$this->validated_data["name"] = trim($this->initial_data['username']);
$this->errors[] = APIResponse\get(5002);
}
} else {
$this->errors[] = APIResponse\get(5000);
}
}

private function __validate_password() {
# Check for our required `password` payload value
if (isset($this->initial_data['password'])) {
$this->validated_data["password"] = trim($this->initial_data['password']);
# Generate the password hash and add it to our validated data
local_user_set_password($this->validated_data, $this->initial_data['password']);
} else {
$this->errors[] = APIResponse\get(5003);
}
}

private function __validate_priv() {
global $priv_list;
$this->validated_data["priv"] = [];

# Check for our optional `priv` payload value
if ($this->initial_data["priv"]) {
# Ensure value is an array
if (!is_array($this->initial_data["priv"])) {
$this->initial_data["priv"] = array($this->initial_data["priv"]);
}

# Loop through each requested privilege and ensure it exists
foreach ($this->initial_data["priv"] as $priv) {
if (array_key_exists($priv, $priv_list)) {
$this->validated_data["priv"][] = $priv;
$this->validated_data["priv"] = array_unique($this->validated_data["priv"]);
} else {
$this->errors[] = APIResponse\get(5006);
break;
}
}
}
}

private function __validate_disabled() {
# Check for our optional `disabled` payload value
if ($this->initial_data["disabled"] === true) {
$this->validated_data["disabled"] = ""; // Update our user's disabled value if not false
} elseif ($this->initial_data["disabled"] === false) {
unset($this->validated_data["disabled"]); // Unset our disabled value if not requested
$this->validated_data["disabled"] = "";
}
}

private function __validate_descr() {
# Check for our optional `descr` payload value
if (isset($this->initial_data['descr'])) {
$this->validated_data["descr"] = trim($this->initial_data['descr']); // Update our user's full name
$this->validated_data["descr"] = $this->initial_data['descr'];
}
}

private function __validate_expires() {
# Check for our optional `expires` payload value
if (isset($this->initial_data['expires'])) {
$this->validated_data["expires"] = trim($this->initial_data['expires']); // Update our user's expiration date
# Try to format the date string, return an error if the format is invalid
try {
$this->validated_data["expires"] = (new DateTime($this->initial_data['expires']))->format("m/d/Y");
} catch (Exception $e) {
$this->errors[] = APIResponse\get(5040);
}
}
}

private function __validate_authorizedkeys() {
# Check for our optional `authorizedkeys` payload value
if (isset($this->initial_data['authorizedkeys'])) {
$this->validated_data["authorizedkeys"] = trim($this->initial_data['authorizedkeys']); // Update our user's authorized keys
$this->validated_data["authorizedkeys"] = base64_encode($this->initial_data['authorizedkeys']);
}
}

private function __validate_ipsecpsk() {
# Check for our optional `ipsecpsk` payload value
if (isset($this->initial_data['ipsecpsk'])) {
$this->validated_data["ipsecpsk"] = trim($this->initial_data['ipsecpsk']); // Update our user's IPsec pre-shared key
# Ensure the PSK does not contain invalid characters
if (preg_match('/^[[:ascii:]]*$/', $_POST['ipsecpsk'])) {
$this->validated_data["ipsecpsk"] = $this->initial_data['ipsecpsk'];
} else {
$this->errors[] = APIResponse\get(5039);
}
}
}

$this->validated_data["scope"] = "user"; // Set our new user's system scope
$this->validated_data["priv"] = []; // Default our privs to empty array
public function validate_payload() {
# Set static object values
$this->validated_data["uid"] = $this->config["system"]["nextuid"];
$this->validated_data["scope"] = "user";

# Run each validation method
$this->__validate_username();
$this->__validate_password();
$this->__validate_priv();
$this->__validate_descr();
$this->__validate_disabled();
$this->__validate_expires();
$this->__validate_authorizedkeys();
$this->__validate_ipsecpsk();
}

public function is_username_reserved($user) {
# Open the /etc/passwd file to read all system users
$sys_users = explode(PHP_EOL, file_get_contents("/etc/passwd"));

# Loop through each system user and check if the username is reserved
foreach ($sys_users as $sys_user_ent) {
$sys_username = explode(":", $sys_user_ent)[0];
if ($sys_username == $user) {
return true;
}
}
return false;
}
}
37 changes: 24 additions & 13 deletions pfSense-pkg-API/files/etc/inc/api/models/APIUserDelete.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,37 @@ class APIUserDelete extends APIModel {
}

public function action() {
$index_id = index_users()[$this->validated_data["username"]]; // Save our user's index ID number
$del_user = $this->config["system"]["user"][$index_id];
local_user_del($this->config["system"]["user"][$index_id]); // Delete our user on the backend
unset($this->config['system']['user'][$index_id]); // Unset our user from config
$this->config['system']['user'] = array_values($this->config['system']['user']); // Reindex our users
$this->write_config(); // Write our new config
return APIResponse\get(0, $del_user);
# Remove user from backend and remove from config
local_user_del($this->config["system"]["user"][$this->id]);
unset($this->config["system"]["user"][$this->id]);
$this->write_config();
return APIResponse\get(0, $this->validated_data);
}

public function validate_payload() {
if (isset($this->initial_data["username"])) {
if (!array_key_exists($this->initial_data["username"], index_users())) {
private function __validate_username() {
# Check for our required `username` payload value
if (isset($this->initial_data['username'])) {
# Loop through each configured user and check if this user exists
foreach ($this->config["system"]["user"] as $id=>$user) {
if ($this->initial_data["username"] === $user["name"]) {
$this->validated_data = $user;
$this->id = intval($id);
}
}
# Set an error if no user was found
if (!isset($this->validated_data["uid"])) {
$this->errors[] = APIResponse\get(5001);
} else {
$this->validated_data["username"] = $this->initial_data['username'];
$this->validated_data["username"] = trim($this->validated_data["username"]);
}
# Set an error if this is a system user
if ($this->validated_data["scope"] !== "user") {
$this->errors[] = APIResponse\get(5005);
}
} else {
$this->errors[] = APIResponse\get(5000);
}
}

public function validate_payload() {
$this->__validate_username();
}
}
Loading